实验六 宽度测量 一 题目描述利用Python中的opencv库编写程序对下图原件进行宽度测量。二 实现过程1、编写代码如下importcv2#导入opencv库importnumpyfromPILimportImage,ImageDraw,ImageFont#给图片添加中文文本函数defImgAddText(img,text,left,top,textColor(0,255,0),textSize20):if(isinstance(img,numpy.ndarray)):imgImage.fromarray(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))#创建一个能够在给定图像上绘图的对象drawImageDraw.Draw(img)# 设置字体的格式fontStyleImageFont.truetype(font/simsun.ttc,textSize,encodingutf-8)# 绘制文本draw.text((left,top),text,textColor,fontfontStyle)# 转换回OpenCV格式returncv2.cvtColor(numpy.asarray(img),cv2.COLOR_RGB2BGR)if__name____main__:imagecv2.imread(E:/OpenCVpicture/gongjian1-2.bmp)#读取图片# 选择一个矩形区域rcv2.selectROI(image,image,False,False)# 选择ROIprint(r)cv2.rectangle(image,(r[0],r[1]),(r[0]r[2],r[1]r[3]),(0,0,255),1)#复制原图中感兴趣的区域rectimage[r[1]:(r[1]r[3]1),(r[0]1):(r[0]r[2])]#第一个参数表示h的范围第二个参数表示w的范围cv2.imshow(rect,rect)#对所选区域进行阈值分割rect_1cv2.cvtColor(rect,cv2.COLOR_BGR2GRAY)#彩色图转为二值图cv2.imshow(rect_1,rect_1)ret,rect_2cv2.threshold(rect_1,70,255,cv2.THRESH_BINARY)#阈值分割cv2.imshow(rect_2,rect_2)#对所选区域进行图像反色rect_3cv2.bitwise_not(rect_2)#cv2.imshow(rect_3, rect_3)height,widthrect_3.shape[:2]# 返回图片的高和宽print(height,width)rect_5rect_3.copy()#将最外面的干扰边框通过反色除去foriin(0,-1):forjinrange(width):rect_5[i,j](255-rect_3[i,j])cv2.imshow(rect_5,rect_5)# 对所选区域进行边缘提取rect_41cv2.Canny(rect_5,1,255)#边缘提取rect_4cv2.bitwise_not(rect_41)#图像反色cv2.imshow(rect_4,rect_4)# 对所选区域进行宽度测量contours,hierarchycv2.findContours(rect_41,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)# 提取轮廓tempnumpy.ones(rect_5.shape,numpy.uint8)*255#创建白色幕布cv2.drawContours(temp,contours,-1,(0,255,0),2)cv2.imshow(temp,temp)image2image1cv2.imread(E:/OpenCVpicture/gongjian1-2.bmp)#读取图片#将8位灰度图转换为24位彩色图rect_51cv2.cvtColor(rect_4,cv2.COLOR_GRAY2BGR)image1[r[1]:(r[1]r[3]1),(r[0]1):(r[0]r[2])]rect_51#在图上显示选取的区域cv2.imshow(image1,image1)#x,y,w,h cv2.boundingRect(contours[-1])#计算轮廓的垂直边界最小矩形# cv2.line(rect_51, (x, y), (x, y h), color, 2, 4) # 左边界# cv2.line(rect_51, (x w-1, y), (x w-1, y h), color, 2, 4) # 右边界boxcv2.minAreaRect(contours[-1])#得到最小外接矩形的中心(x,y), (宽,高), 旋转角度box1cv2.boxPoints(box)# 获取最小外接矩形的4个顶点坐标box1numpy.int0(box1)print(box)box2sorted(box1,keylambday:y[0],reverseFalse)#按照x坐标从小到大给坐标排序,y坐标也从小到大排序#print(box1[0],box1[1],box1[2],box1[3])print(box2[0],box2[1],box2[2],box2[3])color(0,0,255)cv2.line(rect_51,tuple(box2[0]),tuple(box2[1]),color,2,4)#左边界cv2.line(rect_51,tuple(box2[2]),tuple(box2[3]),color,2,4)#右边界image2[r[1]:(r[1]r[3]1),(r[0]1):(r[0]r[2])]rect_51#在图上显示选取的区域cv2.imshow(image2,image2)text宽{}.format(min(box[1][0],box[1][1]))cv2.imshow(image21,ImgAddText(image2,text,5,5,(0,255,0),20))# 窗口显示时间单位毫秒cv2.waitKey(0)# 删除建立的全部窗口释放资源cv2.destroyAllWindows()三 运行结果效果四 问题及解决方法1、框选矩形区域时使用的不是现有的窗口而是出现一个新的窗口。解决方法将cv2.selectROI()函数中第一个参数设为原图窗口的名字而不是用新的名字2、选择好ROI区域后不知道怎样确定选中区域解决方法按enter键或空格键3、横纵坐标的位置反了。解决方法将rectimage[int(r[0]):int(r[0]r[2]),int(r[1]):int(r[1]r[3])]改为rectimage[int(r[1]):int(r[1]r[3]),int(r[0]):int(r[0]r[2])]4、数据维度不统一解决方法利用cv2.cvtColor(image1,cv2.COLOR_BGR2GRAY)将24位彩色图像image1转换为8位灰度图有不同深度的灰色和黑白两色。因为cv2.imread()接口读图像读进来直接是BGR格式数据24位彩色图有255 * 255 * 255种颜色。而截取的区域经过二值化处理已经变为了二值图。5、输入或者输出参数数量不一致导致解决方法使用两个变量。cv2.findContours()函数的返回值opencv2返回两个值contourshierarchy。注:opencv3会返回三个值,分别是img, countours, hierarchy6、当选择的区域的高度不同时可能会出现画红线的地方不是左右边界处。第一次做的解决方法可以对轮廓进行了排序。去最后顺序的轮廓但是局限性仍然还有7、左右有红框使用赋值语句复制的图片解决问题重新读取一张原图在该图上重绘ROI区域不要使用赋值语句。8、边缘提取没有上下界边缘解决方法在用ROI选择区域前先用rectangle()函数框出要选择的区域。9、错误同6相同改善后解决方法将x, y, w, h cv2.boundingRect(contours[0])中的读取countours[0]改成读取contours[-1]因为加了边界后无论怎样截外框都是最后一个。9、cv2.cv.Boxpoint函数出错解决方法解决方法把cv2.cv.BoxPoints()函数改为cv2.boxPoints()。因为版本语法不兼容opencv3已经没有了cv这是适用于opencv2的。10、参数非元组解决方法直接转成tuple的格式tuple(参数)五 实验总结感觉这个实验比以前的实验都要难做一些花了一天半的时间第一次编写的代码有许多尽如人意的地方比如要区域的选择不能是将整个器件都框住区域也不能包含头或尾部只能框器件的局部部分否则图中画的线段就不是左右两侧了再就是求得宽度只能是整数边缘提取后的边缘不是闭合的。在做这个实验的时候有一些步骤不知道要用什么函数所以有借鉴别人的博客参考。这个实验有好多地方是对函数的输入参数的如何正确选择以及对输出参数的理解尤其是提取轮轮廓findContours()函数的输出参数轮廓的层次的理解这里差不多花了一下午还是有些云里雾里的。第一次便于提取实验时就对canny()函数和findContours()函数的关系弄不太清楚通过这次实验有了一定的了解findContours()函数在canny()边缘检测完后用来对检测到的边缘进行提取通常和绘制轮廓函数drawContours()一起使用。与此同时对前面一些实验中的函数也有了进一步的了解。我检测边缘后的输出图片没有上下边一开始以为是Canny()提取边缘那里有问题花了很长时间看博客始终没有找出提取边缘函数那里出了什么错。后来受到一篇博客的启发可以在要截取的区域加边框然后在提取边缘前通过反色把多余的边框处理掉于是又通过这个方法重新做了一个完善版区域选择局限的问题得到了解决不过宽度的测量上不是很准确代码还是存在一定的缺陷如果有机会希望以后能完善的更好。整个实验有借鉴别人的博客启发思路。1、ROI图像的ROI(region of interest)是指图像中感兴趣区域、可以在OpenCV中图像设置图像ROI区域实现只对ROI区域操作。selectROI(windowName, img, showCrosshairNone, fromCenterNone):windowName选择的区域被显示在的窗口的名字img要在什么图片上选择ROIshowCrosshair是否在矩形框里画十字线.fromCenter是否是从矩形框的中心开始画返回的是一个元组[min_x,min_y,w,h]min_x矩形框中最小的x值min_y矩形框中最小的y值w矩形框的宽h矩形框的高2、findContours()函数cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])第一个参数是寻找轮廓的图像第二个参数表示轮廓的检索模式有四种cv2.RETR_EXTERNAL表示只检测外轮廓 。cv2.RETR_LIST检测的轮廓不建立等级关系。cv2.RETR_CCOMP建立两个等级的轮廓上面的一层为外边界里面的一层为内孔的边界信息。如果内孔内还有一个连通物体这个物体的边界也在顶层。cv2.RETR_TREE建立一个等级树结构的轮廓。第三个参数method为轮廓的逼近方法cv2.CHAIN_APPROX_NONE存储所有的轮廓点相邻的两个点的像素位置差不超过1即maxabsx1-x2absy2-y11。cv2.CHAIN_APPROX_SIMPLE压缩水平方向垂直方向对角线方向的元素只保留该方向的终点坐标例如一个矩形轮廓只需4个点来保存轮廓信息。cv2.CHAIN_APPROX_TC89_L1和cv2.CHAIN_APPROX_TC89_KCOS都是使用teh-Chinl chain近似算法。