• STM32实现霍夫圆检测


    本文要讨论的问题来源于工程实际,摄像头去拍圆形标记点得到一张图像,已知标记圆的半径范围(rmin,rmax),需要识别出圆心坐标和半径。采用霍夫圆变换可以很好的实现这个功能,且具有广泛的适应性(就是指在大多数情况下都能识别出圆,成功率高)。基本思路是先对圆进行边缘检测,然后对于边缘检测图像进行霍夫圆检测。

    1 霍夫圆检测算法原理

    假设圆的坐标假设为:

    (x-x_{_{c}})^{2}+(y-y_{c})^{2}=r^{2}

    现在已知圆上的一系列点(xi,yi),则可知圆心(xc,yc)也位于以(xi,yi)为圆心,半径为r的圆上,以这一系列点为圆心画一系列半径为r的圆,则圆心(xc,yc)将位于这些圆的交点上。如下图所示:

     霍夫圆检测的思路就是,对于其中的每一个半径r,做如下处理:

    (1)建立一个投票数组SumArr[height][width],其中height为图像高度,width为图像宽度

    (2)遍历边缘图像,对其中的每一个边缘点(xi,yi),求出以(xi,yi)为圆心,以r为半径的圆周上的所有像素点,将这些像素点的投票数加1,用伪代码表示为:

    1. for(theta=0;theta<360;theta+=step)
    2. {
    3. angle = theta*PI/180;
    4. cx=xi-r*cos(angle);
    5. cy=xi-r*sin(angle);
    6. if(cx>=0 && cx<width && cy>=0 && cy<height)
    7. sumArr[cy][cx] ++;//坐标点(cx,cy)是潜在的圆心,将其投票数增1
    8. }

    (3)统计sumArr数组中的最大值,这个最大值对应的坐标即图中半径为r的圆所在的圆心。

    (4)遍历半径范围(rmin,rmax),对于其中每一个半径r,既然可以求出它所对应的投票数candidate,圆心(xc,yc),取其中投票数最大值对应的半径,即为识别出来的圆半径r,其对应的圆心即为识别出来的圆心。

    2 canny边沿检测算法

    边沿检测算法使用canny边缘检测算法,通过计算图像梯度,

     梯度向量大小

    由于开平方和根号计算较慢,可以用|gx|+|gy|来代替

    计算梯度用sobel算子,sobel算子有俩个:

     将图中的每个像素和sobel算子1和算子2做卷积,求出gx和gy,进而求出梯度值M(x,y),利用梯度值来进行滤波。代码实现为:

    1. def convolution(img,y,x,width,height,sobel):
    2. value = [0]*9
    3. if y-1>=0 and x-1>=0 and y-1<height and x-1<width:
    4. value[0] = img[y-1][x-1]
    5. if y-1>=0 and y-1<height:
    6. value[1] = img[y-1][x]
    7. if y-1>=0 and y-1<height and x+1<width:
    8. value[2] = img[y-1][x+1]
    9. if x-1>=0:
    10. value[3] = img[y][x-1]
    11. value[4] = img[y][x]
    12. if x+1<width:
    13. value[5] = img[y][x+1]
    14. if y+1<height and x-1>=0:
    15. value[6] = img[y+1][x-1]
    16. if y+1<height:
    17. value[7] = img[y+1][x]
    18. if y+1<height and x+1<width:
    19. value[8] = img[y+1][x+1]
    20. out = abs(value[0]*sobel[0]+value[1]*sobel[1]+value[2]*sobel[2]+\
    21. value[3]*sobel[3]+value[4]*sobel[4]+value[5]*sobel[5]+\
    22. value[6]*sobel[6]+value[7]*sobel[7]+value[8]*sobel[8])
    23. return int(out)
    24. def mycanny(img,minVal,maxVal):
    25. sobel1 = [-1,-2,-1,0,0,0,1,2,1]
    26. sobel2 = [-1,0,1,-2,0,2,-1,0,1]
    27. width = img.shape[1]
    28. height = img.shape[0]
    29. out = numpy.zeros(img.shape,dtype=numpy.uint8)
    30. for y in range(height):
    31. for x in range(width):
    32. gx = convolution(img,y,x,width,height,sobel1)
    33. gy = convolution(img,y,x,width,height,sobel2)
    34. temp = gx+gy
    35. if temp <0:
    36. temp = 0
    37. elif temp>255:
    38. temp = 255
    39. if temp<200:
    40. out[y][x] = img[y][x]
    41. else:
    42. out[y][x] = temp
    43. return out

    2 程序实现步骤

    (1) 对原图像进行灰度化处理

    (2) 对灰度图进行canny边缘检测

     (3) 进行圆霍夫检测

    1. def findCir_R(img,r):
    2. width = img.shape[1]
    3. height = img.shape[0]
    4. sumArr = [0]*(width*height)
    5. for y in range(height):
    6. for x in range(width):
    7. if img[y][x] != 255:
    8. continue
    9. for a in range(0,360,10):
    10. theta = a*math.pi/180
    11. cx = int(x- r*math.cos(theta))
    12. cy = int(y-r*math.sin(theta))
    13. if cx>0 and cx<width and cy>0 and cy<height:
    14. sumArr[cy*width+cx] = sumArr[cy*width+cx]+1
    15. maxVal = 0
    16. cirX = 0
    17. cirY = 0
    18. for y in range(height):
    19. for x in range(width):
    20. if sumArr[y*width+x] > maxVal:
    21. maxVal = sumArr[y*width+x]
    22. cirX = x
    23. cirY = y
    24. return maxVal,cirX,cirY
    25. def findCir(img,rmin,rmax):
    26. maxVal = 0
    27. cirX = 0
    28. cirY = 0
    29. radius = 0
    30. for r in range(rmin,rmax+1,1):
    31. value,cx,cy = findCir_R(img,r)
    32. if value > maxVal:
    33. maxVal = value
    34. cirX = cx
    35. cirY = cy
    36. radius = r
    37. return cirX,cirY,radius

    所有代码:

    1. import cv2
    2. import numpy
    3. import os
    4. import math
    5. def show_img(window,img):
    6. cv2.namedWindow(window,0)
    7. cv2.resizeWindow(window,int(img.shape[1]),int(img.shape[0]))
    8. cv2.imshow(window,img)
    9. def findCir_R(img,r):
    10. width = img.shape[1]
    11. height = img.shape[0]
    12. sumArr = [0]*(width*height)
    13. for y in range(height):
    14. for x in range(width):
    15. if img[y][x] != 255:
    16. continue
    17. for a in range(0,360,10):
    18. theta = a*math.pi/180
    19. cx = int(x- r*math.cos(theta))
    20. cy = int(y-r*math.sin(theta))
    21. if cx>0 and cx<width and cy>0 and cy<height:
    22. sumArr[cy*width+cx] = sumArr[cy*width+cx]+1
    23. maxVal = 0
    24. cirX = 0
    25. cirY = 0
    26. for y in range(height):
    27. for x in range(width):
    28. if sumArr[y*width+x] > maxVal:
    29. maxVal = sumArr[y*width+x]
    30. cirX = x
    31. cirY = y
    32. return maxVal,cirX,cirY
    33. def findCir(img,rmin,rmax):
    34. maxVal = 0
    35. cirX = 0
    36. cirY = 0
    37. radius = 0
    38. for r in range(rmin,rmax+1,1):
    39. value,cx,cy = findCir_R(img,r)
    40. if value > maxVal:
    41. maxVal = value
    42. cirX = cx
    43. cirY = cy
    44. radius = r
    45. return cirX,cirY,radius
    46. def convolution(img,y,x,width,height,sobel):
    47. value = [0]*9
    48. if y-1>=0 and x-1>=0 and y-1<height and x-1<width:
    49. value[0] = img[y-1][x-1]
    50. if y-1>=0 and y-1<height:
    51. value[1] = img[y-1][x]
    52. if y-1>=0 and y-1<height and x+1<width:
    53. value[2] = img[y-1][x+1]
    54. if x-1>=0:
    55. value[3] = img[y][x-1]
    56. value[4] = img[y][x]
    57. if x+1<width:
    58. value[5] = img[y][x+1]
    59. if y+1<height and x-1>=0:
    60. value[6] = img[y+1][x-1]
    61. if y+1<height:
    62. value[7] = img[y+1][x]
    63. if y+1<height and x+1<width:
    64. value[8] = img[y+1][x+1]
    65. out = abs(value[0]*sobel[0]+value[1]*sobel[1]+value[2]*sobel[2]+\
    66. value[3]*sobel[3]+value[4]*sobel[4]+value[5]*sobel[5]+\
    67. value[6]*sobel[6]+value[7]*sobel[7]+value[8]*sobel[8])
    68. return int(out)
    69. def mycanny(img,minVal,maxVal):
    70. sobel1 = [-1,-2,-1,0,0,0,1,2,1]
    71. sobel2 = [-1,0,1,-2,0,2,-1,0,1]
    72. width = img.shape[1]
    73. height = img.shape[0]
    74. out = numpy.zeros(img.shape,dtype=numpy.uint8)
    75. for y in range(height):
    76. for x in range(width):
    77. gx = convolution(img,y,x,width,height,sobel1)
    78. gy = convolution(img,y,x,width,height,sobel2)
    79. temp = gx+gy
    80. if temp <0:
    81. temp = 0
    82. elif temp>255:
    83. temp = 255
    84. if temp<200:
    85. out[y][x] = img[y][x]
    86. else:
    87. out[y][x] = temp
    88. return out
    89. img = cv2.imread("D:/pic/cir22.jpg")
    90. show_img("source",img)
    91. gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    92. show_img('gray',gray)
    93. #canny = cv2.Canny(img, 200, 400)
    94. canny = mycanny(gray, 20, 100)
    95. show_img('canny',canny)
    96. #cv2.waitKey(0)
    97. cx,cy,r = findCir(canny,15,30)
    98. print(cx,cy,r)
    99. pic = img.copy()
    100. cv2.circle(pic,(cx,cy),r,(0,0,255))
    101. show_img("result",pic)
    102. cv2.waitKey(0)

    程序运行效果:

    相比前面文章 《STM32识别圆——色块追踪法》,霍夫圆检测算法识别成功率更高。但是这个算法在进行canny边缘检测时需要额外开辟一块和原图一样大小空间的数组用来存储canny结果,在进行霍夫圆检测时需要开辟投票统计数组sumArr。相比色块追踪法需要消耗更多内存,还需要消耗更多的运算时间。色块追踪法的优势是消耗内存少,运算速度快,但是识别成功率低于霍夫法。

  • 相关阅读:
    模型对象CSS2DObject始终在画布的左上角(问题解决)
    解密Spring Cloud LoadBalancer:实现高效负载均衡的魔法密卷(二)
    scratch绘制正方形 电子学会图形化编程scratch等级考试二级真题和答案解析2022年6月
    Scala配置和Spark配置以及Scala一些函数的用法(附带词频统计实例)
    大家都在用的福昕阅读器 foxit 你还不知道吗? 祛除水印&PDF转换&全功能解锁…
    powerlevel10k 颜色和图标的自定义设置
    【C标准库】详解feof函数与EOF
    Linux中磁盘管理
    一个 nginx 通过不同域名映射多个前端项目
    网页自动跳转到其他页面,点击浏览器返回箭头,回不到原来页面的问题
  • 原文地址:https://blog.csdn.net/liuzhijun301/article/details/125527882