当前位置:   article > 正文

利用Python OpenCV进行圆孔检测、计算半径、圆心到四边距离_python opencv 圆检测

python opencv 圆检测

先上最终检测视频:

OpenCV实时检测圆孔

本文重在实操,就不赘述背景意义等内容。

给定问题:假如有一个配件,此时需要检测配件上的圆孔坐标、半径、个数、圆孔到配件四边的距离。

由于没有实物,就在纸上画一个方框,表示配件边缘,花几个圆,表示圆孔:

首先导入要用到的库:

  1. import cv2
  2. import numpy as np

接着定义一些小工具:

  1. ##### 配置摄像头捕捉框的参数 #####
  2. # 分辨率
  3. framewidth = 640
  4. frameheight = 480
  5. cap = cv2.VideoCapture(0)
  6. cap.set(3,framewidth)
  7. cap.set(4,frameheight)
  8. def empty(a):
  9. pass
  10. # 使用Trackbar调参,效率提高一大截
  11. cv2.namedWindow('param')
  12. cv2.createTrackbar('thresh1','param',150,255,empty) # cv2.Canny参数
  13. cv2.createTrackbar('thresh2','param',255,255,empty) # cv2.Canny参数
  14. cv2.createTrackbar('area','param',20,50000,empty) # cv2.contourArea参数
  15. cv2.createTrackbar('param1','param',1,100,empty) # cv2.HoughCircles参数​
  16. cv2.createTrackbar('param2','param',1,100,empty) # cv2.HoughCircles参数

接着定义一个函数,使得能够在同一个窗口显示处理后的图片:

  1. def stackImages(scale,imgArray):
  2. rows = len(imgArray)
  3. cols = len(imgArray[0])
  4. rowsAvailable = isinstance(imgArray[0], list)
  5. width = imgArray[0][0].shape[1]
  6. height = imgArray[0][0].shape[0]
  7. if rowsAvailable:
  8. for x in range ( 0, rows):
  9. for y in range(0, cols):
  10. if imgArray[x][y].shape[:2] == imgArray[0][0].shape [:2]:
  11. imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, scale, scale)
  12. else:
  13. imgArray[x][y] = cv2.resize(imgArray[x][y], (imgArray[0][0].shape[1], imgArray[0][0].shape[0]), None, scale, scale)
  14. if len(imgArray[x][y].shape) == 2: imgArray[x][y]= cv2.cvtColor( imgArray[x][y], cv2.COLOR_GRAY2BGR)
  15. imageBlank = np.zeros((height, width, 3), np.uint8)
  16. hor = [imageBlank]*rows
  17. hor_con = [imageBlank]*rows
  18. for x in range(0, rows):
  19. hor[x] = np.hstack(imgArray[x])
  20. ver = np.vstack(hor)
  21. else:
  22. for x in range(0, rows):
  23. if imgArray[x].shape[:2] == imgArray[0].shape[:2]:
  24. imgArray[x] = cv2.resize(imgArray[x], (0, 0), None, scale, scale)
  25. else:
  26. imgArray[x] = cv2.resize(imgArray[x], (imgArray[0].shape[1], imgArray[0].shape[0]), None,scale, scale)
  27. if len(imgArray[x].shape) == 2: imgArray[x] = cv2.cvtColor(imgArray[x], cv2.COLOR_GRAY2BGR)
  28. hor= np.hstack(imgArray)
  29. ver = hor
  30. return ver

接着定义获取轮廓信息的函数:

这里要讲一下,if area > areamin: 和if w > 40 and w < 400: 这两个地方,由于cv2.findContours不仅会获取配件(矩形框)的轮廓,还会获取其它任何形状的轮廓,包括背景、圆孔的轮廓,从而使用cv2.boundingRect计算边界框的时候,会出现很多个边界框,导致后面计算圆孔到边框的距离时,会有问题,因为可能计算的是自己到自己的距离,例如:

  1. def getContours(img,imgcontour):
  2. # 第二个参数挺重要,由于仙子只想获取矩形框的轮廓,所以要用RETR_EXTERNAL
  3. _,contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
  4. # 存储矩形框的长宽和坐标
  5. xy = [] # 左上坐标
  6. wh = [] # 右下坐标为x+w,y+h
  7. for i, cnt in enumerate(contours):
  8. area = cv2.contourArea(cnt)
  9. areamin = cv2.getTrackbarPos('area','param')
  10. if area > areamin: # 通过面积来移除一些噪声造成的误检
  11. peri = cv2.arcLength(cnt,True) # True指轮廓是闭合的
  12. approx = cv2.approxPolyDP(cnt, 0.02*peri, True)
  13. x, y, w, h = cv2.boundingRect(approx) # 获取矩形框的参数
  14. if w > 40 and w < 400: # 只寻找宽在这个范围内的矩形框
  15. cv2.drawContours(imgcontour, cnt, -1, (255, 0, 255), 3) # 绘制轮廓
  16. cv2.rectangle(imgcontour, (x, y),(x+w, y+h), (0,255,0), 3) # 绘制边界框
  17. # cv2.putText用来在窗口显示信息
  18. cv2.putText(imgcontour, '(x1,y1):'+'('+str(x)+','+str(y)+')', (5, 30), cv2.FONT_HERSHEY_COMPLEX, 0.7,
  19. (0, 0, 0), 2)
  20. cv2.putText(imgcontour, '(x2,y2):'+'('+str(x+w)+','+str(y+h)+')', (5, 90), cv2.FONT_HERSHEY_COMPLEX, 0.7,
  21. (0, 0, 0), 2)
  22. cv2.putText(imgcontour, 'h:'+str(int(h)), (5,150), cv2.FONT_HERSHEY_COMPLEX, 0.7,
  23. (0,0,0), 2)
  24. cv2.putText(imgcontour, 'w:'+str(int(w)), (5,210), cv2.FONT_HERSHEY_COMPLEX, 0.7,
  25. (0, 0, 0), 2)
  26. # cv2.putText(imgcontour, 'area:' + str(int(area)), (100, 200), cv2.FONT_HERSHEY_COMPLEX, 0.7,
  27. # (0, 0, 0), 2)
  28. xy.append((x,y))
  29. wh.append((w,h))
  30. return xy, wh

