对象计数
4.1 题目
如图数出下图的玉米种子的数量
#### 4.2 思路
这个案例的难点在于有的玉米粒是靠在一起的,不然的话直接轮廓查找就能搞定了。基本指导思想就是把玉米粒都分开,分成一粒一粒的再进行轮廓查找。
1、 二值化
2、 形态学操作
3、 距离变换
4、 二值化
5、 形态学操作
6、 轮廓查找
具体步骤和解释都在代码里了。
4.3 代码示例
#include <opencv2\opencv.hpp>
using namespace cv;
int main(int argc, char *argv[])
{
Mat sorImg = imread("yumili.jpg", IMREAD_GRAYSCALE);
//imshow("sorImg", sorImg);
// 第一步:二值化,注意使用THRESH_TRIANGLE
Mat binaryImg;
threshold(sorImg, binaryImg, 0, 255, THRESH_BINARY | THRESH_TRIANGLE);
// 第二步:形态学操作,稍微分离一下玉米粒
Mat morphologyImg;
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(binaryImg, morphologyImg, kernel,Point(-1,-1),2);
imshow("dstImg1", morphologyImg);
// 第三步:距离变换,查找出亮包
Mat dist;
bitwise_not(morphologyImg, morphologyImg);
distanceTransform(morphologyImg, dist, DIST_L2, 3);
normalize(dist, dist, 0, 255, NORM_MINMAX);
// 第四步:再次二值化,局部二值化
Mat dsit_8u;
dist.convertTo(dsit_8u, CV_8U);
adaptiveThreshold(dsit_8u, dsit_8u, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 205, 0.0);
// 第五步:再次形态学操作,彻底分割玉米粒
erode(dsit_8u, dsit_8u, kernel, Point(-1, -1), 18);
// 第六步: 连通区域寻找计算
std::vector<std::vector<Point>>contours;
findContours(dsit_8u, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
RNG rng(124345);
Mat dstImg(dsit_8u.size(),CV_8UC3);
for (int i = 0; i < contours.size(); ++i)
{
drawContours(dstImg, contours, i, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)),-1, 8, Mat());
}
imshow("dstImg", dstImg);
std::cout << "num is:" << contours.size();
waitKey();
return 0;
}
效果图;
4.4 关键API
distanceTransform:距离变换,找出物体的中心。
threshold :THRESH_TRIANGLE方式的二值化适用于直方图单峰的图像。
adaptiveThreshold:局部二值化。
4.5 总结
对于此案例,二值化后直接对图像腐蚀可能会分隔大多数的玉米粒,但是有的总是不能分隔的,使用距离变换找出较明显的特征,在进行腐蚀效果更好。