一.问题描述
农业领域经常需要计算对象个数或者在其他领域进行拍照自动计数,可以提高效率,降低成本。
二.解决思路
二值分割+形态学处理+距离变换+连通区域计算
程序的思路:(对照运行结果理解)
【1】源图像载入
【2】首先将源图像进行二值化操作
【3】进行形态学膨胀操作,通过将高亮区域变大,使得原本紧挨着的各个计数对象变小一点(背景是白色,对象是黑色)
【4】通过距离变换,进行粘连物体的分离
【5】进行自定义阈值操作,即消除距离变换后连接各个计数区域的细线
【6】再次进行形态学膨胀操作,将将高亮区域变大,使得原本紧挨着的各个计数对象变大一点(因为用了取反操作,此时背景是黑色,对象是白色)
【7】为了美观,将各个计数区域进行上色处理
三.参考的博客
https://www.cnblogs.com/long5683/p/9750219.html
OpenCV之bitwise_and、bitwise_not等图像基本运算及掩膜
https://blog.csdn.net/u011028345/article/details/77278467
API:自适应阈值化操作:adaptiveThreshold()函数
https://www.cnblogs.com/GaloisY/p/11037350.html
API:OpenCV距离变换函数:distanceTransform()介绍
https://blog.csdn.net/liubing8609/article/details/78483667
四.源程序
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat src = imread("E:\\pictures\\45.png");
//medianBlur(src, src,5);//中值滤波,去除椒盐噪声
imshow("【1】原图", src);
Mat src_gray, binary;
cvtColor(src, src_gray, COLOR_BGR2GRAY);
threshold(src_gray, binary, 0, 255, THRESH_BINARY | THRESH_TRIANGLE);//颜色单一时,使用THRESH_TRIANGLE比OTSU好
imshow("【2】二值化后", binary);
//形态学操作
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
//膨胀操作,对图像中的高亮部分进行膨胀,拥有比原图更大的高亮区域
dilate(binary, binary, kernel, Point(-1, -1), 4);
imshow("【3】膨胀后", binary);
//距离变换
Mat dist;
bitwise_not(binary, binary);//取反
distanceTransform(binary, dist, DIST_L2, 3);
normalize(dist, dist, 0, 1.0, NORM_MINMAX); //矩阵归一化
imshow("【4】距离变换后", dist);
//阈值化二值分割
//threshold(dist, dist,0.7,1.0,THRESH_BINARY);//对距离进行筛选,去除边缘部分
//normalize(dist, dist, 0, 255, NORM_MINMAX);
Mat dist_8U;
dist.convertTo(dist_8U, CV_8U); //图像类型转换
// 高斯阈值 85 必须是奇数
adaptiveThreshold(dist_8U, dist_8U, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 85, 0.0);//自适应阈值,代替上面的阈值操作
//形态学操作,使得断开部分连接
imshow("【5】自适应阈值后", dist_8U);
kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));
dilate(dist_8U, dist_8U, kernel, Point(-1, -1), 3);
imshow("【6】形态学操作后", dist_8U);
// 连通区域计数
vector<vector<Point>> contours;
findContours(dist_8U, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// 效果显示
Mat markers = Mat::zeros(src.size(), CV_8UC3);
RNG rng(12345);
for (size_t t = 0; t < contours.size(); t++)
{
//对不同的区域进行上色
drawContours(markers, contours, static_cast<int>(t), Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)),-1, 8, Mat());
}
printf("number of corns : %d", contours.size());
imshow("【7】效果图", markers);
waitKey(0);
return 0;
}
运行结果: