opencv图像处理学习(五十一)——拟合(opencv自带函数)

版权声明:不得随意转载 https://blog.csdn.net/qq_35789421/article/details/89374491

很久没更新blog了,主要最近开发任务有点重,然而最近需要开发一个拟合工具箱,所以才会有这一篇blog,废话不多说,进入正题。(磨刀不误砍柴功,省的后面开发不好挨骂)

首先先整理一下参考链接:(包括最小二乘法(下一章的参考链接))

opencv的曲线拟合polyfit:https://www.cnblogs.com/phoenixdsg/p/6978263.html

opencv多边形拟合曲线approxPolyDP()函数:https://blog.csdn.net/u013925378/article/details/86075230

【OpenCV】多项式方程求解(PolySolver):https://blog.csdn.net/baishuo8/article/details/81013649

OpenCV曲线拟合与圆拟合:https://blog.51cto.com/gloomyfish/2128313?source=dra

【OpenCV3】直线拟合——cv::fitLine()详解:https://blog.csdn.net/guduruyu/article/details/69505487

【算法+OpenCV】基于opencv的直线和曲线拟合与绘制(最小二乘法):【算法+OpenCV】基于opencv的直线和曲线拟合与绘制(最小二乘法):https://blog.csdn.net/guduruyu/article/details/72866144

最小二乘法多项式曲线拟合原理与实现:https://blog.csdn.net/JairusChan/article/details/7517773

OpenCV—椭圆拟合fitEllipse:https://blog.csdn.net/i_chaoren/article/details/78358991

OpenCV中approxPolyDP()函数:https://www.cnblogs.com/k1412/p/6884484.html

【算法+OpenCV】基于三次Bezier原理的曲线拟合算法C++与OpenCV实现:https://blog.csdn.net/guduruyu/article/details/60877086

opencv自带函数拟合

(1)直线拟合

在图像处理中,通常会遇到根据给定的点集(比如轮廓)拟合出一条直线的情形。opencv2和opencv3中提供了一个专门用于直线拟合的函数——cv::fitLine()。
cv::fitLine()的具体调用形式如下:


	void cv::fitLine(

		cv::InputArray points, // 二维点的数组或vector

		cv::OutputArray line, // 输出直线,Vec4f (2d)或Vec6f (3d)的vector

		int distType, // 距离类型

		double param, // 距离参数

		double reps, // 径向的精度参数

		double aeps // 角度精度参数

	);

第一个参数是用于拟合直线的输入点集,可以是二维点的cv::Mat数组,也可以是二维点的STL vector。
第二个参数是输出的直线,对于二维直线而言类型为cv::Vec4f,对于三维直线类型则是cv::Vec6f,输出参数的前半部分给出的是直线的方向,而后半部分给出的是直线上的一点(即通常所说的点斜式直线)。
第三个参数是距离类型,拟合直线时,要使输入点到拟合直线的距离和最小化(即下面公式中的cost最小化),可供选的距离类型如下表所示,ri表示的是输入的点到直线的距离。

第四个参数是距离参数,跟所选的距离类型有关,值可以设置为0,cv::fitLine()函数本身会自动选择最优化的值。
第五、六两个参数用于表示拟合直线所需要的径向和角度精度,通常情况下两个值均被设定为1e-2。


	//创建一个用于绘制图像的空白图

	cv::Mat image = cv::Mat::zeros(480, 640, CV_8UC3);

 

	//输入拟合点

	std::vector<cv::Point> points;

	points.push_back(cv::Point(48, 58));

	points.push_back(cv::Point(105, 98));

	points.push_back(cv::Point(155, 160));

	points.push_back(cv::Point(212, 220));

	points.push_back(cv::Point(248, 260));

	points.push_back(cv::Point(320, 300));

	points.push_back(cv::Point(350, 360));

	points.push_back(cv::Point(412, 400));

 

	//将拟合点绘制到空白图上

	for (int i = 0; i < points.size(); i++)

	{

		cv::circle(image, points[i], 5, cv::Scalar(0, 0, 255), 2, 8, 0);

	}

 

	cv::Vec4f line_para; 

	cv::fitLine(points, line_para, cv::DIST_L2, 0, 1e-2, 1e-2);

 

	std::cout << "line_para = " << line_para << std::endl;

 

	//获取点斜式的点和斜率

	cv::Point point0;

	point0.x = line_para[2];

	point0.y = line_para[3];

 

	double k = line_para[1] / line_para[0];

 

	//计算直线的端点(y = k(x - x0) + y0)

	cv::Point point1, point2;

	point1.x = 0;

	point1.y = k * (0 - point0.x) + point0.y;

	point2.x = 640;

	point2.y = k * (640 - point0.x) + point0.y;

 

	cv::line(image, point1, point2, cv::Scalar(0, 255, 0), 2, 8, 0);

 

	cv::imshow("image", image);

	cv::waitKey(0);

	return;

