OpenCv-C++-小案例实战-对象计数

现在我有这么一张图:
在这里插入图片描述
我想用图像处理操作数清楚里面的玉米粒到底有多少个。因为这张图中的玉米粒个数比较少,通过细数之后,我们可以发现玉米粒数量有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;

}

运行结果:
灰度图片:
在这里插入图片描述

二值化图片:
在这里插入图片描述

通过膨胀后的图片:
在这里插入图片描述
此操作是为了尽可能的分离黑色对象。

距离变换后的图片:
在这里插入图片描述
这里寻找出了特征比较明显的部分。

注意:为什么还要进行腐蚀操作?因为把距离变换后的图像局部二值化之后,会得到下满的结果:
在这里插入图片描述
图中我画红色矩形的部分,因为这两颗玉米还有连接,所以我通过腐蚀的操作去除连接的白色小线部分。

之后的结果就是这样:
在这里插入图片描述

最后的结果:
在这里插入图片描述
好了,至此,玉米粒已经被全部分离了出来。

现在来看看结果:
在这里插入图片描述

这与我们数出来的数量一致。

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV小项目 这是一个个人在使用OpenCV过程中写的一些小项目,以及一些非常有用的OpenCV代码,有些代码是对某论文中的部分实现。 注意:代码是在Xcode里写的,如果要在win下测试,遇到问题自己修改。 opencv-rootsift-py 用python和OpenCV写的一个rootsift实现,其中RootSIFT部分的代码参照Implementing RootSIFT in Python and OpenCV这篇文章所写,通过这个你可以了解Three things everyone should know to improve object retrieval这篇文章中RootSIFT是怎么实现的。 sift(asift)-match-with-ransac-cpp 用C++OpenCV写的一个图像匹配实现,里面包含了采用1NN匹配可视化、1NN匹配后经RANSAC剔除错配点可视化、1NN/2NN<0.8匹配可视化、1NN/2NN<0.8经 RANSAC剔除错配点可视化四个过程,其中1NN/2NN<0.8匹配过程是Lowe的Raw feature match,具体可以阅读Lowe的Distinctive image features from scale-invariant keypoints这篇文章。这个对图像检索重排非常有用。另外里面还有用OpenCV写的ASIFT,这部分来源于OPENCV ASIFT C++ IMPLEMENTATION,ASIFT还可以到官网页面下载,ASIFT提取的关键点 比SIFT要多得多,速度非常慢,不推荐在对要求实时性的应用中使用。 更多详细的分析可以阅读博文SIFT(ASIFT) Matching with RANSAC。 有用链接 OpenCV3.0文档 // 测试sparse unsigned int centersNum = 10; vector descrNums; descrNums.push_back(8); descrNums.push_back(12); //unsigned int T[] = {1, 2, 1, 3, 2, 5, 4, 3, 10, 5; 4, 2, 6, 5, 2, 5, 4, 6, 2, 4}; unsigned int T[] = {1, 2, 1, 3, 2, 5, 4, 3, 10, 5, 4, 2, 6, 5, 2, 5, 4, 6, 2, 4}; sp_mat Hist(descrNums.size(), centersNum); static long int count = 0; for (int i = 0; i < descrNums.size(); i++){ unsigned int* desrcElementsTmp = new unsigned int[descrNums[i]]; memcpy(desrcElementsTmp, T + count, descrNums[i] * sizeof(T[0])); //cout << desrcElementsTmp[0] << '\t' << desrcElementsTmp[1] << '\t' << desrcElementsTmp[2] << '\t' << desrcElementsTmp[3] << '\t' << desrcElementsTmp[4] << '\t' <<endl; //cout << desrcElementsTmp[5] << '\t' << desrcElementsTmp[6] << '\t' << desrcElementsTmp[7] << '\t' << desrcElementsTmp[8] << '\t' << desrcElementsTmp[9] << '\t' << desrcElementsTmp[10] << '\t' <<endl; //cout << endl; sp_mat X(1, centersNum); X.zeros(); for (int j = 0; j < descrNums[i]; j++){ X(0, desrcElementsTmp[j]-1) += 1; } X.print("X:"); X = X/norm(X, 2); Hist.row(i) = X; count = count + descrNums[i]; delete desrcElementsTmp; } //Hist.print("Hist:");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值