opencv1.0 + vc++1.0数米粒 (基于c++)
1.关于配置
2.关于分割算法
3.关于具体代码实现
关于配置
参考此文
关于分割算法
看到写的不错【👇】:
七种常见阈值分割代码(Otsu、最大熵、迭代法、自适应阀值、手动、迭代法、基本全局阈值法)
关于具体代码实现
相关代码借鉴:
样例1
样例2
mycode:
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"cv.lib")
#pragma comment(lib,"cxcore.lib")
#pragma comment(lib,"highgui.lib")
#pragma comment(lib,"cvaux.lib")
#pragma comment(lib,"cvcam.lib")
// 平均灰度阈值
int aver(IplImage *inputGrayImg)
{
uchar *data= (uchar *)inputGrayImg->imageData;
int wp = inputGrayImg->widthStep;
int i,j;
int sum=0;
for( i = 0; i < inputGrayImg->height; i++)
{
for(j = 0; j < inputGrayImg->width; j++)
{
sum = sum + data[i * wp + j]; //获得所有灰度的和sum
}
}
return int(sum*1.0/(inputGrayImg->height*inputGrayImg->width)+0.5);
}
// 迭代阈值 初始阈值分为两类不断迭代改进阈值
int iteration(IplImage *inputGrayImg,int aver)
{
int threshold = 0;
int newThreshold = aver; //初始阈值 (max+min)/2,这里用均值代替
while(threshold != newThreshold)
{
int p=1,q=1;
int sum1=0,sum2=0;
uchar *data= (uchar *)inputGrayImg->imageData;
int wp = inputGrayImg->widthStep;
int i,j;
for( i = 0; i < inputGrayImg->height; i++)
{
for(j = 0; j < inputGrayImg->width; j++)
{
if(data[i * wp + j]<newThreshold)
{
sum1+=data[i * wp + j];
p++;
}
else
{
sum2+=data[i * wp + j];
q++;
}
}
}
int aver1=int(sum1/p);
int aver2=int(sum2/q);
threshold = newThreshold;
newThreshold = (aver1+aver2)/2;
}
return threshold;
}
//otsu阈值
int otsu(IplImage *inputGrayImg,int u) //u=w0*u0 + w1*u1 图像总平均灰度 wi 前(背)景像素点数占图像的比例 ui前(背)景像素点的平均灰度
{
int hui[256]={0}; //直方图数组
float g[256];
uchar *data= (uchar *)inputGrayImg->imageData;
int wp = inputGrayImg->widthStep;
int i,j;
for( i = 0; i < inputGrayImg->height; i++)
{
for(j = 0; j < inputGrayImg->width; j++)
{
hui[data[i * wp + j]]+=1; //统计图片不同像素值个数
}
}
int sum=inputGrayImg->height*inputGrayImg->width;
for(i=0;i<256;i++)
{
int sum1=1,sum2=1;
int hui1=1,hui2=1;
for(j=0;j<i;j++)
{
sum1+=hui[j];
hui1+=hui[j]*j;
}
for(j=i;j<256;j++)
{
sum2+=hui[j];
hui2+=hui[j]*j;
}
float w0,w1;
int u0,u1;
w0=sum1*1.0/sum;
w1=sum2*1.0/sum;
u0=int(hui1/sum1);
u1=int(hui2/sum2);
g[i]=w0 * (u0 - u) * (u0 - u) + w1 * (u1 - u) * (u1 - u);
}
int max=0;
for(i=1;i<256;++i)
{
if(g[max]<g[i])
max=i;
}
return max;
}
int main(int argc, char* argv[])
{
IplImage*inputImg,*backImg,*backRImg,*cutImg,*dst_contours;
int gray_mean_threshold,iteration_threshold,otsu_threshold;
//load source images
inputImg = cvLoadImage("C:\\Users\\ocean\\Desktop\\images\\rice.jpg",CV_LOAD_IMAGE_GRAYSCALE); //输入图像
backImg = cvLoadImage("C:\\Users\\ocean\\Desktop\\images\\rice.jpg",CV_LOAD_IMAGE_GRAYSCALE); //背景图像
backRImg = cvLoadImage("C:\\Users\\ocean\\Desktop\\images\\rice.jpg",CV_LOAD_IMAGE_GRAYSCALE); //去背景图
cutImg = cvLoadImage("C:\\Users\\ocean\\Desktop\\images\\rice.jpg",CV_LOAD_IMAGE_GRAYSCALE); //分割图
dst_contours = cvLoadImage("C:\\Users\\ocean\\Desktop\\images\\rice.jpg"); // 轮廓图
IplConvKernel* element=cvCreateStructuringElementEx(4,4,1,1,CV_SHAPE_ELLIPSE,0);//形态学结构指针[创建结构元素,4列4行,椭圆形】
cvErode(inputImg,backImg,element,5);//腐蚀
cvDilate(backImg,backImg,element,5);//膨胀
// sub the background
cvSub(inputImg,backImg,backRImg,0); //用原始图像inputImg减去背景图像backImg,backRImg是结果图像
//阈值分割(均值阈值,迭代阈值,otsu阈值,手动设置阈值)
// 均值阈值
gray_mean_threshold = aver(backRImg);
printf("灰度均值阈值:%d\n",gray_mean_threshold);
//迭代阈值
iteration_threshold = iteration(backRImg,gray_mean_threshold);
printf("迭代阈值:%d\n",iteration_threshold);
//otsu阈值
otsu_threshold = otsu(backRImg,gray_mean_threshold);
printf("otsu阈值:%d\n",otsu_threshold);
//手动设置阈值
cvThreshold(backRImg,cutImg, gray_mean_threshold,255,CV_THRESH_BINARY);
//cvThreshold(backRImg,cutImg, iteration_threshold,255,CV_THRESH_BINARY);
//cvThreshold(backRImg,cutImg, otsu_threshold,255,CV_THRESH_BINARY); //cutImg是分割图像
//通过再次腐蚀膨胀去除噪声,得到最终的分割图片
cvErode(cutImg,cutImg,element,1);//腐蚀
cvDilate(cutImg,cutImg,element,1);//膨胀
//count the number of rice 统计米粒个数
CvMemStorage* stor = cvCreateMemStorage(0); //定义一个容器
CvSeq *cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT,sizeof(CvSeq),sizeof(CvPoint),stor);
CvSeq *cont1 = cvCreateSeq(CV_SEQ_ELTYPE_POINT,sizeof(CvSeq),sizeof(CvPoint),stor);
int numberOfObject = cvFindContours(cutImg,stor,&cont,sizeof(CvContour),CV_RETR_TREE);
int numberOfObject1 = cvFindContours(cutImg,stor,&cont1,sizeof(CvContour),CV_RETR_TREE);
printf("米粒个数:%d\n",numberOfObject); //连通域
double tmpArea=0;
double tmpLength=0;
int x1,y1;
for (;cont != NULL;cont=cont->h_next)
{
CvRect rect=cvBoundingRect(cont,0);
double tmpArea1=fabs(cvContourArea(cont,CV_WHOLE_SEQ)); //获得当前米粒的面积
double tmpLength1=cvArcLength(cont);
if(tmpArea<tmpArea1) //更新为最大的米粒的面积和周长
{
tmpArea=tmpArea1;
tmpLength=tmpLength1;
x1=rect.x;
y1=rect.y;
}
//用白色在图像上绘制所有米粒的轮廓
cvDrawContours(dst_contours,cont,CV_RGB(255,255,255),CV_RGB(255,0,0),0,1,8,cvPoint(1,1));//在图像上绘制轮廓.
}
printf("面积为:%f\n周长为:%f\n",tmpArea,tmpLength);
printf("最大面积位置坐标为:%d %d\n",x1,y1);
for (;cont1 != NULL;cont1=cont1->h_next) //再将所有米粒遍历一次,单独为最大米粒的轮廓上色
{
CvRect rect=cvBoundingRect(cont1,0);
double tmpArea1=fabs(cvContourArea(cont1,CV_WHOLE_SEQ));
if(rect.x==x1&&y1==rect.y&&tmpArea1==tmpArea)
//用红色在图像上绘制最大米粒的轮廓
cvDrawContours(dst_contours,cont1,CV_RGB(255,0,0),CV_RGB(255,0,0),0,1,8,cvPoint(1,1));//在图像上绘制轮廓.
}
//show the result
cvNamedWindow("inputImg");
cvShowImage("inputImg",inputImg);
cvNamedWindow("backImg");
cvShowImage("backImg",backImg);
cvNamedWindow("backRImg");
cvShowImage("backRImg",backRImg);
cvNamedWindow("cutImg");
cvShowImage("cutImg",cutImg);
cvNamedWindow("dst_contours");
cvShowImage("dst_contours",dst_contours);
cvWaitKey(0);
return 0;
}