案例背景:统计下图中玉米粒的个数
方案思路:先灰度化,再二值化(基于THRESH_TRIANGLE,图中直方图有明显的双峰值),腐蚀去掉一些小杂点,距离变换,再自适应局部阈值,膨胀连成连通域,寻找轮廓进行计数。
距离变换于1966年被学者首次提出,目前已被广泛应用于图像分析、计算机视觉、模式识别等领域,人们利用它来实现目标细化、骨架提取、形状插值及匹配、粘连物体的分离等。距离变换是针对二值图像的一种变换。在二维空间中,一幅二值图像可以认为仅仅包含目标和背景两种像素,目标的像素值为1,背景的像素值为0;距离变换的结果不是另一幅二值图像,而是一幅灰度级图像,即距离图像,图像中每个像素的灰度值为该像素与距其最近的背景像素间的距离。
方法:
首先对图像进行二值化处理(其要处理成二值图),然后给每个像素赋值为离它最近的背景像素点与其距离(Manhattan距离or欧氏距离),得到distance metric(距离矩阵),那么离边界越远的点越亮。
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main(int arc, char** argv) {
Mat src = imread("1.jpg");
namedWindow("input", CV_WINDOW_AUTOSIZE);
imshow("input", src);
//灰度化
cvtColor(src, src, CV_BGR2GRAY);
//二值化
Mat binaryImg;
threshold(src, binaryImg, 0, 255, THRESH_BINARY | THRESH_TRIANGLE);//对于直方图为单峰或双峰的图像用THRESH_TRIANGLE效果更好
//取反
Mat srcInv;
bitwise_not(binaryImg, srcInv, Mat());
//腐蚀
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
erode(srcInv, srcInv, kernel, Point(-1, -1), 2);
//距离变换
Mat dst;
distanceTransform(srcInv, dst, DIST_L2, 3);
normalize(dst, dst, 0, 1, NORM_MINMAX);
imshow("output1", dst);
//局部自适应阈值
Mat dst_8u;
dst.convertTo(dst_8u, CV_8U);
adaptiveThreshold(dst_8u, dst_8u, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 75,0.0);
//膨胀
dilate(dst_8u, dst_8u, kernel, Point(-1, -1), 2);
imshow("output2", dst_8u);
//寻找轮廓
vector<vector<Point>>contours;
vector<Vec4i>hierarchy;
findContours(dst_8u, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
Mat draw = Mat::zeros(src.size(), CV_8UC3);
RNG rng(1);
for (int i = 0; i < contours.size(); i++) {
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(draw, contours, i, color, -1);
}
printf("number of corns:%d", contours.size());
imshow("output3", draw);
waitKey(0);
return 0;
}