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

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

使用到的api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
参数说明:https://blog.csdn.net/guduruyu/article/details/69220296
输入图像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() );

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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

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

图像矩的一个小例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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

1
CV_EXPORTS_W void watershed( InputArray image, InputOutputArray markers );
-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!
0%