Python机器视觉第三阶段:缺陷检测算法(传统方法+深度学习入门)
Python机器视觉第三阶段:缺陷检测算法(传统方法+深度学习入门)
在完成了OpenCV基础操作的学习后,我们正式进入缺陷检测算法的学习。这是机器视觉项目的核心,也是从“会用工具”到“解决实际问题”的关键一步。
本阶段内容分为两大块:
- 传统方法:阈值分割、差分、模板匹配、特征工程(LBP/HOG)
- 深度学习方法:分类网络(ResNet)、目标检测(YOLO)、分割网络(UNet)
本文所有代码都可以直接复制运行,注释非常详细。建议在学习过程中结合参考文档边学边练。
一、传统缺陷检测方法
传统方法不依赖深度学习,通过图像处理和特征工程实现缺陷检测,适合规则性强的场景(如印刷品检测、尺寸测量)。
1. 阈值分割(Thresholding)
阈值分割是图像分割中最基础的方法,通过设定像素值阈值将图像分为前景和背景。
1.1 全局阈值
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取灰度图
img = cv2.imread('defect_sample.jpg', cv2.IMREAD_GRAYSCALE)
# 全局阈值分割
ret, thresh_binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
ret, thresh_binary_inv = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh_trunc = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
ret, thresh_tozero = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
# 显示结果
plt.figure(figsize=(15,10))
plt.subplot(2,3,1), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(2,3,2), plt.imshow(thresh_binary, cmap='gray'), plt.title('Binary')
plt.subplot(2,3,3), plt.imshow(thresh_binary_inv, cmap='gray'), plt.title('Binary Inv')
plt.subplot(2,3,4), plt.imshow(thresh_trunc, cmap='gray'), plt.title('Trunc')
plt.subplot(2,3,5), plt.imshow(thresh_tozero, cmap='gray'), plt.title('Tozero')
plt.show()
1.2 自适应阈值(解决光照不均)
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('uneven_light.jpg', cv2.IMREAD_GRAYSCALE)
# 自适应阈值
# ADAPTIVE_THRESH_MEAN_C:邻域均值减去C
# ADAPTIVE_THRESH_GAUSSIAN_C:邻域加权均值减去C
thresh_mean = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 11, 2)
thresh_gaussian = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
plt.figure(figsize=(12,4))
plt.subplot(1,3,1), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(1,3,2), plt.imshow(thresh_mean, cmap='gray'), plt.title('Mean Adaptive')
plt.subplot(1,3,3), plt.imshow(thresh_gaussian, cmap='gray'), plt.title('Gaussian Adaptive')
plt.show()
1.3 Otsu大津法(自动确定最佳阈值)
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('defect_sample.jpg', cv2.IMREAD_GRAYSCALE)
# Otsu自动计算阈值
ret, thresh_otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print(f"Otsu自动计算的阈值: {ret}")
plt.figure(figsize=(12,4))
plt.subplot(1,2,1), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(1,2,2), plt.imshow(thresh_otsu, cmap='gray'), plt.title(f'Otsu (thresh={ret:.1f})')
plt.show()
2. 图像差分(Image Difference)
差分法通过比较待测图像与标准模板图像的差异来检测缺陷,适用于固定场景的缺陷检测。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取标准模板和待测图像
template = cv2.imread('template.jpg', cv2.IMREAD_GRAYSCALE)
test_img = cv2.imread('test_with_defect.jpg', cv2.IMREAD_GRAYSCALE)
# 确保图像大小一致
if template.shape != test_img.shape:
test_img = cv2.resize(test_img, (template.shape[1], template.shape[0]))
# 计算绝对差分
diff = cv2.absdiff(template, test_img)
# 阈值化得到缺陷区域
_, diff_thresh = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)
# 形态学操作去除噪声
kernel = np.ones((3,3), np.uint8)
diff_clean = cv2.morphologyEx(diff_thresh, cv2.MORPH_OPEN, kernel, iterations=2)
# 显示结果
plt.figure(figsize=(15,5))
plt.subplot(1,4,1), plt.imshow(template, cmap='gray'), plt.title('Template')
plt.subplot(1,4,2), plt.imshow(test_img, cmap='gray'), plt.title('Test Image')
plt.subplot(1,4,3), plt.imshow(diff, cmap='gray'), plt.title('Difference')
plt.subplot(1,4,4), plt.imshow(diff_clean, cmap='gray'), plt.title('Defect Regions')
plt.show()
3. 模板匹配(Template Matching)
模板匹配在图像中搜索与模板最相似的区域,常用于定位已知图案或检测缺失/偏移。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取原图和模板
img = cv2.imread('circuit_board.jpg')
template = cv2.imread('component_template.jpg', 0)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
w, h = template.shape[::-1]
# 六种匹配方法
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
plt.figure(figsize=(15,10))
for i, method_name in enumerate(methods):
img_copy = img.copy()
method = eval(method_name)
# 模板匹配
res = cv2.matchTemplate(gray, template, method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 根据方法确定最佳匹配位置
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
# 画矩形框
cv2.rectangle(img_copy, top_left, bottom_right, (0,0,255), 2)
plt.subplot(2,3,i+1)
plt.imshow(cv2.cvtColor(img_copy, cv2.COLOR_BGR2RGB))
plt.title(f'{method_name[7:]}')
plt.axis('off')
plt.tight_layout()
plt.show()
4. 特征工程:LBP与HOG
4.1 LBP(局部二值模式)
LBP是一种纹理描述算子,对光照变化鲁棒,常用于纹理分类和缺陷检测。
import cv2
import numpy as np
import matplotlib.pyplot as plt
from skimage import feature
def compute_lbp(image, radius=1, n_points=8):
"""计算LBP特征图"""
lbp = feature.local_binary_pattern(image, n_points, radius, method='uniform')
return lbp
# 读取灰度图
img = cv2.imread('texture_sample.jpg', cv2.IMREAD_GRAYSCALE)
# 计算LBP
lbp_uniform = compute_lbp(img, radius=1, n_points=8)
plt.figure(figsize=(12,4))
plt.subplot(1,3,1), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(1,3,2), plt.imshow(lbp_uniform, cmap='jet'), plt.title('LBP (Uniform)')
plt.subplot(1,3,3), plt.hist(lbp_uniform.ravel(), bins=20, range=(0, 10))
plt.title('LBP Histogram')
plt.show()
4.2 HOG(方向梯度直方图)
HOG通过计算图像局部区域的梯度方向直方图来描述物体形状,常用于行人检测、物体识别。
import cv2
import matplotlib.pyplot as plt
from skimage.feature import hog
from skimage import exposure
# 读取图像
img = cv2.imread('object_sample.jpg', cv2.IMREAD_GRAYSCALE)
# 计算HOG特征和可视化图像
features, hog_image = hog(img, orientations=9, pixels_per_cell=(8,8),
cells_per_block=(2,2), visualize=True,
block_norm='L2-Hys')
# 增强HOG图像对比度
hog_image_rescaled = exposure.rescale_intensity(hog_image, in_range=(0, 10))
plt.figure(figsize=(15,5))
plt.subplot(1,3,1), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(1,3,2), plt.imshow(hog_image_rescaled, cmap='gray'), plt.title('HOG Visualization')
plt.subplot(1,3,3), plt.plot(features[:500]) # 显示前500个特征
plt.title('HOG Features (first 500)')
plt.show()
print(f"HOG特征维度: {len(features)}")
5. 使用SVM分类缺陷(传统方法进阶)
结合HOG特征和SVM分类器,实现简单的缺陷分类。
import cv2
import numpy as np
from skimage.feature import hog
from sklearn import svm
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
import os
# 假设数据集结构:
# dataset/
# normal/ # 正常样本
# defect/ # 缺陷样本
def extract_hog_features(img_path):
"""提取单张图像的HOG特征"""
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (128, 128)) # 统一大小
features = hog(img, orientations=9, pixels_per_cell=(8,8),
cells_per_block=(2,2), block_norm='L2-Hys')
return features
# 准备数据
def load_dataset(data_dir):
features = []
labels = []
# 加载正常样本(标签0)
normal_dir = os.path.join(data_dir, 'normal')
for fname in os.listdir(normal_dir)[:50]: # 取前50个样本
if fname.endswith(('.jpg', '.png')):
fpath = os.path.join(normal_dir, fname)
features.append(extract_hog_features(fpath))
labels.append(0)
# 加载缺陷样本(标签1)
defect_dir = os.path.join(data_dir, 'defect')
for fname in os.listdir(defect_dir)[:50]:
if fname.endswith(('.jpg', '.png')):
fpath = os.path.join(defect_dir, fname)
features.append(extract_hog_features(fpath))
labels.append(1)
return np.array(features), np.array(labels)
# 请替换为你的数据集路径
# X, y = load_dataset('./dataset')
# 这里用随机数据演示
X = np.random.randn(100, 1764) # 模拟100个样本,每个1764维特征
y = np.random.randint(0, 2, 100)
# 划分训练测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 训练SVM分类器
clf = svm.SVC(kernel='rbf', C=1.0, gamma='scale')
clf.fit(X_train, y_train)
# 预测评估
y_pred = clf.predict(X_test)
print(classification_report(y_test, y_pred, target_names=['Normal', 'Defect']))
二、深度学习方法入门
传统方法对复杂缺陷(如形状变化、纹理异常)效果有限,深度学习方法通过自动学习特征,能处理更复杂的检测任务。
1. 分类网络:ResNet(判断图像是否包含缺陷)
import torch
import torch.nn as nn
import torchvision.transforms as transforms
from torchvision import models
from PIL import Image
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 加载预训练的ResNet18模型
model = models.resnet18(pretrained=True)
# 修改最后一层为二分类(正常/缺陷)
num_ftrs = model.fc.in_features
model.fc = nn.Linear(num_ftrs, 2)
# 设置为评估模式
model.eval()
# 如果支持GPU,将模型移到GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = model.to(device)
# 图像预处理
transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
def predict_defect(image_path):
"""预测图像是否包含缺陷"""
# 读取并预处理图像
image = Image.open(image_path).convert('RGB')
input_tensor = transform(image).unsqueeze(0).to(device)
# 推理
with torch.no_grad():
outputs = model(input_tensor)
_, predicted = torch.max(outputs, 1)
probabilities = torch.nn.functional.softmax(outputs, dim=1)
# 结果解释
class_names = ['Normal', 'Defect']
pred_class = class_names[predicted.item()]
confidence = probabilities[0][predicted.item()].item()
return pred_class, confidence
# 测试
result, conf = predict_defect('test_sample.jpg')
print(f"预测结果: {result}, 置信度: {conf:.4f}")
2. 目标检测:YOLOv8(定位缺陷位置)
YOLOv8是目前最流行的目标检测框架之一,可以同时检测多个缺陷并给出位置。
2.1 安装YOLOv8
pip install ultralytics
2.2 使用预训练模型进行推理
import cv2
from ultralytics import YOLO
import matplotlib.pyplot as plt
# 加载预训练模型(可选择yolov8n.pt, yolov8s.pt, yolov8m.pt等)
model = YOLO('yolov8n.pt') # 先用COCO预训练模型演示
# 加载自定义训练的缺陷检测模型
# model = YOLO('best.pt') # 训练好的缺陷检测模型
# 进行推理
results = model('test_image.jpg')
# 显示结果
img = cv2.imread('test_image.jpg')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 绘制检测结果
for r in results:
boxes = r.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
conf = box.conf[0].item()
cls = int(box.cls[0].item())
# 画矩形框
cv2.rectangle(img_rgb, (int(x1), int(y1)), (int(x2), int(y2)), (255,0,0), 2)
cv2.putText(img_rgb, f'Defect {conf:.2f}', (int(x1), int(y1)-5),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,0,0), 2)
plt.figure(figsize=(12,8))
plt.imshow(img_rgb)
plt.axis('off')
plt.title('YOLOv8 Defect Detection')
plt.show()
2.3 训练自定义缺陷检测模型(步骤概述)
from ultralytics import YOLO
# 1. 准备数据集(YOLO格式)
# - 每张图像对应一个txt标注文件
# - 格式:class_id x_center y_center width height
# - 所有坐标归一化到0-1
# 2. 创建数据集配置文件 defect.yaml
"""
train: ./dataset/images/train
val: ./dataset/images/val
nc: 3 # 缺陷类别数
names: ['scratch', 'dirt', 'crack'] # 类别名称
"""
# 3. 加载预训练模型并训练
model = YOLO('yolov8n.pt') # 加载预训练权重
results = model.train(
data='defect.yaml',
epochs=50,
imgsz=640,
batch=16,
name='defect_detection'
)
# 4. 验证模型
metrics = model.val()
print(f"mAP50: {metrics.box.map50}")
# 5. 导出模型
model.export(format='onnx') # 导出为ONNX格式
3. 图像分割:UNet(像素级缺陷分割)
UNet擅长对每个像素进行分类,能精确分割出缺陷的形状。
import torch
import torch.nn as nn
import torchvision.transforms as transforms
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
# 简单的UNet实现(简化版)
class SimpleUNet(nn.Module):
def __init__(self, n_classes):
super(SimpleUNet, self).__init__()
# 编码器
self.enc1 = self.conv_block(3, 64)
self.enc2 = self.conv_block(64, 128)
self.enc3 = self.conv_block(128, 256)
self.enc4 = self.conv_block(256, 512)
# 池化
self.pool = nn.MaxPool2d(2)
# 解码器
self.up4 = self.up_conv(512, 256)
self.dec4 = self.conv_block(512, 256)
self.up3 = self.up_conv(256, 128)
self.dec3 = self.conv_block(256, 128)
self.up2 = self.up_conv(128, 64)
self.dec2 = self.conv_block(128, 64)
# 输出层
self.out = nn.Conv2d(64, n_classes, kernel_size=1)
def conv_block(self, in_ch, out_ch):
return nn.Sequential(
nn.Conv2d(in_ch, out_ch, 3, padding=1),
nn.BatchNorm2d(out_ch),
nn.ReLU(inplace=True),
nn.Conv2d(out_ch, out_ch, 3, padding=1),
nn.BatchNorm2d(out_ch),
nn.ReLU(inplace=True)
)
def up_conv(self, in_ch, out_ch):
return nn.ConvTranspose2d(in_ch, out_ch, 2, stride=2)
def forward(self, x):
# 编码
e1 = self.enc1(x)
e2 = self.enc2(self.pool(e1))
e3 = self.enc3(self.pool(e2))
e4 = self.enc4(self.pool(e3))
# 解码
d4 = self.up4(e4)
d4 = torch.cat([d4, e3], dim=1)
d4 = self.dec4(d4)
d3 = self.up3(d4)
d3 = torch.cat([d3, e2], dim=1)
d3 = self.dec3(d3)
d2 = self.up2(d3)
d2 = torch.cat([d2, e1], dim=1)
d2 = self.dec2(d2)
out = self.out(d2)
return out
# 使用预训练模型进行推理
def unet_inference(image_path, model_path, device='cpu'):
# 加载模型
model = SimpleUNet(n_classes=2) # 2类:背景和缺陷
model.load_state_dict(torch.load(model_path, map_location=device))
model.eval()
model.to(device)
# 预处理图像
transform = transforms.Compose([
transforms.Resize((256, 256)),
transforms.ToTensor(),
])
img = Image.open(image_path).convert('RGB')
input_tensor = transform(img).unsqueeze(0).to(device)
# 推理
with torch.no_grad():
output = model(input_tensor)
pred = torch.argmax(output, dim=1).squeeze(0).cpu().numpy()
# 可视化结果
fig, axes = plt.subplots(1, 2, figsize=(12,5))
axes[0].imshow(img)
axes[0].set_title('Original Image')
axes[1].imshow(pred, cmap='hot')
axes[1].set_title('Defect Segmentation')
plt.show()
return pred
三、传统方法与深度学习对比总结
| 维度 | 传统方法 | 深度学习方法 |
|---|---|---|
| 数据需求 | 少量样本,依赖人工特征设计 | 大量标注数据,自动学习特征 |
| 计算资源 | CPU即可,速度快 | 需要GPU,训练时间长 |
| 可解释性 | 好,每一步可解释 | 差,黑盒模型 |
| 泛化能力 | 对场景变化敏感 | 对光照、角度变化鲁棒性更好 |
| 缺陷类型 | 规则缺陷(尺寸、位置固定) | 复杂缺陷(纹理异常、形状变化) |
| 开发周期 | 短,快速验证 | 长,需数据准备和训练 |
实际项目建议:
- 先从传统方法快速验证可行性
- 若传统方法效果不达标,再引入深度学习
- 可结合两者:传统方法粗筛ROI + 深度学习精细分类
📚 第三阶段参考文档链接
传统方法文档
- OpenCV图像处理官方教程 —— 涵盖阈值处理、几何变换、滤波、形态学操作、边缘检测等所有传统方法
- OpenCV阈值处理详解 —— 全局阈值、自适应阈值、Otsu二值化详解
- OpenCV形态学转换 —— 腐蚀、膨胀、开运算、闭运算、形态学梯度等
- OpenCV模板匹配 —— 六种匹配方法详解
- OpenCV轮廓检测 —— 轮廓查找、绘制、特征提取
特征工程文档
- scikit-image LBP官方文档 —— LBP局部二值模式详解
- scikit-image HOG官方文档 —— HOG特征提取与可视化
深度学习文档
- PyTorch官方教程 —— 深度学习入门首选,包含分类、检测、分割完整示例
- Ultralytics YOLOv8文档 —— YOLOv8官方中文文档,包含安装、训练、推理全流程
- Torchvision模型库 —— 预训练模型(ResNet、AlexNet等)使用指南
综合实战案例
- Python与OpenCV实战:工业零件缺陷检测 —— 完整缺陷检测系统架构与代码
- OpenCV(Python)9小时速成指南 —— 包含人脸检测、OCR识别等实战项目
- 使用kNN进行手写数据OCR —— 机器学习在视觉中的应用示例
- 使用分水岭算法进行图像分割 —— 分割相互接触物体的经典算法
四、下一步学习建议
完成本阶段学习后,你应该能够:
- 熟练使用阈值分割、差分、模板匹配等传统方法检测规则缺陷
- 理解LBP、HOG等特征提取原理并能应用
- 了解ResNet、YOLO、UNet的基本原理和使用方法
- 能够根据实际问题选择合适的检测方法
下一步:进入第四阶段——综合项目实战,将所学知识整合成一个完整的缺陷检测系统。
如果你在学习过程中有任何问题,欢迎在评论区留言。记得多动手练习,把代码自己敲一遍,才能真正掌握!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)