2. 形状匹配的圆形零件定位
任务目标
本案例实现基于形状匹配的圆形零件定位:输入一张包含圆形零件的灰度图像,算法输出每个检测到圆形的中心坐标(x, y)与半径r,为机器人抓取提供目标位置。采用OpenCV的HoughCircles作为形状匹配核心,配合参数自适应策略应对不同光照与零件尺寸。
环境依赖
Python 3.8+,OpenCV 4.5+(含opencv-python与opencv-python-headless二选一),NumPy。无需额外数据集,使用本地单张图像即可验证。
完整实现代码
import cv2
import numpy as np
import sys
def detect_circles(image_path, min_radius=20, max_radius=200, param2=30):
"""
基于Hough变换的圆形零件定位
返回: [(cx, cy, r), ...],按置信度降序
"""
# 读取图像并转为灰度
img = cv2.imread(image_path)
if img is None:
raise FileNotFoundError(f"无法读取图像: {image_path}")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 预处理:高斯模糊降噪,保留边缘清晰度
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# 自适应Canny阈值(基于灰度中值)
median_val = np.median(blurred)
low_thresh = int(max(0, 0.66 * median_val))
high_thresh = int(min(255, 1.33 * median_val))
edges = cv2.Canny(blurred, low_thresh, high_thresh)
# Hough圆检测(形状匹配核心)
circles = cv2.HoughCircles(
edges,
cv2.HOUGH_GRADIENT,
dp=1.2, # 累加器分辨率与图像分辨率的反比
minDist=min_radius*2, # 圆心最小间距,防止重复检测
param1=high_thresh, # Canny高阈值
param2=param2, # 圆心累加器阈值,越小检测越多(含误检)
minRadius=min_radius,
maxRadius=max_radius
)
results = []
if circles is not None:
# 将结果转为整数坐标并排序(按累加器强度,即第3维)
circles = np.round(circles[0]).astype(int)
# 按置信度(第3列)降序排列
circles = circles[np.argsort(-circles[:, 2])]
for x, y, r in circles:
results.append((int(x), int(y), int(r)))
return results
def visualize_and_save(image_path, circles, output_path="annotated.jpg"):
"""在原图上绘制检测结果并保存"""
img = cv2.imread(image_path)
for (x, y, r) in circles:
cv2.circle(img, (x, y), r, (0, 255, 0), 2) # 外圆
cv2.circle(img, (x, y), 2, (0, 0, 255), 3) # 圆心
cv2.putText(img, f"({x},{y}) r={r}", (x-30, y-r-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
cv2.imwrite(output_path, img)
print(f"标注图像已保存: {output_path}")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("用法: python circle_detect.py <图像路径> [min_radius] [max_radius] [param2]")
sys.exit(1)
img_path = sys.argv[1]
# 可选参数:最小半径、最大半径、累加器阈值
min_r = int(sys.argv[2]) if len(sys.argv) > 2 else 20
max_r = int(sys.argv[3]) if len(sys.argv) > 3 else 200
p2 = int(sys.argv[4]) if len(sys.argv) > 4 else 30
detections = detect_circles(img_path, min_r, max_r, p2)
if not detections:
print("未检测到圆形零件")
else:
print(f"检测到 {len(detections)} 个圆形零件:")
for i, (cx, cy, r) in enumerate(detections):
print(f" 零件{i+1}: 中心=({cx}, {cy}), 半径={r}px")
visualize_and_save(img_path, detections)
运行与验证
运行方式:
python circle_detect.py parts.jpg 20 200 30
其中parts.jpg为输入图像,后三个参数依次为最小半径、最大半径、Hough累加器阈值(默认值已给出,可按需调整)。
验证方法:
- 程序输出每个检测圆的中心坐标与半径,并在原图上绘制绿色外圆与红色圆心,保存为
annotated.jpg。 - 打开标注图像,目视检查绿色圆是否准确贴合零件边缘,圆心是否位于零件几何中心。
- 判定通过标准:图像中每个可见圆形零件均被检出,且无明显的非圆形误检(如方形边缘被误判为圆);输出坐标与图像中零件实际位置偏差不超过3像素。
参数调优建议:若漏检,降低param2(如从30降至20);若误检增多,提高param2。若零件半径超出默认范围,需通过命令行传入正确的min_radius与max_radius。
常见问题
- 检测不到圆:优先检查零件半径是否在
min_radius与max_radius范围内;其次降低param2至15~20重试。 - 误检严重:提高
param2至40~50,或增大minDist(当前为min_radius*2)以抑制邻近重复检测。 - 边缘断裂导致漏检:可适当降低Canny低阈值(代码中
low_thresh),或改用cv2.medianBlur替代高斯模糊以保留更锐利边缘。