一.简介: 通过摄像头读取含有一个水果九宫格的图像,含八个苹果和一个香蕉,判断出香蕉的位置(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分别代表某点的三个通道的数值,如图:
四.算法介绍: 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]; } }五.总结:测试结果稳定。如有其他方法或建议,可以互相交流。