3. 边缘检测与矩形工件抓取
任务目标
本案例实现从单目相机图像中检测矩形工件(如手机中框、PCB板),输出其四个角点在图像坐标系中的像素坐标,供机械臂抓取定位使用。核心算法为Canny边缘检测 + 霍夫直线检测 + 直线交点计算。
环境依赖
Python 3.8+,OpenCV 4.5+,NumPy。若需实际相机采集,另需OpenCV相机驱动(如USB相机),但本案例提供静态图像作为输入,保证可独立复现。
关键完整代码
import cv2
import numpy as np
def detect_rectangle_corners(image_path):
"""
检测图像中最大矩形工件的四个角点
返回: corners (4x2 float array), 按左上、右上、右下、左下排序
"""
# 读取图像
img = cv2.imread(image_path)
if img is None:
raise ValueError(f"无法读取图像: {image_path}")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 1. 高斯模糊降噪
blurred = cv2.GaussianBlur(gray, (5, 5), 1.5)
# 2. Canny边缘检测
edges = cv2.Canny(blurred, 50, 150)
# 3. 霍夫直线检测
lines = cv2.HoughLinesP(edges, 1, np.pi/180,
threshold=80,
minLineLength=60,
maxLineGap=10)
if lines is None:
return None
# 4. 筛选直线:按角度聚类为水平/垂直两组
h_lines, v_lines = [], []
for line in lines:
x1, y1, x2, y2 = line[0]
angle = np.degrees(np.arctan2(y2-y1, x2-x1))
if abs(angle) < 20 or abs(abs(angle)-180) < 20:
h_lines.append((x1, y1, x2, y2))
elif abs(abs(angle)-90) < 20:
v_lines.append((x1, y1, x2, y2))
if len(h_lines) < 2 or len(v_lines) < 2:
return None
# 5. 对每组直线按截距排序,取最外侧两条
def line_intercept(line):
x1, y1, x2, y2 = line
if abs(x2-x1) < 1e-6: # 垂直线
return x1
return y1 - (y2-y1)/(x2-x1) * x1
h_lines.sort(key=line_intercept)
v_lines.sort(key=line_intercept)
top, bottom = h_lines[0], h_lines[-1]
left, right = v_lines[0], v_lines[-1]
# 6. 计算直线交点
def line_intersection(l1, l2):
x1, y1, x2, y2 = l1
x3, y3, x4, y4 = l2
denom = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4)
if abs(denom) < 1e-6:
return None
px = ((x1*y2-y1*x2)*(x3-x4) - (x1-x2)*(x3*y4-y3*x4)) / denom
py = ((x1*y2-y1*x2)*(y3-y4) - (y1-y2)*(x3*y4-y3*x4)) / denom
return (int(px), int(py))
# 左上: top∩left, 右上: top∩right, 右下: bottom∩right, 左下: bottom∩left
corners = [
line_intersection(top, left),
line_intersection(top, right),
line_intersection(bottom, right),
line_intersection(bottom, left)
]
if any(c is None for c in corners):
return None
return np.array(corners, dtype=np.float32)
# 最小调用入口
if __name__ == "__main__":
# 生成测试图像:黑色背景上的白色矩形
test_img = np.zeros((400, 600), dtype=np.uint8)
cv2.rectangle(test_img, (150, 100), (450, 300), 255, -1)
cv2.imwrite("test_rect.png", test_img)
corners = detect_rectangle_corners("test_rect.png")
if corners is not None:
print("检测到的角点坐标:")
for i, pt in enumerate(corners):
print(f" 角点{i+1}: ({pt[0]}, {pt[1]})")
# 预期输出(由代码逻辑直接推出):
# 角点1: (150, 100) 角点2: (450, 100)
# 角点3: (450, 300) 角点4: (150, 300)
else:
print("未检测到矩形")
运行与验证
运行方式:将上述代码保存为rect_detect.py,在终端执行:
python rect_detect.py
验证方法:程序会生成一张600×400像素的测试图,其中白色矩形左上角为(150,100)、右下角为(450,300)。运行后观察控制台输出的四个角点坐标:
- 若输出与预期坐标一致(允许±1像素误差),说明检测流程正确;
- 若输出
None或坐标偏差过大,请检查OpenCV版本及Canny阈值参数。
实际工件适配:将image_path替换为实际工件图像,需保证工件与背景对比明显、光照均匀。若检测失败,可依次调整Canny阈值(50,150)、霍夫threshold(80)和minLineLength(60)。
常见问题
- 检测到多余直线:工件表面纹理或反光会产生干扰线,可增大Canny高阈值或霍夫
threshold值过滤短线段。 - 角点排序错乱:本算法按截距取最外侧直线,适用于矩形近似水平/垂直放置的场景。若工件旋转角度较大,需先计算直线角度并旋转校正。
- 抓取坐标转换:本案例输出像素坐标,实际抓取需通过相机标定矩阵转换为机械臂基坐标系下的三维坐标。