opencv学习笔记四十九:基于距离变换和局部自适应阈值的对象计数

案例背景:统计下图中玉米粒的个数

方案思路:先灰度化,再二值化(基于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;
}

猜你喜欢

转载自blog.csdn.net/qq_24946843/article/details/82770547