目的
了解如何使用OpenCV进行连通域的标记以及计数,从而可以实现计算机视觉中的细胞计数、硬币计数等计数任务。
方法
Source:机器视觉技术与应用_中国大学MOOC(慕课) (icourse163.org)
函数介绍
连通域标记函数 | |
绘制矩阵函数 |
状态矩阵stats解读:
状态矩阵每一行代表一个连通域,每一列分别代表:连通域最小外接四边形的x坐标、连通域最小外接四边形的y坐标、连通域最小外接四边形的宽、连通域最小外接四边形的高以及连通域的面积;
状态矩阵的第一行表示的是背景,一般不计入连通域的数量。
连通域中心矩阵centroids:
实践练习
代码(C++): | #include<opencv2/opencv.hpp> using namespace std; using namespace cv; int main() { void connectedWithStats(); connectedWithStats(); return 0; } void connectedWithStats() { //读取图像并转为灰度图 Mat srcMat = imread("E:\\Projects\\C++\\morphology_practice\\test_data\\coins.png", 0); //判断图像是否读取成功、 if (srcMat.empty()) { cout << "fail to read pic!" << endl; return; } //定义图像容器 Mat stats; Mat centroids; Mat labels; Mat thresh_Mat; Mat erode_Mat; Mat close_Mat; //大津法处理图像(图像二值化,大津法,类内方差最小,类间方差最大) threshold(srcMat, thresh_Mat, 100, 255, THRESH_OTSU); //使用闭运算 //定义结构元素 Mat element = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); //闭运算 morphologyEx(thresh_Mat, close_Mat, MORPH_CLOSE, element); //进行连通域标记 int nComp = connectedComponentsWithStats(close_Mat, labels, stats, centroids, 8, CV_32S); //减去背景0,并输出连通域的个数 cout << "硬币个数为: " << nComp - 1 << endl; //对识别出的连通域加最小外接边框 for (int i = 1;i < nComp;i++) { //定义Rect类 Rect bandbox; bandbox.x = stats.at<int>(i, 0); //表示stats状态矩阵的第i行的第0个元素; bandbox.y = stats.at<int>(i, 1); bandbox.width = stats.at<int>(i, 2); bandbox.height = stats.at<int>(i, 3); //使用rectangle函数绘制矩形 rectangle(thresh_Mat, bandbox, 255, 1, 8, 0); } imshow("thresh_Mat", thresh_Mat); imshow("srcMat", srcMat); waitKey(0); } |
实验图像 |
结果
讨论
如果直接对二值图像进行连通域标记,标记结果为11,这是错误的。所以这篇文章先对二值图像进行了闭运算的操作,将二值化不佳的硬币中的黑色部分填充起来,减少计数错误的概率。