用opencv处理轮廓

Rect boundingRect(InputArray points)

寻找包裹轮廓的最小正矩形。矩形的边界与图像边界平行。 唯一一个参数是输入的二维点集,可以是 vector 或 Mat 类型。只需要 #include "opencv2/opencv.hpp"

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
#include<opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main(){
Mat src = imread("/home/user/test.jpg");
imshow("src", src);

Mat gray, bin_img;
cvtColor(src, gray, COLOR_BGR2GRAY); //将原图转换为灰度图
imshow("gray", gray);

//二值化
threshold(gray, bin_img, 150, 255, THRESH_BINARY_INV);
imshow("bin_img", bin_img);

//寻找最外围轮廓
vector<vector<Point> >contours;
findContours(bin_img, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

//绘制边界矩阵
RNG rngs = { 12345 };
Mat dst = Mat::zeros(src.size(), src.type());
for (int i = 0; i < contours.size(); i++)
{
Scalar colors = Scalar(rngs.uniform(0, 255), rngs.uniform(0, 255), rngs.uniform(0, 255));
drawContours(dst, contours, i, colors, 1);
Rect rects = boundingRect(contours[i]);
rectangle(dst, rects, colors, 2);
}
imshow("dst", dst);

waitKey(0);
}

RotatedRect minAreaRect(InputArray points)

寻找包裹轮廓的最小斜矩形。唯一一个参数是输入的二维点集,可以是 vector 或 Mat 类型。只需要 #include "opencv2/opencv.hpp"

boundingRect 返回结果的区别是:矩形的边界不必与图像边界平行

RotatedRect旋转矩形结构体

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
    Mat gray, bin_img;
cvtColor(src, gray, COLOR_BGR2GRAY); //将原图转换为灰度图
imshow("gray", gray);

//二值化
threshold(gray, bin_img, 150, 255, THRESH_BINARY_INV);
imshow("bin_img", bin_img);

//寻找最外围轮廓
vector<vector<Point> >contours;
findContours(bin_img, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

//绘制最小边界矩阵
RNG rngs = { 12345 };
Mat dst = Mat::zeros(src.size(), src.type());
Point2f pts[4];
for (int i = 0; i < contours.size(); i++)
{
Scalar colors = Scalar(rngs.uniform(0, 255), rngs.uniform(0, 255), rngs.uniform(0, 255));
drawContours(dst, contours, i, colors, 1);
RotatedRect rects = minAreaRect(contours[i]);
// 确定旋转矩阵的四个顶点
rects.points(pts);
for (int i = 0; i < 4; i++)
line(dst, pts[i], pts[(i + 1) % 4], colors, 2);

}
}

double pointPolygonTest(InputArray contour, Point2f pt, bool measureDist)

计算点与轮廓的距离及位置关系,只需要 #include "opencv2/opencv.hpp"

  • contour: 所需检测的轮廓对象

  • pt: Point2f 类型的pt, 待判定位置的点

  • measureDist: 是否计算距离的标志, 当其为true时, 计算点到轮廓的最短距离, 当其为false时, 只判定轮廓与点的位置关系, 具体关系如下:

① 返回值为-1, 表示点在轮廓外部

② 返回值为0, 表示点在轮廓上

③ 返回值为1, 表示点在轮廓内部

如果不需要知道具体的距离,建议将第三个参数设为false,这样速度会提高2到3倍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//计算点到轮廓的距离与位置关系
Mat srcImg = imread("1.png");
imshow("src", srcImg);

Mat dstImg = srcImg.clone();
cvtColor(srcImg, srcImg, CV_BGR2GRAY);
threshold(srcImg, srcImg, 100, 255, CV_THRESH_BINARY);
imshow("threshold", srcImg);

vector<vector<Point>> contours;
vector<Vec4i> hierarcy;
findContours(srcImg, contours, hierarcy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);//TREE:提取所有轮廓 NONE:画出轮廓所有点
cout << "contours.size()=" << contours.size() << endl;
for (int i = 0; i < contours.size(); i++)//遍历每个轮廓
{
for (int j = 0; j < contours[i].size(); j++)//遍历轮廓每个点
{
cout << "(" << contours[i][j].x << "," << contours[i][j].y << ")" << endl;
}
}
double a0 = pointPolygonTest(contours[0], Point(3, 3), true);//点到轮廓的最短距离
double b0 = pointPolygonTest(contours[0], Point(212, 184), false);//点与轮廓的位置关系:-1表示外部;0表示在轮廓上;1表示轮廓内部

求轮廓面积 cv::contourArea

double contourArea( InputArray contour, bool oriented = false );

  • InputArray类型的contour,输入的向量,二维点(轮廓顶点),可以为std::vector或Mat类型。
  • bool类型的oriented,面向区域标识符。若其为true,会返回一个带符号的面积值,正负取决于轮廓的方向。
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
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main(void)
{
Mat A = Mat::zeros(500, 500, CV_8UC1);
circle(A, Point2i(100, 100), 3, 255, -1);
circle(A, Point2i(300, 400), 50, 255, -1);
circle(A, Point2i(250, 100), 100, 255, -1);
circle(A, Point2i(400, 300), 60, 255, -1);

std::vector<std::vector<cv::Point> > contours; // 创建轮廓容器
std::vector<cv::Vec4i> hierarchy;

cv::findContours(A, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE, cv::Point());
if (!contours.empty() && !hierarchy.empty())
{
std::vector<std::vector<cv::Point> >::const_iterator itc = contours.begin();
// 遍历所有轮廓
int i = 1;
while (itc != contours.end())
{
double area = cv::contourArea(*itc);
cout << "第" << i << "个轮廓的面积为:" << area << endl;
i++;
itc++;
}
}
imshow("A", A);
waitKey(0);
system("pause");
return 0;
}

第一个圆的面积并不是9π,而是20。面积值是按照轮廓的内部面积进行计算的,会损失一些。

参考: cv::contourArea