多边形拟合算法对比

  • 背景介绍

opencv中有逼近多边形曲线的函数approxPolyDP,但是不能一直做一个调包侠,因此我用opencv实现了一篇论文中介绍的两种多边形拟合算法。论文名称:《图像处理中多边形拟合的快速算法》,作者:张帆等,点此下载。在写代码之前,首先要了解两个简单的数学知识。

1、已知两个点的坐标(x_{1},y_{1})(x_{2},y_{2}),如何确定直线方程ax+by+c=0 中a、b、c的数值?

             \tiny y=kx+m\Rightarrow \left\{\begin{matrix} y_{1}=kx_{1}+m\\ y_{2}=kx_{2}+m \end{matrix}\right.\Rightarrow \left\{\begin{matrix} k=\frac{y_{2}-y_{1}}{x_{2}-x_{1}}\\ m=y_{1}-\frac{y_{2}-y_{1}}{x_{2}-x_{1}} x_{1}\end{matrix}\right.\Rightarrow y=\frac{y_{2}-y_{1}}{x_{2}-x_{1}}x+(y_{1}-\frac{y_{2}-y_{1}}{x_{2}-x_{1}} x_{1})

整理上式得:

                                         (y_{1}-y_{2})x+(x_{2}-x_{1})y+(y_{2}x_{1}-x_{2}y_{1})=0

因此:

                                         a=y_{1}-y_{2}b=x_{2}-x_{1}c=y_{2}x_{1}-x_{2}y_{1}

2、点到直线的距离公式:

                                                           d=\left | \frac{Ax_{0}+By_{0}+C}{\sqrt{A^{2}+B^{2}}}\right |

  • Opencv实例

该论文中作者提出的多边形拟合算法,我用opencv实现以后发现找到的边缘并不是目标真实的边缘(本人能力水平有限,可能并没有理解作者的真正思想)。因此我对其算法步骤进行了修改,将第六步中的阈值T变为一个比T小的数值,得到下图所示结果。根据结果图可以看出,还是opencv官方的算法效果比较好,不仅速度快而且精度高。其它两种算法速度较慢,且得到的轮廓有些许变形。

效果图:

多边形拟合算法对比_第1张图片

                                                                                                   原图

多边形拟合算法对比_第2张图片     多边形拟合算法对比_第3张图片     多边形拟合算法对比_第4张图片

                     opencv自带方法                                          迭代端点拟合法                                            多边形拟合法

代码:

/
//opencv4.1.0
/

#include 
#include 
#include 

using namespace cv;
using namespace std;

Mat src, binary, src_gray;
TickMeter tm;


/***** Opencv自带方法*****/
void OpencvFitting(vector>& contours);

/***** 迭代端点拟合法*****/
float T = 0.9;    //距离阈值
int step = 20;    //步长
vectorResultContours;
void IterativeEndpointFitting(vector>& contours);
void fun(int top, int sum, int j, vector>& contours);
float getDist_P2L(Point pointP, Point pointA, Point pointB);

/***** 多边形拟合法*****/
void PolygonFitting(vector>& contours);


int main(int argc,char** argv )
{
	//读取图片
	src = imread("3.jpg");
	if (src.empty()){
		printf("读取图像失败\n");
		return -1;
	}
	imshow("输入图片",src);

	//二值化
	cvtColor(src,src_gray,COLOR_BGR2GRAY);
	threshold(src_gray,binary,0,255,THRESH_BINARY|THRESH_OTSU);

	//寻找轮廓,并获取坐标点集
	vectorhireachy;
	vector> contours;
	findContours(binary,contours,hireachy,RETR_TREE,CHAIN_APPROX_NONE,Point());

	//Opencv自带方法
	tm.start();
	OpencvFitting(contours);
	tm.stop();
	cout << "Opencv自带方法运行时间:" << tm.getTimeMilli() << endl;

	//迭代端点拟合法
	tm.start();
	IterativeEndpointFitting(contours);
	tm.stop();
	cout << "迭代端点拟合法运行时间:" << tm.getTimeMilli() << endl;

	//多边形拟合法
	tm.start();
	PolygonFitting(contours);
	tm.stop();
	cout << "多边形拟合法运行时间:" << tm.getTimeMilli() << endl;

	waitKey(0);
	return 0;
}


void OpencvFitting(vector>& contours) {

	//逼近多边形曲线
	vector> poly(contours.size());
	for (size_t t = 0; t>& contours) {

	for (int j = 0; j> contours1;
	contours1.push_back(ResultContours);//一维向量点集转换为二维向量点集:vector  ——  vector>,否则drawContours函数崩溃

	//画出轮廓
	Mat MyResult = Mat::zeros(src_gray.size(), CV_8UC3);
	for (size_t t = 0; t>& contours)
{
	Point LeftE, RightE;                       //左右两侧点距离直线距离最大点的坐标
	float LeftH = 0, RightH = 0;                   //左右两侧点距离直线距离最大值
	int MaxValueIndexLeft, MaxValueIndexRight; //左右两侧点距离直线距离最大值对应的索引
	for (int i = 0; i <= top;i++)
	{
		//左侧距离计算
		float DistanceLeft = getDist_P2L(contours[0][i + j], contours[0][0 + j], contours[0][top + j]);
		if (DistanceLeft>LeftH)
		{
			LeftH = DistanceLeft;
			LeftE = contours[0][i + j];
			MaxValueIndexLeft = i;
		}

		//右侧距离计算
		if (sum != top && i <= (sum - top))
		{
			float DistanceRight = getDist_P2L(contours[0][i + j + top], contours[0][top + j], contours[0][sum + j]);
			if (DistanceRight>RightH)
			{
				RightH = DistanceRight;
				RightE = contours[0][i + j + top];
				MaxValueIndexRight = i;
			}
		}
	}
	if (LeftH>T)
	{
		ResultContours.push_back(LeftE);
		fun(MaxValueIndexLeft, top, j, contours);
	}
	if (RightH>T && sum != top)
	{
		ResultContours.push_back(RightE);
		fun(MaxValueIndexRight, top, j, contours);
	}
}

/***** 点P到直线AB的距离*****/
float getDist_P2L(Point pointP, Point pointA, Point pointB)
{
	//求直线方程
	int A = 0, B = 0, C = 0;
	A = pointA.y - pointB.y;
	B = pointB.x - pointA.x;
	C = pointA.x*pointB.y - pointA.y*pointB.x;
	//代入点到直线距离公式
	float distance = 0;
	distance = ((float)abs(A*pointP.x + B*pointP.y + C)) / ((float)sqrtf(A*A + B*B));
	return distance;
}

void PolygonFitting(vector>& contours) {

	int T = 10;
	Point E;
	vectorResultContours;
	for (int j = 0; jH) {
				H = Distance;
				E = contours[0][i];
			}
		}
		if (H>1) {  //按照论文中描述,此处应该是if(H>T),但我做了修改
			ResultContours.push_back(E);
		}
	}

	vector> contours1;
	contours1.push_back(ResultContours);//一维向量点集转换为二维向量点集:vector  ——  vector>,否则drawContours函数崩溃

	//画出轮廓
	Mat MyResult = Mat::zeros(src_gray.size(), CV_8UC3);
	for (size_t t = 0; t

 

 

 

 

 

 

 

 

你可能感兴趣的:(#,Opencv)