前言
同学要做拧魔方机器人,但是他对视觉识别方面的知识不太明白,于是我帮他完成了识别魔方色块边缘的功能,过程中学习了不少可以用于图像处理的函数。下面我会先详解每一个函数,然后再给出整合后的代码。
环境配置:我是在anaconda上配置的python3.8的环境,用Jupyter Notebook来编写运行代码。
函数详解
魔方图片:
先对目标图像进行预处理:
imgobj = cv2.imread('CV_img/test2.jpg')gray = cv2.cvtColor(imgobj, cv2.COLOR_BGR2GRAY)blurred = cv2.GaussianBlur(gray, (3, 3), 0)canny = cv2.Canny(blurred, 20, 40)kernel = np.ones((3,3), np.uint8)dilated = cv2.dilate(canny, kernel, iterations=2) #闭运算,先膨胀再腐蚀,可以填充孔洞(√)dilated=morphology.dilation(dilated,morphology.square(8))dilated=morphology.erosion(dilated,morphology.square(8))
cv2.imread先读取测试图片;
cv2.cvtColor(p1,p2) 是颜色空间转换函数,p1是需要转换的图片,p2是转换成何种格式。cv2.COLOR_BGR2GRAY 是将BGR格式转换成灰度图片;
cv2.GaussianBlur是高斯滤波,是平滑滤波的一种方法,而Canny算子是边缘检测的一种算法,通过这两个函数对图像进行滤波运算。
dilate()函数可以对输入图像用特定结构元素进行膨胀操作,该结构元素确定膨胀操作过程中的邻域的形状。先创造一个3*3的数组(也就是kernel),用于膨胀操作的结构元素,使得处理后的图片看起来更契合魔方的特征。iterations是被递归的次数。
dilation为膨胀,erosion为腐蚀,先膨胀再腐蚀是闭运算,可以填充孔洞,而先腐蚀再膨胀是开运算,可以消除小物体小斑块,效果由参数morphology.square(p1)中的p1决定,数值越大效果越明显。
然后是提取轮廓:
contours, hierarchy = cv2.findContours(dilated,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
findContours函数是本程序中最核心的函数,大家也可以自己去找其他的资料进行更全面的了解。该函数的作用是检测图片中物体的轮廓。
先从findContours函数原型看起:
findContours( InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point());
各个参数解析:
image,目标图像,一般来说灰度图效果比较好。
contours是一个向量的集合,其中的每一个元素对应着一条轮廓,每个元素保存了一组由连续的Point点构成的点的集合,有多少轮廓,向量contours就有多少元素。你就把它当做一个数组,数组中每一个元素就是一条轮廓,且这个元素包含了这条轮廓的所有信息。
hierarchy,也是一个向量,向量内每个元素保存了一个包含4个int整型的数组。向量hiararchy内的元素和轮廓向量contours内的元素是一一对应的,向量的容量相同。hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],分别表示第i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。
int型的mode,定义轮廓的检索模式:
取值一:cv2.RETR_EXTERNAL只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略。
取值二:cv2.RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓, 所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1.
取值三:cv2.RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层。
取值四:cv2.RETR_TREE, 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
int型的method,定义轮廓的近似方法:
取值一:cv2.CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内。
取值二:cv2.CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留。
取值三和四:cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法。
Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量。
我们这里mode取值cv2.RETR_TREE,method取值cv2.CHAIN_APPROX_SIMPLE.
然后处理contours中的数据以得到我们想要的结果。
candidates = []for i in range(0,len(contours)):area = cv2.contourArea(contours[i])#计算面积if 40000<area<50000:#魔方色块的面积candidates.append(contours[i])cv2.drawContours(imgobj, candidates, -1, (0, 0, 255), 3)cv2.namedWindow("enhanced",0);cv2.resizeWindow("enhanced", 640, 640);cv2.imshow("enhanced",imgobj)cv2.waitKey(0)
我的方案是通过计算每条轮廓所围图形的面积来确定哪些轮廓是我们所需要的。
candidates用来存放符合条件的轮廓。
面积值,我是把所有的轮廓的面积都算出来了,然后找到了相近的九个面积值来确定的。只要保持拍摄魔方的时候摄像头外设的镜头与魔方距离固定应该就可行。
append函数用于在列表末尾添加新的对象。
最终效果图:
只要得到这些轮廓下面就好办了,可以用过函数cv2.moments来查找到每条轮廓的中心点,在通过中心点的HSV值来确定颜色,具体流程我这里就不赘述了,大家自己写一下。
完整代码
我的代码是使用摄像头外设来实时实现识别魔方边缘的。
注意改一个适当的面积范围。
import cv2import numpy as npimport matplotlib.pyplot as pltimport skimagefrom skimage import drawfrom skimage import morphologyfrom skimage import datafrom copy import deepcopyfrom copy import deepcopyimport mathdef video_demo():capture = cv2.VideoCapture(1)#0为电脑内置摄像头,1为摄像头外设while(True):ret, frame = capture.read()#摄像头读取,ret为是否成功打开摄像头,true,false。 frame为视频的每一帧图像frame = cv2.flip(frame, 1)#摄像头是和人对立的,将图像左右调换回来正常显示。imgobj = frame#图像转化为灰度图并进行滤波处理gray = cv2.cvtColor(imgobj, cv2.COLOR_BGR2GRAY)blurred = cv2.GaussianBlur(gray, (3, 3), 0)canny = cv2.Canny(blurred, 20, 40)kernel = np.ones((3,3), np.uint8)dilated = cv2.dilate(canny, kernel, iterations=2)#开运算,先腐蚀再膨胀,可以消除小物体小斑块#闭运算,先膨胀再腐蚀,可以填充孔洞(√)dilated=morphology.dilation(dilated,morphology.square(8))dilated=morphology.erosion(dilated,morphology.square(8))contours, hierarchy = cv2.findContours(dilated,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) #获取图像边缘信息"""candidates = [] #图像边缘滤波for i in range(0,len(contours)):area = cv2.contourArea(contours[i])#先算面积if 40000<area<50000:#在此处修改面积值candidates.append(contours[i])"""cv2.drawContours(imgobj, contours, -1, (0, 0, 255), 3)cv2.imshow("video", imgobj)c = cv2.waitKey(50)if c == 27:#按ESC退出breakvideo_demo()cv2.destroyAllWindows()
如果觉得《Python-Opencv实现魔方边缘识别》对你有帮助,请点赞、收藏,并留下你的观点哦!