• 【千律】OpenCV基础:Hough圆检测


    环境:Python3.8 和 OpenCV

    内容:Hough圆检测

    将直角坐标系中的一个圆映射为新坐标系中的一个点,对于原直角坐标系中的每一个圆,可以对应(a, b, r) 这样一个点,这个点即为新三维中的点。

    标准法实现步骤:
    1.获取原图像的边缘检测图像;

    2.设置最小半径、最大半径和半径分辨率等超参数;

    3.根据转化后空间的圆心分辨率等信息,设置计数器N(a, b, r);

    4.对边缘检测图像的每个白色边缘像素点,其坐标记为(x, y),对于指定的半径r,根据公式 (a-x)^2 + (b-y)^2 = r^2 得到点(a, b)形成的轨迹圆,将轨迹圆经过的计数器N(a, b, r) 加1;

    5.如果某个计数器的点数超过设定阈值,则认为存在对应的圆,取出(a, b, r),求出对应的圆方程。

    梯度法实现步骤:

    1.获取原图像的边缘检测图像;

    2.设置最小半径、最大半径和半径分辨率等超参数;

    3.计算原图像各点x和y方向的梯度;

    4.设置计数器N(a, b);

    5.对边缘图像中任意一点p,其梯度为(px, py),由该点p和其梯度决定一条直线,将直线经过的计数器N(a, b)加1。

    6.如果某计数器点数超过阈值,则认为存在圆,取出的(a, b)记为圆心,通过不断变换半径,若与边缘图的交集点数超过设定阈值,则认为存在圆,从而获得圆方程。

    实现方式1(梯度法):

    1. import cv2 as cv
    2. import numpy as np
    3. import matplotlib.pyplot as plt
    4. # 封装图片显示函数
    5. def image_show(image):
    6. if image.ndim == 2:
    7. plt.imshow(image, cmap='gray')
    8. else:
    9. image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
    10. plt.imshow(image)
    11. plt.show()
    12. # Hough圆检测
    13. if __name__ == '__main__':
    14. # 读取灰度图
    15. img_desk = cv.imread('desk.png')
    16. # 转换为灰度图
    17. img_gray = cv.cvtColor(img_desk, cv.COLOR_BGR2GRAY)
    18. # 参数设置
    19. img_dp = 2 # 图像分辨率与累加器分辨率之比,建议范围为1-2
    20. MinDist = 50 # 两个不同圆圆心之间的距离
    21. Param1 = 400 # Canny边缘阈值的上限,其下限为上限的1/2
    22. Param2 = 80 # cv.HOUGH_GRADIENT 方法的累加器阈值,阈值越小,检测的圆越多
    23. # cv.HOUGH_GRADIENT_ALT 方法中,该参数用于衡量圆的完美度,范围为0-1
    24. MinRadius = 10 # 最小圆半径
    25. MaxRadius = 100 # 最大圆半径
    26. # 霍夫圆检测
    27. circles = cv.HoughCircles(img_gray, cv.HOUGH_GRADIENT, dp=img_dp, minDist=MinDist,
    28. param1=Param1, param2=Param2, minRadius=MinRadius, maxRadius=MaxRadius)
    29. # 绘制结果
    30. for (x, y, r) in circles.squeeze():
    31. cv.circle(img_desk, (int(x), int(y)), int(r), (0, 0, 255), 2)
    32. image_show(img_desk)

    实现方式2(标准法): 

    1. import cv2 as cv
    2. import numpy as np
    3. import matplotlib.pyplot as plt
    4. # 封装图片显示函数
    5. def image_show(image):
    6. if image.ndim == 2:
    7. plt.imshow(image, cmap='gray')
    8. else:
    9. image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
    10. plt.imshow(image)
    11. plt.show()
    12. if __name__ == '__main__':
    13. # 读取灰度图
    14. img_desk = cv.imread('desk.png')
    15. # 转换为灰度图
    16. img_gray = cv.cvtColor(img_desk, cv.COLOR_BGR2GRAY)
    17. # 边缘检测
    18. img_canny = cv.Canny(img_gray, 100, 400)
    19. # 参数设置
    20. img_dr = 1 # 半径分辨率
    21. MinRadius = 10 # 最小圆半径
    22. MaxRadius = 100 # 最大圆半径
    23. thresh = 80 # 累加的阈值
    24. # 初始化计数器
    25. height, width = img_gray.shape
    26. radius = int((MaxRadius - MinRadius) / img_dr) + 1
    27. N = np.zeros((width, height, radius))
    28. # 获取边缘像素位置
    29. edge_y, edge_x = np.where(img_canny == 255)
    30. # 循环绘制不同圆
    31. for (x, y) in zip(edge_x, edge_y):
    32. for r in range(MinRadius, MaxRadius, img_dr):
    33. # 根据个点绘制不同圆
    34. circle = np.zeros((height, width), np.uint8)
    35. cv.circle(circle, (x, y), r, 255, 1)
    36. # 获取不同圆的轨迹位置
    37. bsi, asi = np.where(circle == 255)
    38. # 将轨迹经过位置计数器累加
    39. N[asi, bsi, r - MinRadius] += 1
    40. # 显示累加图
    41. plt.imshow(N[:, :, 50], cmap='jet')
    42. plt.show()
    43. # 获取圆的位置和半径
    44. xs, ys, rs = np.where(N > thresh)
    45. # 绘制图上检测的圆
    46. for (x, y, r) in zip(xs, ys, rs):
    47. cv.circle(img_desk, (x, y), r, (0, 0, 255), 2)
    48. # 显示结果
    49. image_show(img_desk)

    实现方式3(梯度法):

    1. import cv2 as cv
    2. import numpy as np
    3. import matplotlib.pyplot as plt
    4. # 封装图片显示函数
    5. def image_show(image):
    6. if image.ndim == 2:
    7. plt.imshow(image, cmap='gray')
    8. else:
    9. image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
    10. plt.imshow(image)
    11. plt.show()
    12. if __name__ == '__main__':
    13. # 读取灰度图
    14. img_desk = cv.imread('desk.png')
    15. # 转换为灰度图
    16. img_gray = cv.cvtColor(img_desk, cv.COLOR_BGR2GRAY)
    17. # 边缘检测
    18. img_canny = cv.Canny(img_gray, 100, 400)
    19. # 参数设置
    20. img_dr = 1 # 半径分辨率
    21. MinRadius = 10 # 最小圆半径
    22. MaxRadius = 100 # 最大圆半径
    23. thresh1 = 40 # 累加的阈值
    24. thresh2 = 90 # 交集的阈值
    25. # 设置直线长度
    26. height, width = img_canny.shape
    27. lmax = height + width
    28. # 计算梯度
    29. Ix = cv.Sobel(img_gray, cv.CV_64F, 1, 0)
    30. Iy = cv.Sobel(img_gray, cv.CV_64F, 0, 1)
    31. Ix = Ix / (np.sqrt(Ix**2 + Iy**2) + 1e-5)
    32. Iy = Iy / (np.sqrt(Ix ** 2 + Iy ** 2) + 1e-5)
    33. # 设置计数器
    34. N = np.zeros((height, width), np.int32)
    35. # 获取边缘像素位置
    36. edge_y, edge_x = np.where(img_canny == 255)
    37. # 循环绘制不同直线
    38. for (x, y) in zip(edge_x, edge_y):
    39. # 获取不同点梯度
    40. grad_x = Ix[y, x]
    41. grad_y = Iy[y, x]
    42. # 根据点和梯度绘制直线
    43. line_sx = int(x + grad_x * lmax)
    44. line_sy = int(y + grad_y * lmax)
    45. line_ex = int(x - grad_x * lmax)
    46. line_ey = int(y - grad_y * lmax)
    47. line = np.zeros((height, width), np.uint8)
    48. cv.line(line, (line_sx, line_sy), (line_ex, line_ey), 255, 1)
    49. # 获取不同圆的轨迹位置
    50. bsi, asi = np.where(line == 255)
    51. # 将轨迹经过位置计数器累加
    52. N[bsi, asi] += 1
    53. # 显示累加图
    54. plt.imshow(N, cmap='jet')
    55. plt.show()
    56. # 获取圆的位置和半径
    57. bsi, asi = np.where(N > thresh1)
    58. # 根据已知的圆心绘制不同半径的圆
    59. cir_abr = []
    60. for (a, b) in zip(asi, bsi):
    61. for r in range(MinRadius, MaxRadius, img_dr):
    62. # 绘制不同半径的圆
    63. circles = np.zeros((height, width), np.uint8)
    64. cv.circle(circles, (a, b), r, 255, 1)
    65. # 获取绘制圆和边缘图的交集
    66. intersection = cv.bitwise_and(circles, img_canny)
    67. # 累计交集的点数
    68. num_sum = (intersection == 255).sum()
    69. # 获取超过阈值的圆心和半径
    70. if num_sum > thresh2:
    71. cir_abr.append((a, b, r))
    72. # 绘制检测的圆
    73. for (a, b, r) in cir_abr:
    74. cv.circle(img_desk, (a, b), r, (0, 0, 255), 2)
    75. # 显示结果
    76. image_show(img_desk)

  • 相关阅读:
    [图论]哈尔滨工业大学(哈工大 HIT)学习笔记40-53
    网络安全(黑客)自学
    深入理解Java中的引用、复制、克隆和拷贝
    插入排序之希尔排序——【数据结构】
    【入门-06】系统定时器
    uniapp开发短视频系统仿哔哩哔哩
    文件重命名技巧,给每个文件名称统一输入文字
    MySQL入门指南4(查询进阶,外连接)
    电商项目高级篇-01 elasticsearch
    rabbitMq路由键介绍
  • 原文地址:https://blog.csdn.net/weixin_42704093/article/details/125396756