拟合效果如下:

(2)椭圆拟合

OpenCV中提供的椭圆拟合API如下:

RotatedRect fitEllipse(InputArray points)

输入:二维点集,要求拟合的点至少为6个点。存储在std::vector<>
 or Mat
处理:该函数使用的是最小二乘的方法进行拟合的。

输出:RotatedRect 类型的矩形,是拟合出椭圆的最小外接矩形。

对应的参数可参见下图:


#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/highgui/highgui.hpp"

#include <iostream>

using namespace cv;

using namespace std;

 

int main( )

{

	const char* filename = "rect-45度.bmp";

	Mat src_image = imread(filename, 0);

	if( src_image.empty() )

	{

		cout << "Couldn't open image!" << filename;

		return 0;

	}

 

	imshow("原图", src_image);

	

	//轮廓

	vector<vector<Point>> contours;

 

	//使用canny检测出边缘

	Mat edge_image;

	Canny(src_image,edge_image,30,70);

	imshow("canny边缘",edge_image);

 

	//边缘追踪,没有存储边缘的组织结构

	findContours(edge_image, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

	Mat cimage = Mat::zeros(edge_image.size(), CV_8UC3);

 

	for(size_t i = 0; i < contours.size(); i++)

	{

		//拟合的点至少为6

		size_t count = contours[i].size();

		if( count < 6 )

			continue;

 

		//椭圆拟合

		RotatedRect box = fitEllipse(contours[i]);

 

		//如果长宽比大于30,则排除,不做拟合

		if( MAX(box.size.width, box.size.height) > MIN(box.size.width, box.size.height)*30 )

			continue;

 

		//画出追踪出的轮廓

		drawContours(cimage, contours, (int)i, Scalar::all(255), 1, 8);

		

		//画出拟合的椭圆

		ellipse(cimage, box, Scalar(0,0,255), 1, CV_AA);

	}

	imshow("拟合结果", cimage);

 

	waitKey();

	return 0;

}

(3)多边形拟合

approxPolyDP()函数是opencv中对指定的点集进行多边形逼近的函数,其逼近的精度可通过参数设置。

对应的函数为:
void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed);

例如:approxPolyDP(contourMat, approxCurve, 10, true);//找出轮廓的多边形拟合曲线

第一个参数 InputArray curve:输入的点集
第二个参数OutputArray approxCurve:输出的点集,当前点集是能最小包容指定点集的。画出来即是一个多边形。
第三个参数double epsilon:指定的精度,也即是原始曲线与近似曲线之间的最大距离。
第四个参数bool closed:若为true,则说明近似曲线是闭合的;反之,若为false,则断开。


该函数采用是道格拉斯-普克算法(Douglas-Peucker)来实现。该算法也以Douglas-Peucker算法和迭代终点拟合算法为名。是将曲线近似表示为一系列点,并减少点的数量的一种算法。该算法的原始类型分别由乌尔斯·拉默(Urs Ramer)于1972年以及大卫·道格拉斯(David Douglas)和托马斯·普克(Thomas Peucker)于1973年提出,并在之后的数十年中由其他学者予以完善。

经典的Douglas-Peucker算法描述如下:

(1)在曲线首尾两点A,B之间连接一条直线AB,该直线为曲线的弦;

(2)得到曲线上离该直线段距离最大的点C,计算其与AB的距离d;

(3)比较该距离与预先给定的阈值threshold的大小,如果小于threshold,则该直线段作为曲线的近似,该段曲线处理完毕。

(4)如果距离大于阈值,则用C将曲线分为两段AC和BC,并分别对两段取信进行1~3的处理。

(5)当所有曲线都处理完毕时,依次连接各个分割点形成的折线,即可以作为曲线的近似。


猜你喜欢

转载自blog.csdn.net/qq_35789421/article/details/89374491