机器视觉-数米粒实验(vc++6.0 + opencv1.0)

机器视觉实验合集:
机器视觉-模板匹配实验(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;
}
  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值