现在我有这么一张图:
我想用图像处理操作数清楚里面的玉米粒到底有多少个。因为这张图中的玉米粒个数比较少,通过细数之后,我们可以发现玉米粒数量有17个。那么,如何通过图像处理操作来计算其中的玉米粒个数呢?
解决思路:
1、图像二值化;
2、形态学操作(膨胀与腐蚀)
3、距离变换
4、局部阈值二值化操作(adaptiveThreshold方法)
5、轮廓发现
因为我的文章中并没有膨胀与腐蚀的操作,这里我记录一下用来提醒自己:
膨胀:相对于高亮部分(白色区域),在二值化处理后,填补上黑色的部分。
腐蚀,与膨胀相反,去掉白色的部分。
基本解释已在代码中注释
代码:
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace cv;
using namespace std;
Mat src;
int main(int argc, char**argv)
{
src = imread("D:/test/count.png",0);
if (src.empty())
{
cout << "图片未找到!!!" << endl;
return -1;
}
imshow("input image", src);
/*-----------二值化操作-----------*/
Mat binaryImg, dilateImg;
threshold(src, binaryImg, 0, 255, THRESH_BINARY | THRESH_TRIANGLE);
/*这里为什么不用OTSU呢?因为对于这样的一张图,基本上对象是同一种颜色,
对于单种颜色,用TRIANGLE会比较好,对于2种及以上颜色,就要使用OTSU方法*/
imshow("binart image",binaryImg);
/*-------------形态学操作---------------*/
//膨胀操作,增大高亮(白色)区域,分离黑色连接色块
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));
dilate(binaryImg, dilateImg, kernel, Point(-1, -1),1);
imshow("dilate image", dilateImg);
/*---------------距离变换---------------*/
bitwise_not(dilateImg, dilateImg, Mat());
Mat dist;
distanceTransform(dilateImg, dist, DIST_L2, 3);
normalize(dist, dist, 0, 1, NORM_MINMAX);
imshow("dist image", dist);
/*-------------阈值二值化分割--------------*/
//threshold(dist, dist, 0, 1, THRESH_BINARY);
Mat dist_8u;
dist.convertTo(dist_8u, CV_8U);
//局部阈值化操作
adaptiveThreshold(dist_8u, dist_8u, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 81,0);
normalize(dist_8u, dist_8u, 0, 255, NORM_MINMAX);//归一化处理
kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
erode(dist_8u, dist_8u, kernel, Point(-1, -1),1);
imshow("dist_8u image", dist_8u);
/*-------------轮廓发现---------------*/
vector<vector<Point>> contours;
Mat resultImg = Mat::zeros(src.size(), CV_8UC3);
RNG rng((int)time(0));
findContours(dist_8u, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(-1, -1));
for (size_t i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(resultImg, contours, static_cast<int>(i), color, -1, 8, Mat(), 0, Point(-1, -1));
}
printf("玉米粒数量:%d\n", contours.size());
imshow("Final image", resultImg);
waitKey(0);
return 0;
}
运行结果:
灰度图片:
二值化图片:
通过膨胀后的图片:
此操作是为了尽可能的分离黑色对象。
距离变换后的图片:
这里寻找出了特征比较明显的部分。
注意:为什么还要进行腐蚀操作?因为把距离变换后的图像局部二值化之后,会得到下满的结果:
图中我画红色矩形的部分,因为这两颗玉米还有连接,所以我通过腐蚀的操作去除连接的白色小线部分。
之后的结果就是这样:
最后的结果:
好了,至此,玉米粒已经被全部分离了出来。
现在来看看结果:
这与我们数出来的数量一致。