C++
提示:以下是本篇文章正文内容,下面案例可供参考
//米粒分割算法
void ImageSmoothHandle::riceDetection()
{
Mat oImg, imageOutput, imageOtsu;
string fp = "../resource/米粒图片.png";//相对路径,相对.cpp 文件的路径
oImg = readImageV2(fp);
//imshow("rice Img",oImg);
Mat gray, dist, element, dist2;
//图像预处理
cvtColor(oImg, gray, CV_RGB2GRAY);//预处理错误将导致内存错误
//imshow("gray", gray);
//waitKey();
//使用局部阈值得大津算法进行图像的二值化
//参数1:InputArray类型的src,输入图像,填单通道,单8位浮点类型Mat即可
//参数2:函数运算后的结果存放在这。即为输出图像(与输入图像同样的尺寸和类型)。
//参数3:预设满足条件的最大值。
//参数4:指定自适应阈值算法。可选择ADAPTIVE_THRESH_MEAN_C 或 ADAPTIVE_THRESH_GAUSSIAN_C两种。(具体见下面的解释)。
//参数5:指定阈值类型。可选择THRESH_BINARY或者THRESH_BINARY_INV两种。(即二进制阈值或反二进制阈值)。
//参数6:表示邻域块大小,用来计算区域阈值,一般选择为3、5、7......等。
//参数7:参数C表示与算法有关的参数,它是一个从均值或加权均值提取的常数,可以是负数。(具体见下面的解释)。
adaptiveThreshold(gray, dist, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 17, 0);
imshow("threshold", dist);
//waitKey();
//函数会返回指定形状和尺寸的结构元素
//第一个参数表示内核的形状,有三种形状可以选择
//矩形:MORPH_RECT;
//交叉形:MORPH_CORSS;
//椭圆形:MORPH_ELLIPSE;
//第二和第三个参数分别是内核的尺寸以及锚点的位置。
element = getStructuringElement(MORPH_RECT, Size(3, 3));
//imshow("shap", element);
//waitKey();
//开运算:
//先腐蚀,再膨胀,可清除一些小东西(亮的),放大局部低亮度的区域
//闭运算:
//先膨胀,再腐蚀,可清除小黑点
//形态学梯度:
//膨胀图与腐蚀图之差,提取物体边缘
//顶帽:
//原图像 - 开运算图,突出原图像中比周围亮的区域
//黑帽:
//闭运算图 - 原图像,突出原图像中比周围暗的区域
//imshow("ddd", dist);
//waitKey();
morphologyEx(dist, dist2, MORPH_OPEN, element);
//imshow("sss", dist2);
//waitKey();
//Mat temp;
//IplConvKernel * myModel;
//函数中参数cols,rows确定了构造的矩形大小。
//anchor_x, anchor_y确定了封闭矩形内参考点的横纵坐标。
//shape是自定义核的形状,具体为
//1.CV_SHAPE_RECT 核是矩形
//2.CV_SHAPE_CROSS 核是勺子交叉形
//3.CV_SHAPE_ELLIPSE 核是椭圆形
//4.CV_SHAPE_CUSTOM 核是用户自定义类型
//myModel = cvCreateStructuringElementEx(3, 3, 2, 2, CV_SHAPE_RECT);
//参数分别为原图像,输出图像,temp临时图像,自定义核,形态学操作方法(CV_MOP_OPEN CV_MOP_CLOSE CV_MOP_GRADIENT CV_MOP_TOPHAT CV_MOP_BLACKHAT)
//cvMorphologyEx(&dist, &dist2, temp,myModel, CV_MOP_OPEN, 1);
//imshow("dj",temp);
//cvShowImage("xxx", temp);
//waitKey();
Mat contours, hierarchy, seg;
vector> cnts;
//第一个参数:image,单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;
//第二个参数:contours,定义为“vector> contours”,是一个向量,并且是一个双重向量,向量内每个元素保存了一组由连续的Point点构成的点的集合的向量,每一组Point点集就是一个轮廓。有多少轮廓,向量contours就有多少元素。
//第三个参数:hierarchy,定义为“vector hierarchy”;Vec4i是Vec的别名,定义了一个“向量内每一个元素包含了4个int型变量”的向量
//向量hiararchy内的元素和轮廓向量contours内的元素是一一对应的,向量的容量相同。
//hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],分别表示第i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。如果没有对应项,则相应的hierarchy[i]设置为负数。
//第四个参数:int型的mode,定义轮廓的检索模式:
//取值一:CV_RETR_EXTERNAL只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
//取值二:CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关
//系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,
//RETR_CCOMP:提取所有轮廓,并将轮廓组织成双层结构(two-level hierarchy),顶层为连通域的外围边界,次层位内层边界
//第五个参数:int型的method,定义轮廓的近似方法:
//CHAIN_APPROX_NONE:获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1
//CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息
seg = dist2.clone();
findContours(seg, cnts, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);//轮廓检测函数
//drawContours(dist2, contours, -1, (120, 0, 0), 2);//绘制轮廓
int count = 0;//米粒总数
Rect rect;
string strCount;
double length;
vector> er;
RotatedRect minArea;
Point p[100];
for (int i = cnts.size() - 1; i >= 0; i--)
{
vector c = cnts[i];
//计算整个轮廓或部分轮廓的面积
double area = contourArea(c);
if (area < 10)//滤除面积小于10的分割结果,可能是噪声
continue;
count++;
//cout << "blob" << i << ":" << area << endl;
//rect = boundingRect(c);//计算轮廓包围矩形(水平的)
minArea = minAreaRect(c);
//boxPoints(minArea, er);
//length = arcLength(er,true);
rectangle(oImg, rect, Scalar(0, 0, 0xff), 1);//在原始图像上画出包围矩形,并给每个矩形标号
stringstream ss;
ss << count;
ss >> strCount;
putText(oImg, strCount, Point(rect.x, rect.y), CV_FONT_HERSHEY_PLAIN, 0.5, Scalar(0, 0xff, 0));
//waitKey();
}
cout << "米粒数量" << count << endl;
imshow("原图", oImg);
imshow("形态学去噪",dist2);
waitKey();
}
原图:
阈值:
形态学去噪 (开运算)
形态学去噪(闭运算)