机器视觉实验合集:
机器视觉-模板匹配实验(vc++6.0 + opencv1.0)
机器视觉-数米粒实验(vc++6.0 + opencv1.0)
机器视觉-手写数字识别(vc++6.0 + opencv1.0)
本实验基于学校课程要求,实验环境采用vc++6.0 + opencv1.0
注:在第七步时会获得不同的阈值,从而得到不同的cutImg,导致对8、9、10步的结果产生一定影响,这是正常现象
实验步骤:
1.创建工程文件(项目)
2.创建源文件
3.为项目配置OpenCV环境
4.读取原图像
5.通过腐蚀膨胀获得背景图像
6.用原始图像减去背景图像,得到去背景图像
7.将去背景图像通过阈值分割(分别利用均值灰度、迭代方法、OTSU方法获得阈值),然后得到不同的分割图像,并进行去噪处理
8.对得到的不同的分割图像分别进行分析,查找轮廓,得到米粒数
9.遍历轮廓,得到最大的米粒,以及周长面积、位置,并用白色画出轮廓
10.对原图像进行遍历,将上一步中最大米粒的轮廓用不同颜色标识出来
具体步骤实施如下:
1.创建工程文件(项目),如图所示依次点击,创建空白工程
2.按照下图依次点击,创建c++source file,并命名为main,记住一定要勾选右上角添加到工程!!!然后点击确认
3.为该项目配置OpenCV环境,参考 https://blog.csdn.net/weixin_44496128/article/details/105271821
重点检查第6、8、9步是否配置到位,在此默认读者已独立配置完成,主要向读者介绍实验后续操作!!!
4.读取原图像,在主函数中写如下代码(记得替换成你自己的路径)
IplImage*inputImg,*backImg,*backRImg,*cutImg,*dst_contours;
int averyuzhi,iteryuzhi,otsuyuzhi;
//加载图像
inputImg = cvLoadImage("D:\\vc++\\Microsoft Visual Studio\\MyProjects\\机器视觉2020\\米粒实验\\rice.jpg" , CV_LOAD_IMAGE_GRAYSCALE);
backImg = cvLoadImage("D:\\vc++\\Microsoft Visual Studio\\MyProjects\\机器视觉2020\\米粒实验\\rice.jpg" , CV_LOAD_IMAGE_GRAYSCALE);
backRImg = cvLoadImage("D:\\vc++\\Microsoft Visual Studio\\MyProjects\\机器视觉2020\\米粒实验\\rice.jpg" , CV_LOAD_IMAGE_GRAYSCALE);
cutImg = cvLoadImage("D:\\vc++\\Microsoft Visual Studio\\MyProjects\\机器视觉2020\\米粒实验\\rice.jpg" , CV_LOAD_IMAGE_GRAYSCALE);
dst_contours = cvLoadImage("D:\\vc++\\Microsoft Visual Studio\\MyProjects\\机器视觉2020\\米粒实验\\rice.jpg");
5.通过腐蚀膨胀获得背景图像
//得到背景图像
IplConvKernel* element=cvCreateStructuringElementEx(4,4,1,1,CV_SHAPE_ELLIPSE,0);//形态学结构指针[创建结构元素,4列4行,椭圆形】
cvErode(inputImg,backImg,element,5);//腐蚀
cvDilate(backImg,backImg,element,5);//膨胀
6.用原始图像减去背景图像,得到去背景图像
//得到去背景图像
cvSub(inputImg,backImg,backRImg,0);//用原始图像减去背景图像,backRImg是结果图像
7.将去背景图像通过阈值分割(分别利用均值灰度、迭代方法、OTSU方法获得阈值),然后得到不同的分割图像,并进行去噪处理
//调用函数得到分割图像
averyuzhi=aver(backRImg);//均值方法获得阈值
iteryuzhi=iterate(backRImg,averyuzhi);//迭代方法获得迭代阈值
otsuyuzhi=otsu(backRImg,averyuzhi);//OTSU方法获得阈值
printf("均值方法获取的阈值为:%d\n",averyuzhi);
printf("迭代方法获取的阈值为:%d\n",iteryuzhi);
printf("OTSU方法获取的阈值为:%d\n",otsuyuzhi);
//分别利用不同的阈值得到分割图像,cutImg是分割图像
//cvThreshold(backRImg,cutImg,averyuzhi,255,CV_THRESH_BINARY);
//cvThreshold(backRImg,cutImg,iteryuzhi,255,CV_THRESH_BINARY);
cvThreshold(backRImg,cutImg,otsuyuzhi,255,CV_THRESH_BINARY);
//通过再次腐蚀膨胀去除噪声,得到最终的分割图片
cvErode(cutImg,cutImg,element,1);//腐蚀
cvDilate(cutImg,cutImg,element,1);//膨胀
8.对得到的不同的分割图像分别进行分析,查找轮廓,得到米粒数
//查找轮廓,得到米粒数
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);
9.遍历轮廓,得到最大的米粒,以及周长面积,并用白色画出轮廓
//遍历轮廓并画出外轮廓
float tmpArea=0;
float 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);
10.对原图像进行遍历,将最大米粒的轮廓用不同颜色标识出来
for (;cont1 != NULL;cont1=cont1->h_next)//再将所有米粒遍历一次,单独为最大米粒的轮廓上色
{
CvRect rect=cvBoundingRect(cont1,0);
float 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));
}
11.按照不同步骤的需求命名并输出相应的图片,例如第10步中三种不同阈值导致所获得的最终结果图
注:以下为部分上述提到,但未展示的代码或函数
输出图像的代码如下:
//例如命名并输出步骤5中的背景图像
cvNamedWindow("back");
cvShowImage("back",backImg);
三种求阈值的函数分别如下所示:
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 iterate(IplImage *inputGrayImg,int a)//迭代阈值
{
int threshold = 0; int newThreshold = a;
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 avg1=int(sum1/p);int avg2=int(sum2/q);
threshold = newThreshold;
newThreshold = (avg1+avg2)/2;
}
return threshold;
}
int otsu(IplImage *inputGrayImg,int u)//OTSU方法
{
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;
}