本系统用于检测 Genesis 2000 CAM 软件截图中的青色(Cyan)DRC 异常标记,并输出用于设备干涉的操作框坐标。系统采用传统机器视觉技术(OpenCV),通过 HSV 色彩空间分割、形态学操作、轮廓分析等方法实现异常定位。
- Genesis CAM 阶段 DRC 检查自动化
- 焊盘异常定位(短路、开路、间距不足等)
- 为后续设备 API 提供精确的操作框坐标(
coor1/coor2)
背景:
原代码通过 get_target_pad_mask() 提取红色焊盘区域,并用 cv2.bitwise_and 将其与蓝色掩膜做硬相交,试图把"蓝色异常"限制在焊盘范围内。
# origin.py — 致命逻辑
blue_mask = cv2.bitwise_and(blue_mask, target_pad_mask)根本矛盾:
在 Genesis 的 2D 渲染中,青色 DRC 标记在视觉上直接覆盖并遮挡其下方的红色铜层。这意味着:
- 青色区域在图像中显示为青色像素
- 同一位置的红色铜层像素已被替换,不存在于图像中
target_pad_mask在青色标记所在处恰好出现黑洞(零值)
对两个掩膜做 AND 运算,其结果是将真正的检测目标抹除:
蓝色掩膜 (blue_mask): 焊盘掩膜 (target_pad_mask): AND 结果:
0 0 1 1 0 0 0 0 0 0 0 0 0 0 0
0 1 1 1 0 AND 0 0 0 0 0 = 0 0 0 0 0
0 0 1 1 0 0 0 0 0 0 0 0 0 0 0
↑ ↑ ↑
青色目标 青色遮挡形成的黑洞 目标被完全抹除
实际影响:正常情况下应当产生高置信度检测结果的图像,因为青色像素与焊盘掩膜的黑洞完全重合,导致所有有效候选轮廓被过滤,系统退化为"保底输出"或"默认区域"。
背景:
原代码在生成 2.5 倍扩展坐标时,将"非扩展轴"的两个端点设为同一个中心值:
# origin.py — 垂直方向时,X 轴两端点相同
p1_x = (min_x + max_x) // 2 # ← 中点
p1_y = max(0, min_y - expansion)
p2_x = (min_x + max_x) // 2 # ← 同一个中点
p2_y = min(h - 1, max_y + expansion)实际影响:
输出的矩形框 coor1(x, y1) → coor2(x, y2) 中 x 坐标完全相同,描述的是一条零宽度的线段而非具有面积的矩形。后续设备 API 或宏调用在接收到此类退化几何体时,轻则输出错误,重则直接崩溃。
期望输出 (具有面积的矩形): 实际输出 (零宽度线段):
coor1(280, 120) coor1(300, 120)
┌─────┐ |
│ │ → |
│ │ |
└─────┘ |
coor2(320, 480) coor2(300, 480)
背景:
原代码使用通用"蓝色"阈值,未针对 Genesis 特有的纯青色(Cyan)进行专项调优:
# origin.py — 通用蓝色阈值,未区分青色
lower_blue1 = np.array([100, 80, 80])
upper_blue1 = np.array([130, 255, 255])
lower_blue2 = np.array([90, 60, 60])
upper_blue2 = np.array([110, 255, 255])实际影响:
- Genesis 截图经过压缩后,青色 DRC 标记的饱和度(S)和亮度(V)会发生衰减,标准蓝色阈值无法覆盖衰减后的像素范围
- 同时,原代码基于残缺像素块的原生 Bounding Box 计算几何属性,当 DRC 标记被黑色间距"腰斩"后,碎片的重心和比例严重失真
背景:
原代码在引入方向判断前,永远只输出横向扩展结果。引入方向判断后,原版本已尝试修复此问题,但受制于缺陷 2,仍然输出退化线段。
原代码在多候选场景下,使用"选择 Y 坐标最大(最靠下)"的启发式规则,与焊盘结构完全脱耦,误选率高。
┌────────────────────────────────────────────────────────────┐
│ 软约束替代硬相交 + 外接圆重构 + 铜边界锚点定向 + 正交轴边界继承 + Cyan 调优 │
└────────────────────────────────────────────────────────────┘
│
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
Task 1: 废除 AND Task 2: 正交轴 Task 3: 外接圆
改用距离场软约束 边界继承机制 重构 + Cyan 阈值
graph TD
Input[输入图像] --> Interface[界面区域检测]
Input --> ROI[ROI 内容区提取]
Input --> Pad[圆形焊盘检测]
Pad --> PadMask[生成焊盘掩膜]
PadMask --> SoftPad[超大膨胀 / 软约束距离场]
Input --> HSV[HSV 提取 Cyan 青色]
HSV --> CyanMask[青色掩膜]
ROI --> CyanMask
Interface --> CyanMask
CyanMask --> Contours[轮廓分析]
SoftPad --> DistMap[焊盘距离场]
DistMap --> Affinity[计算空间亲和度]
Contours --> Affinity
Affinity --> Filter[过滤器: 面积 / 宽高比 / 界面 / 亲和度]
Filter --> Valid{有效候选?}
Valid -->|是| Rank[按亲和度排序]
Valid -->|否| Fallback[保底候选池]
Rank --> Selected[选定目标]
Fallback --> Selected
Selected --> Circle[cv2.minEnclosingCircle 外接圆重构]
Circle --> Direction[方向判断: 铜边界锚点 -> 焊盘圆心 -> 几何兜底]
Direction --> Box[正交轴边界继承生成操作框]
Box --> Output[JSON 输出]
移除原代码中对真实目标具有致命破坏性的 bitwise_and 操作:
# origin.py — 已废除
blue_mask = cv2.bitwise_and(blue_mask, target_pad_mask)青色掩膜现在独立提取,不再与焊盘掩膜做直接相交过滤:
# main.py — 新流程:青色掩膜仅受 ROI 和界面区约束
cyan_mask = build_hsv_mask(hsv, config.cyan_hsv_ranges)
cyan_mask = cv2.bitwise_and(cyan_mask, roi_mask)
cyan_mask = cv2.bitwise_and(cyan_mask, cv2.bitwise_not(interface_mask))焊盘掩膜改为参与空间距离计算,而非直接用于像素级过滤:
步骤 1:对焊盘掩膜进行超大尺寸膨胀,弥合青色遮挡形成的黑洞:
# main.py — build_soft_pad_mask()
def build_soft_pad_mask(target_pad_mask, config):
"""对焊盘掩膜做超大膨胀,主动弥合青色 DRC 标记遮挡形成的黑洞。"""
dilate_kernel = build_kernel(config.pad_soft_dilate_kernel_size) # 25×25
return cv2.dilate(target_pad_mask, dilate_kernel, iterations=config.pad_soft_dilate_iterations)步骤 2:基于膨胀后的焊盘掩膜生成距离场,对每个轮廓中心计算"距焊盘最近非零像素的欧氏距离":
# main.py — 距离场计算
if np.count_nonzero(soft_pad_mask) > 0:
pad_distance_map = cv2.distanceTransform(
cv2.bitwise_not(soft_pad_mask), cv2.DIST_L2, 5
)
else:
pad_distance_map = np.full((h, w), np.inf, dtype=np.float32)步骤 3:将距离与自适应阈值比较,得到"空间亲和度"标志,作为过滤条件之一:
# main.py — 逐轮廓亲和度判定
pad_distance = float(pad_distance_map[center_y, center_x])
pad_distance_limit = max(config.pad_affinity_min_pixels,
circle_radius * config.pad_affinity_radius_ratio)
pad_affinity_passed = pad_distance <= pad_distance_limit新旧流程对比:
| 环节 | origin.py(旧) | main.py(新) |
|---|---|---|
| 焊盘掩膜用途 | 与青色掩膜做 AND 硬过滤 |
仅用于生成距离场,作为软约束 |
| 青色目标是否会被掩膜黑洞消除 | ✅ 会(致命缺陷) | ❌ 不会 |
| 约束方式 | 像素级布尔运算 | 轮廓中心欧氏距离阈值 |
| 黑洞弥合 | 无 | 25×25 超大膨胀核 |
原代码在判定扩展方向后,将"副轴"(不做 2.5 倍扩展的轴)的两端均设为同一中心值,导致输出零面积线段:
# origin.py — 以垂直扩展为例,X 轴两端相同
p1_x = (min_x + max_x) // 2
p1_y = max(0, min_y - expansion)
p2_x = (min_x + max_x) // 2 # ← 与 p1_x 相同 → 零宽度
p2_y = min(h - 1, max_y + expansion)新代码明确区分"主轴"(做 2.5 倍外推)和"副轴"(继承原始轮廓边界 + Padding),保证操作框始终具有 2D 面积:
# main.py — build_operation_rectangle()
def build_operation_rectangle(candidate, direction, img_shape, config):
"""
主轴:基于重构圆直径做 2.5 倍外推
副轴:继承原始残缺轮廓的 min/max 边界并追加 padding
"""
cx, cy = candidate["circle_center"]
radius = candidate["circle_radius"]
min_x, max_x, min_y, max_y = candidate["contour_bounds"]
native_diameter = max(2.0 * radius, float(config.minimum_box_side_pixels))
expanded_diameter = native_diameter * config.operation_box_expand_ratio # × 2.5
half_major_axis = expanded_diameter / 2.0
padding = config.operation_box_padding_pixels # 默认 4px
if direction == "horizontal":
# 主轴 X:以重构圆心为基准 × 2.5 外推
x1 = cx - half_major_axis
x2 = cx + half_major_axis
# 副轴 Y:继承原始轮廓上下边界并加 padding
y1 = min_y - padding
y2 = max_y + padding
else: # vertical
# 副轴 X:继承原始轮廓左右边界并加 padding
x1 = min_x - padding
x2 = max_x + padding
# 主轴 Y:以重构圆心为基准 × 2.5 外推
y1 = cy - half_major_axis
y2 = cy + half_major_axis
return clamp_rectangle(x1, y1, x2, y2, img_shape, config)效果对比:
| 方向 | origin.py 输出 | main.py 输出 | 几何有效性 |
|---|---|---|---|
| horizontal | coor1(120, 350) → coor2(480, 350) |
coor1(120, 346) → coor2(480, 358) |
❌ 线段 → ✅ 矩形 |
| vertical | coor1(300, 120) → coor2(300, 520) |
coor1(286, 120) → coor2(314, 520) |
❌ 线段 → ✅ 矩形 |
上一版 main.py 的 determine_anomaly_direction() 只有一层外部参考:如果能找到焊盘圆心,就按“异常圆心相对焊盘圆心”的位置判断方向;如果找不到,则直接回退到固定默认值。当前版本改成了多级决策链,方向稳定性明显更高:
# 当前 main.py — 多级方向判定入口
nearest_anchor_point, nearest_anchor_distance = find_nearest_mask_point(
candidate["circle_center"], direction_anchor_points
)
direction, direction_source, effective_reference_point = determine_anomaly_direction(
candidate=candidate,
direction_anchor_point=nearest_anchor_point,
pad_center=nearest_pad,
config=config,
)# 当前 main.py — determine_anomaly_direction() 的核心优先级
1. pad_boundary -> 优先使用最近红铜边界锚点
2. pad_center -> 次优使用焊盘圆心
3. contour_aspect -> 无可靠外部参考时按轮廓宽高差推断
4. reconstructed_extent -> 近方形轮廓时按圆心到边界跨度兜底关键增量点:
- 新增
build_direction_anchor_mask(),对target_pad_mask做形态学梯度,提取红铜边界作为方向锚点 - 新增
direction_min_reference_distance_pixels = 6.0,避免参考点离异常中心过近时产生抖动判断 - 新增
direction_min_anisotropy_pixels = 3.0,当轮廓近似方形时不再误用宽高比微小噪声 - 即使圆形结构检测失败,也能通过红铜边界或轮廓几何继续输出稳定方向,而不是退化到写死默认值
将通用"蓝色"阈值替换为针对 Genesis 纯青色的专项范围,放宽 S/V 下限以捕获压缩衰减的像素:
# origin.py — 通用蓝色(无法准确覆盖 Genesis Cyan)
lower_blue1 = np.array([100, 80, 80])
upper_blue1 = np.array([130, 255, 255])
lower_blue2 = np.array([90, 60, 60])
upper_blue2 = np.array([110, 255, 255])# main.py — Genesis Cyan 专项阈值(DetectorConfig)
cyan_hsv_ranges: tuple = (
((78, 50, 50), (98, 255, 255)), # 标准青色(H:78~98,S/V 下限 50)
((90, 40, 40), (110, 255, 255)), # 宽容青色(覆盖压缩衰减)
)主要改动:
- H 通道精确锁定青色区间(78~110),排除偏蓝或偏绿的干扰色
- S/V 下限从 60/60 降低至 40/40,覆盖因截图压缩导致饱和度和亮度下降的像素
Genesis DRC 标记经常被黑色间距"腰斩"为多段残缺碎片,直接使用碎片的 Bounding Box 会导致重心和尺寸严重失真。
新机制:对每个候选轮廓强制拟合最小外接圆,以理想圆的圆心和半径作为后续所有计算的基准:
# main.py — 外接圆重构(位于逐轮廓分析循环中)
(circle_cx, circle_cy), circle_radius = cv2.minEnclosingCircle(contour)
circle_radius = max(float(circle_radius), config.min_reconstructed_radius_pixels)
center_x = int(np.clip(round(circle_cx), 0, w - 1))
center_y = int(np.clip(round(circle_cy), 0, h - 1))外接圆的作用链:
残缺青色轮廓
│
▼
cv2.minEnclosingCircle(contour)
│ 得到理想圆 (cx, cy, r)
├──→ 距离场查询坐标:pad_distance_map[cy, cx]
├──→ 焊盘距离计算:dist(圆心, 焊盘中心)
├──→ 方向判断:铜边界锚点优先,其次焊盘圆心,最后几何兜底
└──→ 操作框生成:以 2r 为原始直径 × 2.5 外推(替代残缺 Bounding Box)
对比:
| 属性 | origin.py(残缺 BBox) | main.py(外接圆重构) |
|---|---|---|
| 重心位置 | 残缺碎片几何中心,偏离真实 | 完整标记的理想圆心 |
| 尺寸参考 | 碎片的 w_box × h_box |
外接圆直径 2r |
| 方向判断基准 | 碎片重心 | 理想圆心 |
| 操作框主轴长度 | 残缺片段宽/高 × 2.5 | 完整圆直径 × 2.5 |
调试模式下,重构圆会以青色圆圈绘制在 _contours_debug.jpg 上,可直观验证重构效果。
将原代码中散落在各处的硬编码魔法数字,统一迁移至冻结数据类 DetectorConfig,便于实验和复现:
# main.py — 集中参数管理(节选)
@dataclass(frozen=True)
class DetectorConfig:
# 焊盘软约束
pad_soft_dilate_kernel_size: int = 25 # 黑洞弥合核尺寸
pad_affinity_radius_ratio: float = 0.90 # 亲和度半径比例
pad_affinity_min_pixels: float = 8.0 # 亲和度最小距离(像素)
# Cyan 形态学
cyan_open_kernel_size: int = 3
cyan_close_kernel_size: int = 5
# 方向判定
direction_min_reference_distance_pixels: float = 6.0
direction_min_anisotropy_pixels: float = 3.0
# 操作框生成
operation_box_expand_ratio: float = 2.5 # 2.5 倍外推
operation_box_padding_pixels: int = 4 # 副轴 Padding
minimum_box_side_pixels: int = 4 # 最小边长保障
# 外接圆
min_reconstructed_radius_pixels: float = 3.0
# 保底参数
fallback_width_ratio: float = 0.20
fallback_height_ratio: float = 0.12新版增加了两级保底机制,确保自动化链路不因单张图像异常而中断:
# main.py — 顶层异常隔离
def extract_blue_regions_x_range(image_path, output_dir=None, debug=False, config=CONFIG):
try:
return _extract_blue_regions_x_range_impl(...)
except Exception as error:
# 管道异常时,返回具备 2D 面积的应急矩形,而非 None
return build_exception_fallback_result(...)- Fallback 候选池:所有候选均未通过过滤时,从候选池中按亲和度排序选出最优候选
- 默认区域:候选池也为空时,在 ROI 中心输出具备明确 2D 面积的默认矩形
- 应急兜底:整个检测管道抛出未捕获异常时,仍然输出有效 JSON,不返回
None
# main.py — 当前版本入口
nearest_anchor_point, nearest_anchor_distance = find_nearest_mask_point(
candidate["circle_center"], direction_anchor_points
)
direction, direction_source, effective_reference_point = determine_anomaly_direction(
candidate=candidate,
direction_anchor_point=nearest_anchor_point,
pad_center=nearest_pad,
config=config,
)# main.py — determine_anomaly_direction() 的主逻辑(节选)
if direction_anchor_point is not None and reference_distance >= 6:
return "vertical" if abs(dx) >= abs(dy) else "horizontal", "pad_boundary", direction_anchor_point
if pad_center is not None and reference_distance >= 6:
return "vertical" if abs(dx) >= abs(dy) else "horizontal", "pad_center", pad_center
if abs(contour_width - contour_height) >= 3:
return ("horizontal", "contour_aspect", None) if contour_width >= contour_height else ("vertical", "contour_aspect", None)
return ("horizontal", "reconstructed_extent", None) if horizontal_span >= vertical_span else ("vertical", "reconstructed_extent", None)当前版本的判定顺序:
| 优先级 | direction_source |
触发条件 | 判定依据 |
|---|---|---|---|
| 1 | pad_boundary |
最近红铜边界锚点存在,且参考距离 ≥ 6px | 比较异常圆心相对锚点的 abs(dx) / abs(dy) |
| 2 | pad_center |
焊盘圆心存在,且参考距离 ≥ 6px | 比较异常圆心相对焊盘圆心的 abs(dx) / abs(dy) |
| 3 | contour_aspect |
无可靠外部参考,且宽高差 ≥ 3px | 轮廓宽高差决定主轴 |
| 4 | reconstructed_extent |
轮廓近似方形 | 比较重构圆心到左右/上下边界的有效跨度 |
方向语义保持不变:
direction |
主轴 | 操作框行为 |
|---|---|---|
horizontal |
X 轴 | 沿 X 轴按重构圆直径做 2.5 倍外推 |
vertical |
Y 轴 | 沿 Y 轴按重构圆直径做 2.5 倍外推 |
1. get_target_pad_mask() → 提取与焊盘圆相连的红色连通域
2. build_soft_pad_mask() → 超大膨胀(25×25)弥合青色遮挡黑洞
3. cv2.distanceTransform() → 生成逐像素到焊盘掩膜的距离场
4. pad_distance_map[cy,cx] → 读取候选轮廓外接圆圆心处的距离值
5. 与阈值比较:
limit = max(8px, radius × 0.90)
pad_affinity_passed = (pad_distance <= limit)
候选排序键(升序,越小越优先):
# main.py — build_candidate_rank()
(
pad_distance, # 焊盘距离(越近越好)
distance_to_structure, # 圆形结构距离(越近越好)
-circle_radius, # 半径(越大越好,取负)
-area, # 面积(越大越好,取负)
)1. HSV 双阈值提取所有红色像素(焊盘 + 走线)
2. 形态学闭运算填充红色区域内部小孔
3. connectedComponentsWithStats 标记所有红色连通域
4. 遍历 circular_centers(检测到的焊盘圆):
a. 在圆心坐标处查询连通域 ID
b. 若圆心落在空洞(label == 0),以 1.6 倍半径探测圆查找最近连通域
c. 将匹配到的连通域 ID 加入有效集合
5. 只保留有效集合内的连通域像素,生成 target_pad_mask
6. 超大膨胀(25×25)→ soft_pad_mask(弥合黑洞,供距离场使用)
所有保底矩形输出均通过 clamp_rectangle() 保证最小边长(minimum_box_side_pixels = 4),防止退化为点或线:
# main.py — clamp_rectangle()
if x2 - x1 < min_side:
center_x = (x1 + x2) / 2.0
x1 = int(np.clip(np.floor(center_x - min_side / 2.0), 0, w - 1))
x2 = int(np.clip(np.ceil (center_x + min_side / 2.0), 0, w - 1))
# Y 轴同理| 场景 | origin.py | main.py |
|---|---|---|
| 青色像素与焊盘掩膜黑洞完全重合 | 目标被 AND 抹除,退化为保底输出 | 软约束距离场判断,正常检测 |
| 青色像素与焊盘掩膜部分重合 | 目标被部分抹除,轮廓碎片化 | 完整保留青色轮廓 |
| 焊盘未被检测到 | 全部目标被 AND 抹除 | 保底返回所有候选 |
| 方向 | origin.py(p1_x == p2_x 或 p1_y == p2_y) |
main.py(副轴继承原始边界) |
|---|---|---|
| vertical | 零宽度线段,API 崩溃风险 | 具有明确宽度的矩形 |
| horizontal | 零高度线段,API 崩溃风险 | 具有明确高度的矩形 |
| 场景 | origin.py(残缺 BBox) | main.py(外接圆) |
|---|---|---|
| DRC 标记被黑色间距切为两半 | 操作框仅覆盖半个标记 | 操作框覆盖完整标记直径 |
| 碎片偏于一侧 | 重心偏离,方向判断错误 | 外接圆圆心准确,方向正确 |
| 极小碎片(< 5px) | 操作框塌陷 | min_reconstructed_radius_pixels = 3.0 保障最小尺寸 |
基于 282 张 TrainData/ 训练图像的批量推理结果(开启 --debug):
| 指标 | origin.py | main.py | 变化 |
|---|---|---|---|
| 正常检测率(高置信度) | 约 0%(因死锁全部退化) | 67.4%(190/282) | 大幅提升 |
| 保底输出率 | 约 100% | 1.1%(3/282) | 显著降低 |
| 默认区域率 | 0%(因死锁退化为保底而非默认) | 31.6%(89/282,真实无青色标记) | 如实反映 |
| 零面积坐标输出 | 存在(API 崩溃风险) | 0(全部具备 2D 面积) | 彻底消除 |
说明:31.6% 默认区域主要为训练集中不含青色 DRC 标记的正常焊盘图像,属于正常分布,并非检测失败。
{
"image_name": "031",
"anomalies": [
{
"id": 0,
"direction": "vertical",
"coor1": { "x": 300, "y": 180 },
"coor2": { "x": 300, "y": 520 },
"confidence": "high",
"type": "normal"
}
]
}coor1.x == coor2.x == 300,零宽度线段。
{
"image_name": "031",
"anomalies": [
{
"id": 0,
"direction": "vertical",
"coor1": { "x": 284, "y": 120 },
"coor2": { "x": 316, "y": 540 },
"reconstructed_circle": {
"center": { "x": 300, "y": 330 },
"radius": 32.5
},
"confidence": "high",
"type": "normal",
"debug_pad_reference": {
"direction_source": "pad_boundary",
"anomaly_center": { "x": 300, "y": 330 },
"anchor_distance": 18.44,
"pad_boundary_point": { "x": 282, "y": 328 },
"pad_center": { "x": 295, "y": 310 },
"effective_reference_point": { "x": 282, "y": 328 }
}
}
]
}coor1.x = 284,coor2.x = 316,宽度为 32px,正常 2D 矩形。
| 字段 | 类型 | 说明 | 来源 |
|---|---|---|---|
direction |
string | 切削方向("horizontal" / "vertical") |
不变 |
reconstructed_circle |
object | 外接圆重构结果(圆心坐标 + 半径) | 新增 |
reconstructed_circle.center |
object | 理想圆心 {x, y} |
新增 |
reconstructed_circle.radius |
float | 外接圆半径(像素) | 新增 |
confidence |
string | "high" / "low" / "very_low" |
不变 |
type |
string | "normal" / "fallback" / "default_region" |
不变 |
debug_pad_reference |
object | 方向判定参考信息(仅 debug 模式) | 扩充 |
debug_info |
object | 过滤诊断信息(仅 fallback 时输出) | 扩充 |
debug_pad_reference 子字段(当前版新增方向来源信息):
| 字段 | 类型 | 说明 |
|---|---|---|
direction_source |
string | 本次方向判定命中的参考层级:pad_boundary / pad_center / contour_aspect / reconstructed_extent |
anomaly_center |
object | 当前异常的重构圆心 {x, y} |
anchor_distance |
float / "inf" | 异常圆心到最近红铜边界锚点的距离 |
pad_boundary_point |
object / null | 最近红铜边界锚点 |
pad_center |
object / null | 最近焊盘圆心 |
effective_reference_point |
object / null | 实际参与本次方向判断的参考点 |
debug_info 子字段(新增 pad/结构距离):
| 字段 | 类型 | 说明 |
|---|---|---|
area |
int | 候选轮廓像素面积 |
pad_distance |
float / "inf" | 外接圆圆心到焊盘掩膜的距离(像素) |
structure_distance_ratio |
float / "inf" | 外接圆圆心到最近焊盘圆心的距离/半径比 |
filter_reasons |
list[str] | 被过滤的原因列表 |
for anomaly in result["anomalies"]:
x1 = anomaly["coor1"]["x"]
y1 = anomaly["coor1"]["y"]
x2 = anomaly["coor2"]["x"]
y2 = anomaly["coor2"]["y"]
direction = anomaly.get("direction", "horizontal")
# 新增:可通过 reconstructed_circle 获取精确圆心供精准对位
circle = anomaly.get("reconstructed_circle", {})
cx = circle.get("center", {}).get("x")
cy = circle.get("center", {}).get("y")
radius = circle.get("radius")
if direction == "horizontal":
device.move_horizontal(x1, x2, y_center=(y1 + y2) // 2)
else:
device.move_vertical(y1, y2, x_center=(x1 + x2) // 2)| 字段 | 兼容性 |
|---|---|
id, coor1, coor2, confidence, type |
完全兼容,结构不变 |
direction |
已存在,语义不变 |
reconstructed_circle |
新增,旧端用 .get() 可安全忽略 |
debug_pad_reference.direction_source / .anchor_distance 等 |
新增子字段,旧端忽略 |
debug_info.pad_distance / .structure_distance_ratio |
新增子字段,旧端忽略 |
Python >= 3.9
opencv-python >= 4.5.0
numpy >= 1.19.0
scipy/scikit-image(分水岭算法)已从必要依赖中移除;DetectorConfig使用dataclasses(标准库),无额外依赖。
单张图像处理:
conda activate base
python main.py --single TrainData/031.png --output results --debug批量处理目录:
conda activate base
python main.py TrainData/ --output anomaly_results --debug参数说明:
| 参数 | 说明 | 默认值 |
|---|---|---|
input_dir |
批量处理的输入图像目录 | 必填 |
--single |
单张图像路径 | — |
--output / -o |
输出目录 | ./anomaly_results |
--debug |
保存调试可视化图像 | 关闭 |
每张图像生成以下文件:
| 文件名 | 内容 |
|---|---|
{name}_anomalies.json |
检测结果(含 reconstructed_circle 字段) |
{name}_contours_debug.jpg |
原图 + 重构圆(青色)+ 操作框(蓝色)+ 焊盘圆(黄色) |
{name}_pad_mask_filled.jpg |
软约束焊盘掩膜(超大膨胀后) |
{name}_blue_mask_filtered.jpg |
通过亲和度过滤的青色候选掩膜 |
{name}_direction_anchor_mask.jpg |
红铜边界锚点掩膜(用于方向判定) |
blue_anomalies_summary.json |
全批次汇总 JSON |
若正常检测率偏低,可依次尝试:
- 漏检青色区域:调低
cyan_hsv_ranges的 S/V 下限(当前为 40) - 焊盘亲和度过于严格:调大
pad_affinity_radius_ratio(当前 0.90)或pad_affinity_min_pixels(当前 8) - 黑洞弥合不足:调大
pad_soft_dilate_kernel_size(当前 25) - 方向偶发抖动或误判:调整
direction_min_reference_distance_pixels(当前 6)或direction_min_anisotropy_pixels(当前 3) - 操作框过大/过小:调整
operation_box_expand_ratio(当前 2.5)
| 问题 | 原症状 | 修复方案 | 关键代码位置 |
|---|---|---|---|
| 布尔逻辑死锁 | 青色目标被 AND 操作完全抹除 | 废除硬相交,改用软约束距离场 | build_soft_pad_mask() / pad_distance_map |
| 方向参考单一 | 焊盘圆缺失时易退化到固定方向,方向抖动明显 | 铜边界锚点优先 + 焊盘圆心次优 + 几何兜底 | build_direction_anchor_mask() / determine_anomaly_direction() |
| 坐标轴塌陷 | 输出零面积线段,API 崩溃 | 正交轴边界继承 + 副轴 Padding | build_operation_rectangle() |
| 残缺标记失真 | 碎片重心/尺寸偏离,框偏小 | cv2.minEnclosingCircle 外接圆重构 |
逐轮廓分析循环 |
- DetectorConfig:所有超参数集中管理,消除散落魔法数字
- 异常隔离:顶层
try/except确保单张图像失败不中断批量任务 - 保底分层:正常 → Fallback 候选池 → 默认区域 → 应急兜底,四级降级
- 方向可解释性:
debug_pad_reference.direction_source可直接追踪本次方向来自铜边界、焊盘圆心还是几何兜底 reconstructed_circle输出:让下游设备可以直接使用物理圆心精准对位,无需二次推算