Canny等边缘检测算可以根据求导等计算差异来检测边缘,但是无法将轮廓合成一个整体,轮廓发现是奖边缘像素合成轮廓。

轮廓发现findContours

void findContours( InputOutputArray image,      //输入图像8bit单通道二值化图像
                    OutputArrayOfArrays contours, //找到的轮廓 vector<vector<Point>>
                    OutputArray hierachy          //可选 图像拓扑结构 vector<Vec4i>
                    int mode,                     //轮廓返回模式
                    int method,                   //发现方法
                    Point offset = Point());      //轮廓像素的位移,默认(0,0)无位移

mode类型

RETR_EXTERNAL

只检测最外层轮廓

RETR_LIST

检索所有轮廓并存到List中,无上下结构,全部同级

RETR_CCOMP

检索所有轮廓,并组成双层结构

RETR_TREE

检索所有轮廓,并重新建立网状轮廓结构。

method类型

CHAIN_APPROX_NONE

返回轮廓中所有点

CHAIN_APPROX_SIMPLE

水平、垂直、斜 只返回最后一个点

CHAIN_APPROX_TC89_KCOS

CHAIN_APPROX_TC89_L1

使用Teh-Chin链逼近算法

 hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],分别表示第i个轮廓  同级的下一条轮廓、同级的前一条轮廓、下级的第一个子节点、上级父节点。如果当前轮廓没有 同级的下一条轮廓、同级的前一条轮廓、下级的第一个子节点、上级父节点,则hierarchy[i][0] ~hierarchy[i][3]的相应位被设置为默认值-1。

轮廓绘制

void drawContours( InputOutputArray image,      //输出图像
                   InputArrayOfArrays contours, //全部发现的轮廓对象
                   int contourIdx,              //轮廓索引号
                   const Scalar& color,         //颜色
                   int thickness = 1,           //线宽
                   int lineType = LINE_8,       //线类型
                   InputArray hierarchy = noArray(), //拓扑结构图
                   int maxLevel = INT_MAX,     //最大层数,0绘制当前,1绘制当前及内嵌轮廓
                   Point offset = Point() );   //轮廓位移

实战演练:

#include<opencv2/opencv.hpp>
#include<opencv2/core/mat.hpp>
#include<iostream>
#include<vector>
using namespace std;
using namespace cv;

int threshold_value = 153;
int threshild_max = 255;
RNG	rng;
void Demo_Contours(int, void*);
Mat src,dst;
const char* output_image = "findcountours_demo";

int main()
{
	src = imread("C:/Users/LBJ/Desktop/OpenCVTest/fish.jpg");
	if (!src.data )
	{
		cout << "The iamge is empty" << endl;
		return -1;
	}
	namedWindow(output_image, WINDOW_AUTOSIZE);
	namedWindow("Input_Image", WINDOW_AUTOSIZE);
	imshow("Input_Image", src);

	cvtColor(src, src, CV_BGR2GRAY);
	const char* tracker_label = "Threshold Value:";

	createTrackbar(tracker_label, output_image, &threshold_value, threshild_max, Demo_Contours);
	Demo_Contours(0, 0);
	
	waitKey(0);
	return 0;
}

void Demo_Contours(int, void*)
{
	Mat canny_output;
	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	Canny(src, canny_output, threshold_value, threshold_value * 2, 3, false);
	findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
	
	dst = Mat::zeros(src.size(), CV_8UC3);
	RNG rng(12345);
	for (size_t i = 0; i < contours.size(); i++)
	{
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		drawContours(dst, contours, i, color, 2, 8, hierachy, 0, Point(0, 0));
	}

	for (size_t i = 0; i < hierachy.size(); i++)
	{
		cout << hierachy[i] << endl;


	}
	imshow(output_image, dst);


}

opencv 多边形覆盖 opencv 合并两个轮廓_sed

可以用轮廓间的关系定位二维码的三个定位点,他们都有两个子轮廓

#include<opencv2/opencv.hpp>
#include<opencv2/core/mat.hpp>
#include<iostream>
#include<vector>
using namespace std;
using namespace cv;

int threshold_value = 153;
Mat src, dst;
const char* output_image = "findcountours_demo";

int main()
{
	src = imread("C:/Users/LBJ/Desktop/OpenCVTest/二维码.jpg");
	if (!src.data)
	{
		cout << "The iamge is empty" << endl;
		return -1;
	}
	namedWindow(output_image, WINDOW_AUTOSIZE);
	namedWindow("Input_Image", WINDOW_AUTOSIZE);
	imshow("Input_Image", src);

	GaussianBlur(src, src, Size(5, 5), 0, 0);
	cvtColor(src, src, CV_BGR2GRAY);
	threshold(src, src, 0, 255, THRESH_BINARY | THRESH_OTSU);


	vector<Point> point;
	Mat canny_output;
	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	Canny(src, canny_output, threshold_value, threshold_value * 2, 3, false);
	imshow("Canny_imge", canny_output);

	findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));



	int k=0;
	dst = Mat::zeros(src.size(), CV_8UC3);

	for (size_t i = 0; i < contours.size(); i++)
	{
		int j = i, temp = 0;
		while (hierachy[j][2] != -1)
		{
			temp++;
			j = hierachy[j][2];

		}
		if (temp >= 4)
		{
			drawContours(dst, contours, i, Scalar(0, 0, 255), -1, 8, hierachy, 0, Point(0, 0));
			cout << contours[i] << endl;
	
			
		}
		else
		{
			drawContours(dst, contours, i, Scalar(255, 255, 255), 1, 8, hierachy, 0, Point(0, 0));
		}
	}

	imshow(output_image, dst);

	waitKey(0);
	return 0;
}

opencv 多边形覆盖 opencv 合并两个轮廓_#include_02

凸包

 

 

 

 

opencv 多边形覆盖 opencv 合并两个轮廓_sed_03

void convexHull( InputArray points, OutputArray hull,//输入点来自findContours 输出凸包
                 bool clockwise = false, bool returnPoints = true );
               //顺时针方向              //true表示返回点个数

测试点是否落在轮廓内

double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist );
//输入轮廓,点,
//true 发返回实际距离
//false 1边上在内面,0在边界,-1在外部

多边形逼近

用RDP算法,使轮廓的顶点数变少。

void approxPolyDP( InputArray curve,   
                    OutputArray approxCurve,    
                    double epsilon,          //两点间的最小距离 
                    bool closed );           //是否形成闭合曲线

轮廓几何及特性概括

//获得长度
double arcLength( InputArray curve, bool closed ); //轮廓及轮廓是否闭合
//获得矩形包围框
Rect boundingRect( InputArray array ); 
//获得旋转的矩形包围框
RotatedRect minAreaRect( InputArray points );
//获得最小包围圆
void minEnclosingCircle( InputArray points,       //输入点集
                         CV_OUT Point2f& center,  //圆心位置
                         CV_OUT float& radius );  //圆半径
//最小椭圆
RotatedRect fitEllipse( InputArray points );

图像矩

opencv 多边形覆盖 opencv 合并两个轮廓_#include_04

中心位置 x=m10/m00 y=y01/y00

//计算矩,返回的位Moments结构体,包括0-3阶 m,mu,nu。
Moments moments( InputArray array, bool binaryImage = false );//输入数据,是否是二值图像
//计算面积
double contourArea( InputArray contour, bool oriented = false ); //输入轮廓数据,返回绝对值
//计算长度
double arcLength( InputArray curve, bool closed ); //输入轮廓曲线,是否是封闭曲线。