一.简介:
通过摄像头读取含有一个水果九宫格的图像,含八个苹果和一个香蕉,判断出香蕉的位置(1~9)。场景如图:

水果图像识别系统硬件部分 识别图中水果_九宫格

二.计算准备
1.下载安装opencv并配置到visual studio中。
2.读取图像,代码如下:

VideoCapture cc;//初始化一个VideoCapture对象
cc.open(“C:\Users\…vedio.mp4”);//读取该地址上的视频
 cc.open(0) //调用电脑摄像头
 cc.open(1)//调用连接在电脑上的摄像头
 //以上三选一Mat frame;//初始化图像矩阵
 cc>>frame;//读取图像三.图像基础知识
 1.首先推荐一篇非常值得一看的图像二值化介绍 2.上面文章的部分摘引:
 在计算机视觉里,一般用矩阵来表示图像。在这个矩阵里,每一个像素就是矩阵中的一个元素。在三通道的彩色图像中,这个元素是由三个数字组成的元组。所以首先要掌握访问像素点数值的方法,这里只介绍一种就够用了:.at< Vec3b>(i, j)[n] ,i和j分别为行列数,n是颜色通道。实际使用例如:
 frame.at< Vec3b>(i, j)[0]=255;
 cout<<frame.at< Vec3b>(i, j)[1];
 frame.at< Vec3b>(i, j)[2]=frame.at< Vec3b>(i+1, j)[2] ;
//frame 是由刚刚的VideoCapture读取进来的图像矩阵
 3.通常我们用hsv来判断颜色,具体原因可自行查阅,在hsv中,我们可以用特定的区间来区分各种颜色,H,S,V分别代表某点的三个通道的数值,如图:

水果图像识别系统硬件部分 识别图中水果_水果图像识别系统硬件部分_02

四.算法介绍:
1.首先的工作是区分各个红色连通区域,这是为了避免把同一个苹果识别成多个。此处使用的是深搜算法,这个算法可以用来解决迷宫问题,关于搜索算法,推荐一个app:“动画演示算法”加以理解,网上也有很多相关博客帮助学习。
这个问题中,我们用一个队列来储存坐标,我们可以理解为一个“数组”,下面用粗略的伪代码解释一下:

while 队列不为空{
取出队列的第一个坐标(x,y);
if 点(x+1,y)的颜色是红色{
    将(x+1,y)加入队列,并改为白色}
if 点(x-1,y)的颜色是红色{
    将(x-1,y)加入队列,并改为白色}
if 点(x,y+1)的颜色是红色{
    将(x,y+1)加入队列,并改为白色}
if 点(x,y-1)的颜色是红色{
    将(x,y-1)加入队列,并改为白色}
    }

简而言之,检查红色点周围的点,如果同样是红色,则判定为同一区域,不重复计数,在这个过程中,可以数出这个区域的像素量,像素量过少的区域显然不是我们要考虑的范围(事实上,大约像素量大于1000的区域才是有用的)。用这样的方法,我们可以找出所有红色或黄色连通区域,选取区域中某个点的坐标作为这块区域的大致坐标,便完成了定位。

2.虽然我们定位出来有限多个红色或黄色连通区域,但是观察一下上面的具体场景我们可以发现,具体场景中是存在不少干扰的,例如黄色的桌子,显示器下方红色的框条,这些区域的面积并不比水果小,所以仅靠上面的计算我们并不能认为连通区域便是水果。

3.为解决这个问题,我们需要定位出九宫格,这对我们肉眼来说轻而易举,开始如何告诉编译器呢。这个有很多钟方法,有的朋友是用opencv的算法定位出了九宫格中的十字架,在这个坐标范围外的排除即可,但是,嗯,我不太会用opencv的各种函数,所以决定算方差。备注,在前面的搜索过程中,我保留了像素量较大的12个红色区域和5个黄色区域,”方差“不是按照数学课本的公式,稍微简化了一下,代码如下:

//避免苹果纵坐标越界
  for (int t = 0; t < 12; t++)
  {
   for (int u = 0; u < 12; u++)
   {
    if (u == t)continue;
    if (abs(apple[u][0] - apple[t][0]) < 30)break;
    if (u == 11 || (t == 11 && u == 10))
    {
     for (int k = t + 1; k < 12; k++)
     {
      apple[k - 1][0] = apple[k][0];
      apple[k - 1][1] = apple[k][1];
      apple[k - 1][2] = apple[k][2];
     }
     t--;
    }
   }
  }

横坐标同样排查一遍,由于我是用数组储存的坐标,所以删除不符合坐标的过程有些繁琐。苹果的排查是以其他同色区域为参考,确定八个苹果坐标后,香蕉的排查就可以以苹果坐标为参考。

for (int t = 0; t < 5; t++)
  {
   bool x = 0, y = 0;
   for (int u = 0; u < 8; u++)
   {
    if (abs(apple[u][0] - banana[t][0]) < 30)
    {
     x = 1;
     break;
    }
   }
   for (int u = 0; u < 8; u++)
   {
    if (abs(apple[u][1] - banana[t][1]) < 50)
    {
     y = 1;
     break;
    }
   }
   if (x == 1 && y == 1&& banana[t][2]>real_bana[2])
   {
    real_bana[0] = banana[t][0];
    real_bana[1] = banana[t][1];
    real_bana[2] = banana[t][2];
   }
  }

五.总结:测试结果稳定。如有其他方法或建议,可以互相交流。