接着,定义主函数:

这里要注意,OpenCV计算圆孔大小、距离等指标时,只是计算像素有多少,而不会计算真实的距离,从而需要根据圆孔的真实大小和对应的像素大小计算一个比例系数,例如,圆孔真实半径为3mm,对应图像上的像素为r,那么需要计算比例系数factor_c=r/3,从而在计算其它圆孔的真实尺寸时,只需用r/factor_c即可。

  1. def main():
  2. while True:
  3. # sucess, image = cap.read() # 要用摄像头时,用这个
  4. image = cv2.imread('D:/14.jpg') # 用本地图片检测
  5. imgContour = image.copy()
  6. # 图像预处理
  7. imgBlur = cv2.GaussianBlur(image, (5, 5), 1) # 高斯模糊
  8. imgGray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 转为灰度,减少剔除不必要的信息,从而进行轮廓检测
  9. # 获取最外面的轮廓
  10. thresh1 = cv2.getTrackbarPos('thresh1', 'param')
  11. thresh2 = cv2.getTrackbarPos('thresh2', 'param')
  12. imgCanny = cv2.Canny(imgGray, thresh1, thresh2) # 轮廓检测
  13. kernel = np.ones((3, 3))
  14. imgDil = cv2.dilate(imgCanny, kernel, iterations=1) # 膨胀,把边界变粗,有利于检测轮廓
  15. xy, wh = getContours(imgDil, imgContour)
  16. print('xy',xy)
  17. print('wh:',(wh))
  18. # 单独用霍夫圆检测圆圈
  19. param1= cv2.getTrackbarPos('param1', 'param')
  20. param2 = cv2.getTrackbarPos('param2', 'param')
  21. maxRadius = cv2.getTrackbarPos('maxRadius', 'param')
  22. # minDist = cv2.getTrackbarPos('minDist', 'param')
  23. circles = cv2.HoughCircles(imgDil, cv2.HOUGH_GRADIENT, 1, minDist=30, param1=param1, param2=param2, minRadius=0, maxRadius=100)
  24. detect_circles = np.uint16(np.around(circles))
  25. distances = []
  26. for (x,y,r) in detect_circles[0, :]:
  27. cv2.circle(imgContour, (x, y), r, (0, 255, 0), 3)
  28. # cv2.circle(imgContour, (x, y), 2, (0, 255, 255), 2)
  29. # area = 3.14159 * r * r
  30. if len(xy) == 0: # 实时检测时,要加上这句,不然只要没有检测到圆孔就会停止,此时可能只是还没有对齐摄像头
  31. continue
  32. print(x,'\n',y,'\n',r)
  33. # 获取圆心到矩形四边的距离,需要令最小为0,因为当圆孔边缘贴着矩形一边时,可能会因为误差导致检测到的距离为负数
  34. d_up = max(0, (y - xy[0][1]))
  35. d_down = max(0, (xy[0][1] + wh[0][1] - y))
  36. d_left = max(0, (x - xy[0][0]))
  37. d_right = max(0, (xy[0][0] + wh[0][0] - x))
  38. distances.append((d_up, d_down, d_left, d_right))
  39. factor_c = 18.666 # 比例因子
  40. cv2.putText(imgContour, 'r:' + str(round(int(r)/factor_c,2)), (x+20, y+80), cv2.FONT_HERSHEY_COMPLEX, 0.7,
  41. (0, 0, 0), 2)
  42. cv2.putText(imgContour, 'd:'+'('+str(round(d_up/factor_c,2))+','+str(round(d_down/factor_c,2))+','+
  43. str(round(d_left/factor_c,2))+','+str(round(d_right/factor_c,2))+')', (x+30, y+160),
  44. cv2.FONT_HERSHEY_COMPLEX, 0.7,(0, 0, 0), 2)
  45. print('nums=',len(distances))
  46. # 显示新图像
  47. imgStack = stackImages(0.8, [imgContour, imgDil])
  48. cv2.namedWindow('image', cv2.WINDOW_NORMAL)
  49. cv2.resizeWindow("image", 900, 600)
  50. cv2.imshow('image', imgStack)
  51. # 按任意键退出
  52. if cv2.waitKey(1) & 0xFF == ord('q'):
  53. break
  54. print('distances:\n',distances)
  55. # break
  56. # cv2.waitKey(0)
  57. # cv2.destroyAllWindows()
  58. if __name__ == '__main__':
  59. main()

最终效果1:可以看到误差比较小,效果还是挺好的。

注:真实圆孔大小半径为3mm,矩形框为30×40mm。

小结:调参挺繁琐,慢慢调吧。


如有新的想法,期待交流探讨

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/72425
推荐阅读
相关标签
  

闽ICP备14008679号