1. 基于OpenCV的金属表面划痕检测
任务目标
构建一个基于OpenCV的金属表面划痕检测程序。输入为金属表面灰度图像,输出为标记了划痕的彩色图像,并在终端打印每条划痕的位置(外接矩形中心)与长度(像素)。
实现思路
划痕在金属表面通常表现为低灰度值的细长暗线。采用以下处理链:高斯模糊降噪 → 顶帽变换增强暗细节 → 二值化 → 形态学闭运算连接断裂 → 连通域分析。通过连通域的外接矩形长宽比与面积筛选,排除点状噪声和块状污渍,保留细长划痕。
完整代码
import cv2
import numpy as np
def detect_scratches(image_path):
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
if img is None:
raise ValueError(f"无法读取图像: {image_path}")
# 1. 高斯模糊降噪
blurred = cv2.GaussianBlur(img, (5, 5), 0)
# 2. 顶帽变换:原图 - 开运算,提取暗背景下的细小暗划痕
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 15))
tophat = cv2.morphologyEx(blurred, cv2.MORPH_TOPHAT, kernel)
# 3. 自适应阈值二值化(暗区域为白色前景)
_, binary = cv2.threshold(tophat, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 4. 闭运算连接断裂的划痕段
close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, close_kernel, iterations=2)
# 5. 连通域分析
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(closed, connectivity=8)
# 6. 筛选划痕:面积适中 + 长宽比大(细长)
result = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
scratches = []
min_area = 50
max_area = 5000
min_ratio = 3.0
for i in range(1, num_labels): # 跳过背景
x, y, w, h, area = stats[i]
if area < min_area or area > max_area:
continue
# 长宽比:保证划痕方向上的长度远大于宽度
long_side = max(w, h)
short_side = min(w, h)
if short_side == 0:
continue
ratio = long_side / short_side
if ratio < min_ratio:
continue
# 记录划痕信息
cx, cy = centroids[i]
length = long_side
scratches.append({
"center": (int(cx), int(cy)),
"length": int(length),
"bbox": (x, y, w, h)
})
# 在结果图上绘制
cv2.rectangle(result, (x, y), (x + w, y + h), (0, 0, 255), 2)
cv2.circle(result, (int(cx), int(cy)), 3, (0, 255, 0), -1)
return result, scratches
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print("用法: python scratch_detect.py <图像路径>")
sys.exit(1)
output_img, scratch_list = detect_scratches(sys.argv[1])
# 保存结果
out_path = "scratches_result.jpg"
cv2.imwrite(out_path, output_img)
print(f"结果图像已保存: {out_path}")
# 打印检测结果
print(f"检测到 {len(scratch_list)} 条划痕:")
for idx, s in enumerate(scratch_list, 1):
print(f" 划痕{idx}: 位置={s['center']}, 长度={s['length']}px")
运行方式
pip install opencv-python numpy
python scratch_detect.py metal_surface.jpg
验证方法
准备一张包含明显划痕的金属表面灰度图(建议分辨率不低于300×300,划痕宽度2~10像素)。运行程序后:
- 检查生成的
scratches_result.jpg:红色矩形应框住划痕主体,绿色圆点标记划痕中心; - 终端输出的划痕数量与目视结果一致,位置坐标落在划痕实际所在区域;
- 长度值(像素)与图像中划痕的像素跨度大致相符。
若检测效果不理想,可优先调整 min_ratio(划痕越细长值越大)和 min_area(过滤小噪点)。
常见问题
- 检测不到划痕:划痕过浅时,降低顶帽变换的核尺寸(如改为11×11);或改用更小的
min_area。 - 误检块状区域:提高
min_ratio至5.0以上,或增大min_area过滤小污点。 - 划痕断裂为多段:增大闭运算的迭代次数或核尺寸(如5×5)。