Machine Vision - Counting Rice Experiment (vc++6.0 + opencv1.0)

Collection of machine vision experiments:
machine vision - template matching experiment (vc++6.0 + opencv1.0)
machine vision - counting rice grains experiment (vc++6.0 + opencv1.0)
machine vision - handwritten digit recognition (vc++6.0 + opencv1 .0)

This experiment is based on the school curriculum requirements. The experimental environment uses vc++6.0 + opencv1.0
. Note: Different thresholds will be obtained in the seventh step, resulting in different cutImg, which will have a certain impact on the results of steps 8, 9, and 10. ,it `s just normal

Experimental steps:
1. Create engineering files (projects)
2. Create source files
3. Configure the OpenCV environment for the project
4. Read the original image
5. Obtain the background image through corrosion and expansion
6. Subtract the background image from the original image to obtain the background removal Image
7. Segment the background-removed image through threshold segmentation (use mean grayscale, iterative method, and OTSU method to obtain the threshold respectively), and then obtain different segmented images, and perform denoising processing. 8. Analyze the different segmented images obtained
. , search the contour, and get the number of rice grains.
9. Traverse the contour, get the largest rice grain, as well as the perimeter area and position, and draw the outline in white.
10. Traverse the original image and mark the outline of the largest rice grain in the previous step with different colors.

The specific steps are as follows:
1. Create a project file (project) and click in sequence as shown in the figure to create a blank project Insert image description here
2. Click in sequence as shown in the figure below to create a c++ source file and name it main. Remember to check it Add the upper right corner to the project! ! ! Then click Confirm
Insert image description here
3. Configure the OpenCV environment for the project, refer to https://blog.csdn.net/weixin_44496128/article/details/105271821

Focus on checking whether steps 6, 8, and 9 are configured in place. By default, the reader has completed the configuration independently. We mainly introduce the follow-up operations of the experiment to the reader! ! !

4. Read the original image and write the following code in the main function (remember to replace it with your own path)

  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. Obtain background image through corrosion expansion

  //得到背景图像
  IplConvKernel* element=cvCreateStructuringElementEx(4,4,1,1,CV_SHAPE_ELLIPSE,0);//形态学结构指针[创建结构元素,4列4行,椭圆形】

  cvErode(inputImg,backImg,element,5);//腐蚀
  cvDilate(backImg,backImg,element,5);//膨胀

6. Subtract the background image from the original image to obtain the background-removed image

  //得到去背景图像
  cvSub(inputImg,backImg,backRImg,0);//用原始图像减去背景图像,backRImg是结果图像

7. Segment the background-removed image through threshold value (use mean grayscale, iterative method, and OTSU method to obtain the threshold respectively), and then obtain different segmented images and perform denoising processing.

  //调用函数得到分割图像
  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. Analyze the different segmented images obtained, find the contours, and obtain the number of rice grains.

  //查找轮廓,得到米粒数
  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. Traverse the outline to get the largest rice grain and perimeter area, and draw the outline in white

  //遍历轮廓并画出外轮廓
  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. Traverse the original image and mark the outline of the largest rice grain in different colors.

  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. Name and output the corresponding pictures according to the needs of different steps. For example, in step 10, the three different thresholds lead to the final result obtained. Note: The following are some of the codes mentioned above but not
Insert image description here
shown or
the code of the function output image. as follows:

    //例如命名并输出步骤5中的背景图像
    cvNamedWindow("back");
    cvShowImage("back",backImg);

The three threshold functions are as follows:

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;
}

Guess you like

Origin blog.csdn.net/weixin_44496128/article/details/105282454