使用Circle Hough Transform检测圆形时遇到异常问题
霍夫圆变换统计硬币数量结果异常问题
我在使用霍夫圆变换统计二值图像中的圆形(硬币)数量时遇到问题,变换后的结果不符合预期。
原始二值图像:
变换后图像:
相关代码如下:
import numpy as np import cv2 from skimage import io, filters, measure from skimage.filters import threshold_otsu, threshold_local from scipy import ndimage import matplotlib.pyplot as plt import math def Countcoins(img): im2 = cv2.resize(img, (2000, 2000), interpolation = cv2.INTER_LINEAR) b,g,r = cv2.split(im2) r = cv2.GaussianBlur(r, (25,25), 0) g = cv2.GaussianBlur(g, (25,25), 0) b = cv2.GaussianBlur(b, (25,25), 0) r = cv2.medianBlur(r, 15) g = cv2.medianBlur(g, 15) b = cv2.medianBlur(b, 15) ret_red, coins_red = cv2.threshold(r, 0, 255, cv2.THRESH_OTSU) ret_green, coins_green = cv2.threshold(g, 0, 255, cv2.THRESH_OTSU) ret_blue, coins_blue = cv2.threshold(b, 0, 255, cv2.THRESH_OTSU) image_merge = cv2.merge([coins_red, coins_green, coins_blue]) im_gray = cv2.cvtColor(image_merge, cv2.COLOR_BGR2GRAY) im_gray = np.where(im_gray != im_gray[0,0], 255, 0) im_gray = np.asarray(im_gray, dtype="uint8")*255 im_gray = cv2.morphologyEx(im_gray, cv2.MORPH_CLOSE, np.ones((6, 6), np.uint8), iterations=5) plt.imshow(im_gray, cmap = 'grey') plt.colorbar(shrink=0.8) plt.show() im_gray = np.asarray(im_gray, dtype="uint8")*255 circles = cv2.HoughCircles(im_gray,cv2.HOUGH_GRADIENT,1,250,param1 = 30, param2 = 5,minRadius=0,maxRadius=0) circles = np.uint8(np.around(circles)) for i in circles[0,:]: # draw the outer circle cv2.circle(im2,(i[0],i[1]),i[2],(0,255,0),20) # draw the center of the circle cv2.circle(im2,(i[0],i[1]),2,(0,0,255),20) print('硬币数量: ' + str(len(circles[0,:]))) plt.imshow(im2) im = cv2.imread('coins2.jpg') Countcoins(im)
问题分析与修复方案
1. 二值图像重复放大导致数据损坏
代码中im_gray = np.asarray(im_gray, dtype="uint8")*255执行了两次:第一次在二值化后已经将图像转为0/255的二值格式,第二次重复乘255会导致uint8类型数据溢出(超过255的部分取模变为0),直接破坏了二值图像的有效性。
修复:删除第二次的重复放大代码,保留第一次即可。
2. 霍夫圆变换参数设置不合理
param2=5阈值过低:该参数是累加器阈值,值越低会检测到越多假阳性圆,导致结果中出现大量无效小圆圈。minDist=250过大:最小圆心距离设置超过了硬币实际间距,会漏掉相邻的硬币。minRadius=0, maxRadius=0:自动检测半径结合低阈值,会生成大量无意义的小半径圆。
修复:调整参数适配硬币特征:
circles = cv2.HoughCircles( im_gray, cv2.HOUGH_GRADIENT, dp=1, minDist=50, # 缩小最小圆心距离,适配硬币密度 param1=50, # 提高边缘检测阈值,减少噪声干扰 param2=30, # 提高累加器阈值,过滤假阳性 minRadius=50, # 根据硬币实际大小设置最小半径 maxRadius=200 # 设置最大半径 )
3. 预处理步骤冗余
对RGB三个通道分别做高斯模糊+中值模糊是冗余操作,直接对灰度图处理即可,减少计算量同时避免通道合并的干扰。
4. 颜色空间显示失真
OpenCV读取的图像是BGR格式,matplotlib默认用RGB显示,直接plt.imshow(im2)会导致颜色偏色,需要转换颜色空间。
优化后的完整代码
import numpy as np import cv2 import matplotlib.pyplot as plt def Countcoins(img): im2 = cv2.resize(img, (2000, 2000), interpolation=cv2.INTER_LINEAR) # 直接转灰度图处理,减少冗余步骤 im_gray = cv2.cvtColor(im2, cv2.COLOR_BGR2GRAY) im_gray = cv2.GaussianBlur(im_gray, (25,25), 0) im_gray = cv2.medianBlur(im_gray, 15) # Otsu阈值二值化,反转图像让硬币区域为白色 ret, im_binary = cv2.threshold(im_gray, 0, 255, cv2.THRESH_OTSU) im_binary = cv2.bitwise_not(im_binary) im_binary = cv2.morphologyEx(im_binary, cv2.MORPH_CLOSE, np.ones((6,6), np.uint8), iterations=5) plt.imshow(im_binary, cmap='grey') plt.colorbar(shrink=0.8) plt.show() # 霍夫圆变换 circles = cv2.HoughCircles( im_binary, cv2.HOUGH_GRADIENT, dp=1, minDist=50, param1=50, param2=30, minRadius=50, maxRadius=200 ) if circles is not None: circles = np.uint16(np.around(circles)) for i in circles[0,:]: cv2.circle(im2, (i[0],i[1]), i[2], (0,255,0), 10) cv2.circle(im2, (i[0],i[1]), 2, (0,0,255), 10) print('硬币数量: ' + str(len(circles[0,:]))) else: print('未检测到硬币') # 转换颜色空间适配matplotlib显示 plt.imshow(cv2.cvtColor(im2, cv2.COLOR_BGR2RGB)) plt.show() im = cv2.imread('coins2.jpg') Countcoins(im)
内容的提问来源于stack exchange,提问作者Guillermo
相关产品推荐
相关产品推荐

