轮廓查找、图像矩、多边形测试、图像分水岭

  • 1. 银行卡轮廓查找与绘制
  • 2. [图像矩,多边形测试](http:///opencvdoc/2.3.2/html/doc/tutorials/imgproc/shapedescriptors/point_polygon_test/point_polygon_test.html#point-polygon-test)
  • 3. 图像分水岭


1. 银行卡轮廓查找与绘制

使用到的api

/*
参数说明:
输入图像image必须为一个2值单通道图像

contours参数为检测的轮廓数组,每一个轮廓用一个point类型的vector表示

hiararchy参数和轮廓个数相同,每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ],
分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,该值设置为负数。

mode表示轮廓的检索模式
CV_RETR_EXTERNAL 表示只检测外轮廓
CV_RETR_LIST 检测的轮廓不建立等级关系
CV_RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
CV_RETR_TREE 建立一个等级树结构的轮廓。具体参考contours.c这个demo

method为轮廓的近似办法
CV_CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
CV_CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS 使用teh-Chinl chain 近似算法
offset表示代表轮廓点的偏移量,可以设置为任意值。对ROI图像中找出的轮廓,并要在整个图像中进行分析时,这个参数还是很有用的。
*/
CV_EXPORTS_W void findContours( InputArray image, OutputArrayOfArrays contours,
                              OutputArray hierarchy, int mode,
                              int method, Point offset = Point());
// 查找轮廓
CV_EXPORTS void findContours( InputArray image, OutputArrayOfArrays contours,
                               int mode, int method, Point offset = Point());

// 获取可以包围轮廓的外层的矩形
CV_EXPORTS_W Rect boundingRect( InputArray array );

// 绘制轮廓

CV_EXPORTS_W 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, Point offset = Point() );

以下是银行卡的轮廓查找与绘制

int main(){
	Mat src = imread("E:/card.jpg");
	if (!src.data){
		cout << "read error" << endl;
		return -1;
	}

	imshow("src", src);

	// 梯度和二值化
	Mat binary;
	Canny(src,binary,50,150);
	imshow("binary", binary);

	//findContours(InputArray image, OutputArrayOfArrays contours,
	//	int mode, int method, Point offset = Point())

	vector<vector<Point>> contours;
	findContours(binary,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);

	Mat contours_mat = Mat::zeros(src.size(),CV_8UC3);
	for (int i = 0; i < contours.size(); i++)
	{
		// 画轮廓 
		// boundingRect 获取可以包围轮廓的外层的矩形
		Rect rect = boundingRect(contours[i]);
		if (rect.width > src.cols / 2 && rect.height > src.rows / 2){
			// 画轮廓
			drawContours(contours_mat,contours,i,Scalar(0,0,255),1);
			rectangle(contours_mat, Point(rect.x, rect.y), Point(rect.x + rect.width, rect.y + rect.height), Scalar(255, 255, 255), 2);
			break;
		}
	}

	imshow("contours_mat", contours_mat);
	
	waitKey(0);
	return 0;
}

2. 图像矩,多边形测试

使用到的api

// measureDist: 为`true`的情况下, `<0:在外面,>0:在里面,==0:在轮廓上`。
// 为`false`的情况,`-1:在外面,1:在里面,0:在轮廓上`
double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist )

图像矩的一个小例子

int main(){
	/// 创建一个图形     
	const int r = 100;
	Mat src = Mat::zeros(Size(4 * r, 4 * r), CV_8UC1);

	/// 绘制一系列点创建一个轮廓:
	vector<Point2f> vert(6);

	vert[0] = Point(1.5*r, 1.34*r);
	vert[1] = Point(1 * r, 2 * r);
	vert[2] = Point(1.5*r, 2.866*r);
	vert[3] = Point(2.5*r, 2.866*r);
	vert[4] = Point(3 * r, 2 * r);
	vert[5] = Point(2.5*r, 1.34*r);

	/// 在src内部绘制
	for (int j = 0; j < 6; j++)
	{
		line(src, vert[j], vert[(j + 1) % 6], Scalar(255), 3, 8);
	}

	imshow("src", src);

	// 查找轮廓
	vector<vector<Point>> contours;
	findContours(src,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);

	// 计算到轮廓的距离
	Mat raw_dist(src.size(),CV_32FC1);
	for (int row = 0; row < src.rows; row++)
	{
		for (int col = 0; col < src.cols; col++)
		{
			raw_dist.at<float>(row, col) = pointPolygonTest(contours[0], Point2f(col, row), true);
		}
	}

	// 优化
	Mat drawing = Mat::zeros(src.size(),CV_8UC3);
	for (int row = 0; row < src.rows; row++)
	{
		for (int col = 0; col < src.cols; col++)
		{
			// raw_dist.at<float>(row, col) = pointPolygonTest(contours[0], Point2f(col, row), true);
			float value = raw_dist.at<float>(row, col);
			if (value < 0){ // 外面
				drawing.at<Vec3b>(row, col)[0] = saturate_cast<int>((int)abs(value));
			}
			else if (value > 0){ // 里面
				drawing.at<Vec3b>(row, col)[2] = saturate_cast<int>((int)value);
			}
			else{// 矩形上面
				drawing.at<Vec3b>(row, col)[0] = 255;
				drawing.at<Vec3b>(row, col)[1] = 255;
				drawing.at<Vec3b>(row, col)[2] = 255;
			}
		}
	}

	imshow("drawing", drawing);

	waitKey(0);
	return 0;
}

3. 图像分水岭

使用到的api

CV_EXPORTS_W void watershed( InputArray image, InputOutputArray markers );