如何稳定精准测量圆角矩形旋转角度以适配工业PNP设备
解决方案:圆角矩形旋转角度高精度测量优化
问题回顾
我用diplib编写Python脚本,用于测量圆角矩形产品的中心与旋转角度,数据将驱动工业PNP设备完成面板精准放置。当前采用Feret.MinAng测量旋转角度存在偏差——该方法仅取对象两侧最远像素对,而非对边平均距离;即使对目标做形态学开运算,仍有约0.05度误差,同时担心光源波动会影响测量的稳定性与一致性,寻求优化方案。
原代码
import diplib as dip import numpy as np import matplotlib.pyplot as pp # 加载图像 img = dip.ImageRead('back6.png') img = img(1) gm = dip.Norm(dip.GradientMagnitude(img)) water = dip.Watershed(gm, connectivity=1, maxDepth=6, flags={'correct', 'labels'}) water = dip.SmallObjectsRemove(water, 8000) blocks = dip.MeasurementTool.Measure(water, img, features=['Mass']) blocks = dip.ObjectToMeasurement(water, blocks['Mass']) blocks[blocks < 31000000] = 0 result = dip.MeasurementTool.Measure(water, img, features=['Center']) print(result) board = (blocks > 3100000) board = dip.FillHoles(board) rect = dip.Opening(board, 35) dip.viewer.Show(board) dip.viewer.Show(rect) result = dip.MeasurementTool.Measure(dip.Label(board), img, features=['Center', 'Feret']) print(result) result = dip.MeasurementTool.Measure(dip.Label(rect), img, features=['Center', 'Feret']) print(result) target = dip.Overlay(img, rect-dip.BinaryErosion(rect, 1, 1)) dip.viewer.Show(target) dip.viewer.Show(dip.Overlay(img, board-dip.BinaryErosion(board, 1, 1))) circle = dip.Image(target.Sizes(), 1, 'SFLOAT') circle.Fill(0) dip.DrawBandlimitedBall(circle, diameter=10, origin=result[1]['Center']) circle /= dip.Maximum(circle) target *= 1 - circle target += circle * dip.Create0D([255,10,0]) dip.viewer.Show(target)
测试与效果
- 测试图片:![深色背景下的圆角矩形产品图]
- 轮廓追踪效果:![清晰显示产品轮廓与稳定中心标记的效果图]
- 误差情况:![形态学开运算后
Feret.MinAng仍有约0.05度误差的截图]
优化方案
1. 用图像矩替换Feret角计算主轴方向
Feret角依赖单点对,易受圆角、噪声干扰。改用**图像矩(Image Moments)**计算主轴方向,基于目标所有像素的分布趋势,鲁棒性更强:
# 替换原Feret测量逻辑,改用Orientation特征 result = dip.MeasurementTool.Measure(dip.Label(rect), img, features=['Center', 'Orientation']) # 将弧度转换为角度 angle_deg = dip.RadiansToDegrees(result[1]['Orientation']) print(f"主轴角度:{angle_deg}度")
矩计算的是目标整体分布的平均趋势,能有效抵消圆角和局部噪声带来的偏差,精度远高于Feret角。
2. 加入光源波动抗性的预处理流程
针对光照波动,添加灰度归一化+自适应阈值预处理,抵消光照不均和强度波动:
# 加载图像后加入预处理步骤 # 灰度归一化,统一图像亮度范围 img_normalized = dip.Normalize(img, lower=0, upper=255) # 自适应阈值分割,替代梯度+分水岭的复杂流程 thresholded = dip.AdaptiveThreshold(img_normalized, windowSize=15, offset=10) # 后续分割、形态学处理基于thresholded执行
自适应阈值能在局部区域动态调整阈值,抵消光照不均;灰度归一化则统一整体亮度,降低光源强度波动的影响。
3. 轮廓点最小二乘拟合矩形
若矩计算仍有细微误差,可提取目标轮廓后做最小二乘拟合,进一步平均圆角带来的偏差:
# 提取目标轮廓 contours = dip.FindContours(rect) # 整理轮廓点坐标 points = [] for contour in contours: points.extend(contour.T) points = np.array(points) # 用PCA做最小二乘拟合主轴 from sklearn.decomposition import PCA pca = PCA(n_components=2) pca.fit(points) # 转换为角度 angle_pca = np.arctan2(pca.components_[0,1], pca.components_[0,0]) angle_pca_deg = dip.RadiansToDegrees(angle_pca) print(f"PCA拟合角度:{angle_pca_deg}度")
通过拟合所有轮廓点,相比Feret角的单点对,精度和稳定性更优。
4. 多帧平均抵消随机噪声
若设备支持连续采集,对多帧测量结果做平均,抵消单次采集的随机噪声和光源波动:
# 示例:3帧平均逻辑 angles = [] for _ in range(3): # 替换为实时图像采集逻辑 img = dip.ImageRead('back_frame.png') # 执行预处理、分割、角度测量流程 # ... angles.append(angle_deg) avg_angle = np.mean(angles) print(f"多帧平均角度:{avg_angle}度")
内容的提问来源于stack exchange,提问作者HotCat
相关产品推荐
相关产品推荐

