利用OpenCV测量工件尺寸
1.问题描述这是博主的作业分享利用OpenCV和基础图像处理方法测量一个工件的尺寸。依据题目内容描述此次作业的任务为使用课程所学的机器视觉方法对工业流水线上的精密金属零件进行尺寸自动测量以便计算其与标准尺寸的偏差。需要测量的参数包括图中工件的水平尺寸、垂直尺寸以及内径圆的直径。由于题目中给出了像素精度0.02mm/pixel所以此处无需依据相机的针孔成像模型建立物体三维坐标到图像二维像素坐标的映射直接测量图中工件的水平像素数、垂直像素数以及内径圆直径的像素数即可。2.算法设计2.1灰度图在此次任务中由于只需要测量工件的水平尺寸、垂直尺寸以及内径圆的直径所以色彩信息是不需要的所以先将参考图片转为灰度图。# 转为灰度图像并显示 gray_image cv.cvtColor(source_image,cv.COLOR_BGR2GRAY) cv.namedWindow(Gray Image,cv.WINDOW_AUTOSIZE) cv.imshow(Gray Image,gray_image) 2.2滤波由于参考图像中加入了椒盐噪声需要对图像进行滤波这里使用空间域的中值滤波核的尺寸为5*5暂时不用频域滤波算法。# 中值滤波去除噪声 denoise_image cv.medianBlur(gray_image,5) cv.namedWindow(MedianBlur Image,cv.WINDOW_AUTOSIZE) cv.imshow(MedianBlur Image,denoise_image)2.3图像增强这一部分是后面加的发现对于这种亮度不均匀的金属件不做增强直接二值化会留下很多细小的空洞后面形态学操作难以补回来补回来了边缘也模糊的厉害。所以在这里做一个Gamma校正。首先选择gamma为0.6将图像暗部区域拉亮输出像素与原像素映射关系如下所示。# Gamma校正 # 设置gamma值 小于1 将暗区拉亮 gamma 0.6 # 创建gamma查找表 tabel np.array([(((i / 255.0) ** gamma) * 255) for i in np.arange(0, 256)]).astype(uint8) # 应用查找表 enhance_image cv.LUT(denoise_image,tabel)2.4图像二值化由于中值滤波实际上是对于核区域内的像素进行排序选择中间值作为代表虽然在图像上看不太出但是实际上对于图像的边缘是有一个模糊的效果的。但是在此处不做考虑使用Otsu对图像进行自适应阈值分割。# 二值化图像 ret,binary_image cv.threshold(enhance_image,0,255,cv.THRESH_BINARY cv.THRESH_OTSU) cv.namedWindow(Binary Image,cv.WINDOW_AUTOSIZE) cv.imshow(Binary Image,binary_image)2.5形态学操作对图像进行二值化后可以看到图像中存在许多黑色空洞这对后续的算法是不利的所以需要进行形态学操作填补这些细小的空洞尤其是内径圆处的环形空洞这严重影响了后续检测内径圆直径。这里先用闭运算先膨胀后腐蚀填补细小空洞再用开运算先腐蚀后膨胀去除小噪声闭运算核选择8*8的椭圆形迭代次数为3尽可能填补空洞。# 形态学操作 # 闭运算 kernel_fill cv.getStructuringElement(cv.MORPH_ELLIPSE,(8,8)) process_image cv.morphologyEx(binary_image,cv.MORPH_CLOSE,kernel_fill,iterations3) # 开运算 kernel_open cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5)) process_image cv.morphologyEx(process_image,cv.MORPH_OPEN,kernel_open) cv.namedWindow(Process Image,cv.WINDOW_AUTOSIZE) cv.imshow(Process Image,process_image)2.6外形尺寸测量首先测量外部轮廓的宽和高这里使用API查找图像轮廓选择最大的轮廓作为外轮廓外接矩形得中心坐标、宽和高信息。当然不用API也可以比如从上往下遍历以及从左往右遍历当白色像素占据一定比例时开始计数小于一定比例时停止计数一样可以获得宽和高信息。再测量内径圆的直径这里可以沿用上一个API得到的图像轮廓选择第二大的轮廓作为内径圆的轮廓外接矩形获取中心坐标、宽和高信息。也可以选用霍夫圆检测这个算法是将所有可能的圆进行投票最终返回检测到的圆。此处选择沿用上一个API反转图像后选择第三大轮廓作为内径圆的轮廓霍夫圆检测不一定能返回值实际使用不稳定。第一张测量的水平尺寸12.24mm垂直尺寸12.24mm内径圆直径3.28mm第二张测量的水平尺寸12.32mm垂直尺寸12.32mm内径圆直径3.20mm第三张测量的水平尺寸11.94mm垂直尺寸11.94mm内径圆直径3.36mm3.完整代码source_image cv.imread(./source_images/t03-mixed.png)换成你自己的图像名我这里用的是相对路径如果不想出问题的话可以换成你图片的绝对路径。import cv2 as cv import numpy as np # 读取原始图像并显示 source_image cv.imread(./source_images/t03-mixed.png) cv.namedWindow(Source Image,cv.WINDOW_AUTOSIZE) cv.imshow(Source Image,source_image) # 转为灰度图像并显示 gray_image cv.cvtColor(source_image,cv.COLOR_BGR2GRAY) cv.namedWindow(Gray Image,cv.WINDOW_AUTOSIZE) cv.imshow(Gray Image,gray_image) # 中值滤波去除噪声 denoise_image cv.medianBlur(gray_image,5) cv.namedWindow(MedianBlur Image,cv.WINDOW_AUTOSIZE) cv.imshow(MedianBlur Image,denoise_image) # Gamma校正 # 设置gamma值 小于1 将暗区拉亮 gamma 0.6 # 创建gamma查找表 tabel np.array([(((i / 255.0) ** gamma) * 255) for i in np.arange(0, 256)]).astype(uint8) # 应用查找表 enhance_image cv.LUT(denoise_image,tabel) # 二值化图像 ret,binary_image cv.threshold(enhance_image,0,255,cv.THRESH_BINARY cv.THRESH_OTSU) cv.namedWindow(Binary Image,cv.WINDOW_AUTOSIZE) cv.imshow(Binary Image,binary_image) # 形态学操作 # 闭运算 kernel_fill cv.getStructuringElement(cv.MORPH_ELLIPSE,(8,8)) process_image cv.morphologyEx(binary_image,cv.MORPH_CLOSE,kernel_fill,iterations3) # 开运算 kernel_open cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5)) process_image cv.morphologyEx(process_image,cv.MORPH_OPEN,kernel_open) cv.namedWindow(Process Image,cv.WINDOW_AUTOSIZE) cv.imshow(Process Image,process_image) # 查找图像轮廓 out_contours,out_hierarchy cv.findContours(process_image,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE) # 工件外轮廓 out_contour max(out_contours,keycv.contourArea) # 轮廓外接矩形获取宽和高信息 x,y,w,h cv.boundingRect(out_contour) # 内径圆检测 binary_inv cv.bitwise_not(process_image) inner_contours, _ cv.findContours(binary_inv, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE) # 排序后取第三大轮廓 sorted_inner sorted(inner_contours, keycv.contourArea, reverseTrue) inner_contour sorted_inner[2] if len(sorted_inner) 2 else sorted_inner[0] (center_x, center_y), radius_px cv.minEnclosingCircle(inner_contour) center_x, center_y, radius_px int(center_x), int(center_y), int(radius_px) # 显示检测结果 # 给原图像加上外轮廓矩形框 output_image source_image.copy() cv.rectangle(output_image,(x,y),(xw,yh),(0,255,0),thickness3) # 给图像加上内径圆矩形框 cv.rectangle(output_image,(center_x-radius_px,center_y-radius_px),(center_xradius_px,center_yradius_px),(255,0,0),thickness3) # 给图像加上文本显示 # 像素精度0.02mm/pixel # 水平尺寸 width w * 0.02 # 垂直尺寸 height h * 0.02 # 内径圆直径 diameter radius_px * 2 * 0.02 font cv.FONT_HERSHEY_SIMPLEX font_scale 0.8 thickness 1 color (0, 255, 0) text1 fwidth: {width:.2f} mm cv.putText(output_image, text1, (0, 20),font, font_scale, color, thickness, cv.LINE_AA) # 第二行垂直尺寸 text2 fheight: {height:.2f} mm cv.putText(output_image, text2, (0,50), font, font_scale, color, thickness, cv.LINE_AA) # 第三行内径 text3 fdiameter: {diameter:.2f} mm cv.putText(output_image, text3, (0, 80),font, font_scale, color, thickness, cv.LINE_AA) cv.namedWindow(Output Image,cv.WINDOW_AUTOSIZE) cv.imshow(Output Image,output_image) cv.waitKey(0) cv.destroyAllWindows()