1. 基于OpenCV的划痕检测基础

任务目标

使用OpenCV对金属表面图像中的划痕进行检测,输出划痕的像素位置(最小外接矩形中心)与长度(像素单位),并在图像上可视化标注检测结果。

环境依赖

Python 3.8+,OpenCV 4.5+,NumPy。安装命令:pip install opencv-python numpy

实现思路

划痕在金属表面通常表现为低灰度值的细长暗线。本案例采用以下处理链路:灰度化 → 高斯模糊降噪 → 自适应阈值提取暗部区域 → 形态学闭运算连接断裂线段 → 查找轮廓 → 按面积与长宽比过滤非划痕噪声 → 输出并标注结果。

关键完整代码

import cv2
import numpy as np

def detect_scratch(image_path):
    # 读取图像
    img = cv2.imread(image_path)
    if img is None:
        raise ValueError("无法读取图像,请检查路径")

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 高斯模糊抑制噪声
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)

    # 自适应阈值提取暗色划痕区域(划痕比背景暗)
    thresh = cv2.adaptiveThreshold(
        blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
        cv2.THRESH_BINARY_INV, 11, 2
    )

    # 形态学闭运算连接断裂的划痕线段
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 3))
    closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)

    # 查找轮廓
    contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    results = []
    for cnt in contours:
        area = cv2.contourArea(cnt)
        if area < 30:  # 过滤极小噪声
            continue

        # 最小外接矩形
        rect = cv2.minAreaRect(cnt)
        (cx, cy), (w, h), angle = rect
        length = max(w, h)  # 划痕长度取矩形长边

        # 长宽比过滤:划痕应为细长形状
        if length < 10 or min(w, h) == 0:
            continue
        aspect_ratio = length / min(w, h)
        if aspect_ratio < 2.0:  # 非细长形状视为非划痕
            continue

        # 绘制检测结果
        box = cv2.boxPoints(rect)
        box = np.int0(box)
        cv2.drawContours(img, [box], 0, (0, 0, 255), 2)
        cv2.circle(img, (int(cx), int(cy)), 3, (0, 255, 0), -1)

        results.append({
            "center": (round(cx, 1), round(cy, 1)),
            "length_px": round(length, 1),
            "angle_deg": round(angle, 1)
        })

    return img, results

if __name__ == "__main__":
    # 使用示例(替换为实际图像路径)
    img_out, scratches = detect_scratch("metal_scratch.jpg")
    print(f"检测到 {len(scratches)} 条划痕:")
    for i, s in enumerate(scratches):
        print(f"  划痕{i+1}: 中心位置{s['center']}, 长度{s['length_px']}px, 角度{s['angle_deg']}°")
    cv2.imwrite("scratch_result.jpg", img_out)
    print("结果图像已保存为 scratch_result.jpg")

运行与验证

  1. 准备一张包含划痕的金属表面图像(建议亮背景、暗划痕),命名为 metal_scratch.jpg 放在脚本同目录。
  2. 运行脚本:python scratch_detection.py
  3. 观察终端输出:应打印每条划痕的中心坐标与像素长度。
  4. 打开生成的 scratch_result.jpg:划痕被红色矩形框标注,中心点有绿色圆点标记。

通过判定标准:图像中肉眼可见的划痕均被红色框包围,且每个框内包含一条完整划痕;终端输出的划痕数量与图像中划痕数量一致;长度数值与图像中划痕的像素尺度相符(可用图像编辑软件测量对比)。

常见问题

  • 检测不到划痕:若划痕过浅,可降低自适应阈值参数blockSize(如从11改为7)或增大C值(从2改为3);若划痕断裂严重,增大闭运算迭代次数或kernel尺寸。
  • 误检过多:提高面积阈值(30→50)或长宽比阈值(2.0→3.0)。
  • 划痕为亮色:若划痕比背景亮,将THRESH_BINARY_INV改为THRESH_BINARY。
检测框 中心点

上图展示了检测效果示意:红色矩形为划痕的最小外接框,绿色圆点标记划痕中心位置。实际输出坐标与长度以终端打印数值为准。