赞
踩
大家好,我是松鼠,好久不见啦,今天跟大家分享一下魔方机器人的视觉部分——opencv颜色识别。
先给自己叠个甲,因为这些知识都是自学的,所以本文只算是抛砖引玉,做的不够好的地方可以一起交流~
Vol.1
什么是魔方机器人?
相信大家都见过魔方吧?大家可以把魔方打乱,然后再把它扭回六个面。而魔方机器人就是可以把打乱的魔方进行复原的机器人。
Vol.2
什么是视觉?
机器视觉本质上是让机器人能感受到世界的信息,就像人用眼睛去看东西,机器也需要用摄像头去看东西,然后把信息转化为机器人需要做出的应答。
魔方机器人的视觉就是通过对颜色的识别,让机器人按照算法对魔方实现控制,使魔方在最短的步数之内复原。
♬..~ ♫. ♪..
话不多说,我们进入正题,我用的是opencv来识别颜色,接下来,我一定让有着一定基础的你有所收获!
首先我们举个例子,这是我从网上找到的一张魔方机器人摄像头拍摄到的图片。
很明显,这个视角看魔方很不直观,对于笨笨的机器人来说,这样的图片需要做一些处理才能让它识别。
我们可以通过透视变换来处理图像(简而言之就是把图片看魔方的视角变为正视):
- # 透视变换
- width,height = 300,300 #设定图片大小
- pts1 = np.float32([[25,68],[203,19],[32,309],[186,400]]) #划分第一张图的四个点
- pts2 = np.float32([[210,20],[370,72],[196,400],[363,318]]) #划分第二张图的四个点
- pts3 = np.float32([[0,0],[width,0],[0,height],[width,height]]) #转化为第一张图片
- pts4 = np.float32([[0,0],[width,0],[0,height],[width,height]]) #转化为第二张图片
- matrix1 = cv2.getPerspectiveTransform(pts1,pts3)
- imgOutput1 = cv2.warpPerspective(img,matrix1,(width,height)) #第一张图透视变换
- matrix2 = cv2.getPerspectiveTransform(pts2,pts4)
- imgOutput2 = cv2.warpPerspective(img,matrix2,(width,height)) #第二张图透视变换
透视变化以后:
OK,现在我们得到了两张图片:
那么现在,我这里有两个方案去识别颜色:
1 通过边缘检测来识别魔方的格子(通过检测边缘,找出四边形即为魔方格子,检测格子颜色)
2 通过分割的手段来识别(把一张图片分割成九个部分,识别每个部分的颜色占比)
♬..♩~ ♫. ♪..
通过实践发现,因为图片太模糊了,边缘检测的方法只能检测出少数几个格子。
很明显,效果不是很好,所以我在下面主要介绍分割的方法。
- pic1 = imgOutput1[0:100,0:100]
- pic2 = imgOutput1[0:100,100:200]
- pic3 = imgOutput1[0:100,200:300]
- ...
- #以此类推
因为我们已经设置了图片为(300,300),我们可以直接通过循环的方式分割9个格子:
- for i in range(3):
- for j in range(3):
- y_start = i*width//3
- y_end = (i+1)*width//3
- x_start = j * width // 3
- x_end = (j + 1) * width // 3
- pic = imgOutput1[y_start:y_end,x_start:x_end]
这样会方便很多~
然后格子就会单独成一张图片啦!
效果如下:
(这是我通过cv2.imwrite保存得来的,有兴趣的朋友自己试试)
♬..♩~ ♫. ♪..
看到这里,你已经离识别成功不远啦~
接下来,我们的流程是:
1、先找出魔方各个颜色的HSV范围(这一点是基础)
2、把图片转为HSV格式
3、利用cv2.inRange函数设阈值,去除背景部分
4、利用cv2.countNonZero函数计算阈值内的比例,即颜色比例,超过某个值则判断为某个颜色
5、逻辑判断,判断颜色
我认为最让人头疼的就是问题就是:某个颜色的HSV值范围如何确定?
我这里是通过鼠标点击图片的事件去获取该点的HSV值,然后制定大概的范围作为某种颜色的HSV识别范围。
主要代码如下:
- def getpos(event,x,y,flags,param):
- if event==cv2.EVENT_LBUTTONDOWN: #定义一个鼠标左键按下去的事件
- print(HSV[y,x])
- cv2.setMouseCallback("imageHSV",getpos)#返回HSV值
话不多说上代码:
- for i in range(3):
- for j in range(3):
- y_start = i*width//3
- y_end = (i+1)*width//3
- x_start = j * width // 3
- x_end = (j + 1) * width // 3
- pic = imgOutput1[y_start:y_end,x_start:x_end]
- hsv = cv2.cvtColor(pic,cv2.COLOR_BGR2HSV)
- mask_red = cv2.inRange(hsv,red_min,red_max)
- red_bili = cv2.countNonZero(mask_red)/(pic.size/3)
- mask_yellow = cv2.inRange(hsv, yellow_min, yellow_max)
- yellow_bili = cv2.countNonZero(mask_yellow) / (pic.size / 3)
- mask_blue = cv2.inRange(hsv, blue_min, blue_max)
- blue_bili = cv2.countNonZero(mask_blue) / (pic.size / 3)
- mask_white = cv2.inRange(hsv, white_min, white_max)
- white_bili = cv2.countNonZero(mask_white) / (pic.size / 3)
- mask_orange = cv2.inRange(hsv, orange_min, orange_max)
- orange_bili = cv2.countNonZero(mask_orange) / (pic.size / 3)
- mask_green = cv2.inRange(hsv, green_min, green_max)
- green_bili = cv2.countNonZero(mask_green) / (pic.size / 3)
- if red_bili >= 0.4:
- print("第一张图第"+str(3*i+j+1)+"个格子:红")
- if yellow_bili >= 0.4:
- print("第一张图第"+str(3*i+j+1)+"个格子:黄")
- if blue_bili >= 0.4:
- print("第一张图第"+str(3*i+j+1)+"个格子:蓝")
- if white_bili >= 0.4:
- print("第一张图第"+str(3*i+j+1)+"个格子:白")
- if orange_bili >= 0.3:
- print("第一张图第"+str(3*i+j+1)+"个格子:橙")
- if green_bili >= 0.4:
- print("第一张图第" + str(3 * i + j + 1) + "个格子:绿")
效果如下:
总代码:
- import cv2
- import numpy as np
- img = cv2.imread('./cube.png')
- width,height = 300,300 #设定图片大小
- yellow_min = np.array([30,40,150])
- yellow_max = np.array([60,80,190])
- red_min = np.array([3,240,90])
- red_max = np.array([15,255,120])
- blue_min = np.array([95,220,90])
- blue_max = np.array([115,255,160])
- white_min = np.array([110,9,160])
- white_max = np.array([125,45,190])
- orange_min = np.array([6,205,235])
- orange_max = np.array([10,225,255])
- green_min = np.array([80,240,100])
- green_max = np.array([85,255,120])
- pts1 = np.float32([[25,68],[203,19],[32,309],[186,400]]) #划分第一张图的四个点
- pts2 = np.float32([[210,20],[370,72],[196,400],[363,318]]) #划分第二张图的四个点
- pts3 = np.float32([[0,0],[width,0],[0,height],[width,height]]) #转化为第一张图片
- pts4 = np.float32([[0,0],[width,0],[0,height],[width,height]]) #转化为第二张图片
- # 透视变换
- matrix1 = cv2.getPerspectiveTransform(pts1,pts3)
- imgOutput1 = cv2.warpPerspective(img,matrix1,(width,height)) #第一张图透视变换
- matrix2 = cv2.getPerspectiveTransform(pts2,pts4)
- imgOutput2 = cv2.warpPerspective(img,matrix2,(width,height)) #第二张图透视变换
- # cv2.imshow("原图",img)
- cv2.imshow("Output1",imgOutput1)
- cv2.imshow("Output2",imgOutput2)
- cv2.imwrite("./photos/Output1.png",imgOutput1)
- cv2.imwrite("./photos/Output2.png",imgOutput2)
- for i in range(3):
- for j in range(3):
- y_start = i*width//3
- y_end = (i+1)*width//3
- x_start = j * width // 3
- x_end = (j + 1) * width // 3
- pic = imgOutput1[y_start:y_end,x_start:x_end]
- hsv = cv2.cvtColor(pic,cv2.COLOR_BGR2HSV)
- mask_red = cv2.inRange(hsv,red_min,red_max)
- red_bili = cv2.countNonZero(mask_red)/(pic.size/3)
- mask_yellow = cv2.inRange(hsv, yellow_min, yellow_max)
- yellow_bili = cv2.countNonZero(mask_yellow) / (pic.size / 3)
- mask_blue = cv2.inRange(hsv, blue_min, blue_max)
- blue_bili = cv2.countNonZero(mask_blue) / (pic.size / 3)
- mask_white = cv2.inRange(hsv, white_min, white_max)
- white_bili = cv2.countNonZero(mask_white) / (pic.size / 3)
- mask_orange = cv2.inRange(hsv, orange_min, orange_max)
- orange_bili = cv2.countNonZero(mask_orange) / (pic.size / 3)
- mask_green = cv2.inRange(hsv, green_min, green_max)
- green_bili = cv2.countNonZero(mask_green) / (pic.size / 3)
- if red_bili >= 0.4:
- print("第一张图第"+str(3*i+j+1)+"个格子:红")
- if yellow_bili >= 0.4:
- print("第一张图第"+str(3*i+j+1)+"个格子:黄")
- if blue_bili >= 0.4:
- print("第一张图第"+str(3*i+j+1)+"个格子:蓝")
- if white_bili >= 0.4:
- print("第一张图第"+str(3*i+j+1)+"个格子:白")
- if orange_bili >= 0.3:
- print("第一张图第"+str(3*i+j+1)+"个格子:橙")
- if green_bili >= 0.4:
- print("第一张图第" + str(3 * i + j + 1) + "个格子:绿")
- for i in range(3):
- for j in range(3):
- y_start = i*width//3
- y_end = (i+1)*width//3
- x_start = j * width // 3
- x_end = (j + 1) * width // 3
- pic = imgOutput2[y_start:y_end,x_start:x_end]
- hsv = cv2.cvtColor(pic,cv2.COLOR_BGR2HSV)
- mask_red = cv2.inRange(hsv,red_min,red_max)
- red_bili = cv2.countNonZero(mask_red)/(pic.size/3)
- mask_yellow = cv2.inRange(hsv, yellow_min, yellow_max)
- yellow_bili = cv2.countNonZero(mask_yellow) / (pic.size / 3)
- mask_blue = cv2.inRange(hsv, blue_min, blue_max)
- blue_bili = cv2.countNonZero(mask_blue) / (pic.size / 3)
- mask_white = cv2.inRange(hsv, white_min, white_max)
- white_bili = cv2.countNonZero(mask_white) / (pic.size / 3)
- mask_orange = cv2.inRange(hsv, orange_min, orange_max)
- orange_bili = cv2.countNonZero(mask_orange) / (pic.size / 3)
- mask_green = cv2.inRange(hsv, green_min, green_max)
- green_bili = cv2.countNonZero(mask_green) / (pic.size / 3)
- if red_bili >= 0.4:
- print("第二张图第"+str(3*i+j+1)+"个格子:红")
- if yellow_bili >= 0.4:
- print("第二张图第"+str(3*i+j+1)+"个格子:黄")
- if blue_bili >= 0.4:
- print("第二张图第"+str(3*i+j+1)+"个格子:蓝")
- if white_bili >= 0.4:
- print("第二张图第"+str(3*i+j+1)+"个格子:白")
- if orange_bili >= 0.3:
- print("第二张图第"+str(3*i+j+1)+"个格子:橙")
- if green_bili >= 0.4:
- print("第二张图第" + str(3 * i + j + 1) + "个格子:绿")
- cv2.waitKey(0)
- cv2.destroyAllWindows()
最终效果:
开源!opencv魔方机器人视觉教程
每一个格子都能准确识别出来,我们的视觉部分就差不多完成了,剩下的就是电控+机械部分啦~
♬..♩~ ♫. ♪..
因为篇幅太长了,很多细节我没有细说,有空我会单独把透视变化和HSV阈值的确定和大家分享的,还有需要源文件的朋友可以在公众号【松鼠小铺子】回复“颜色识别”就可以获取啦~
来咯来咯~
有帮助的话点个赞吧~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。