OpenCV使用分水岭变换来实现图像中对象计数
一、概述
案例:使用分水岭变换来实现图像中对象计数,达到统计图像中对象数目的目的
重要API介绍:
watershed(src,markers);
src:原图像
markers:目标markers,生成markers是通过findContours边沿查找+drawContours来实现的。ps:这一步非常重要,有了marker就可以使用分水岭算法了。
使用分水岭算法实现图像中对象计数的步骤:
1.输入图像(如果有需要可以对图像进行滤波模糊去除图像中的噪声)
2.图像灰度化
3.图像二值化(由于要使用距离变换,此处必须是二值图像)
4.执行距离变换
5.图像归一化
6.图像二值化(只1取值需要的部分)
7.执行轮廓发现,先找到marker
8.绘制找到的marker
9.执行分水岭变换
10.输出最终的变换后的marker(其实此时已经可以了,只是为了好看,所以需要下面11、12步给每一个对象着色)
11.给找到的对象进行着色(此处有技巧,可查看代码中的注释)
12.输出着色后的图像
二、代码示例
//1.载入图像 Mat src = imread(filePath); if(src.empty()){ qDebug()<<"输入图片为空"; return; } //2.使用金字塔边缘保留滤波平滑图像平坦区域,为了去除图像中的噪声 Mat meanShiftMat; pyrMeanShiftFiltering(src,meanShiftMat,21,51); imshow("meanShiftMat",meanShiftMat); //3.图像灰度化(因为后面要做二值化) Mat gray; cvtColor(meanShiftMat,gray,COLOR_BGR2GRAY); imshow("gray",gray); //4.图像二值化 Mat thresholdMat; threshold(gray,thresholdMat,0,255,THRESH_BINARY|THRESH_OTSU); imshow("threshold",thresholdMat); //5.执行距离变换 Mat dist; distanceTransform(thresholdMat,dist,DistanceTypes::DIST_L2,3,CV_32F); normalize(dist,dist,0,1,NORM_MINMAX);//将数据归一化0~1 threshold(dist,dist,0.4,1,THRESH_BINARY);//再次执行二值化是去掉小于0.4的像素值,只保留较大的 imshow("dst",dist); //6.寻找种子 Mat dist_mark; dist.convertTo(dist_mark,CV_8U); vector<vector<Point>> contours; findContours(dist_mark,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE,Point(0,0)); //7.寻找mark Mat markers = Mat::zeros(src.size(),CV_32SC1); for(size_t i =0;i<contours.size();i++){ drawContours(markers,contours,static_cast<int>(i),Scalar::all(static_cast<int>(i) + 1), -1);//此处是为下面着色做准备的 } circle(markers, Point(5, 5), 3, Scalar(255), -1); // imshow("markers",markers*10000); //执行形态学变换 Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); morphologyEx(src, src, MORPH_ERODE, k); //8.执行分水岭变换 watershed(src,markers); Mat mark = Mat::zeros(markers.size(),CV_8UC1); markers.convertTo(mark,CV_8UC1); bitwise_not(mark,mark,Mat()); imshow("mark",mark); //9.给找到的对象进行着色 vector<Vec3b> colors;//将随机生成的RGB颜色放入集合中,为下面markers对象着色做铺垫 for (size_t i = 0; i < contours.size(); i++) { int r = theRNG().uniform(0, 255); int g = theRNG().uniform(0, 255); int b = theRNG().uniform(0, 255); colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r)); } Mat dst = Mat::zeros(markers.size(),CV_8UC3);//因为要绘制彩色图像,所以这里要创建三通道的 int index = 0; for(int row=0;row<markers.rows;row++){ for(int col=0;col<markers.cols;col++){ index = markers.at<int>(row,col);//获取指定位置的像素值 //此处要好好解释一下: //index>0过滤掉黑色的,index<=contours.size()的意思和上面markers的颜色对应,上面markers的颜色值是1~contours.size()之间 //每个markers中的对象的颜色值都是1~contours.size()之间的值,只要判断这个值就说明找到了这个对象,而找到了这个对象自然可以给这个对象重新安排值 //这个技巧一定要知道不然下面的内容是看不懂的。 if (index > 0 && index <= contours.size()) { dst.at<Vec3b>(row, col) = colors[index - 1]; } else { dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0); } } } cout<<"对象个数:"<<contours.size()<<endl; //10.显示最终图像 imshow("Final Result", dst);
三、演示图像(每一个步骤都有呈现的展示效果)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探