简介这份资源面向计算机相关专业在校学生、教师及企业开发者提供一套基于Python的无监督正样本缺陷检测完整源码与项目说明解决仅有正样本数据时如何训练模型并输出缺陷mask的问题。项目分两个数据部分part1为黑灰图part2为白边包围与纯黑图均属正样本测试集TC_image包含正常与负样本负样本涵盖凸起、块状、线状、缺口等缺陷类型。针对背景单一、平坦区域采用较简单模型而复杂纹理数据集则改用无缺陷大图原图训练待测图以切片形式输入体现了从简单到复杂场景的模型选型思路。压缩包共120个文件以63个py源码、29个pyc编译文件、7个md说明文档为主另含bmp、jpg、png图像样本、xml标注、ipynb实验笔记及Dockerfile等整体约1.59MB结构清晰便于复现。已有261人学习适合作为毕业设计、课程大作业或项目立项演示读者可借鉴完整训练流程、数据组织方式与缺陷判定逻辑并在此基础上修改扩展。1. 只有正样本也能训缺陷检测这套 Python 源码到底能解决什么产线上拿到的样本几乎全是良品这是缺陷检测项目里最真实的开局。你手里可能只有几百张 OK 图缺陷图一张没有或者偶尔冒出来几张还标不全 mask。传统监督检测在这里直接卡死——没有负样本分类器学不出边界。这套基于 Python 的无监督正样本训练源码解决的正是这个场景只用正样本训练推理时输入一张带缺陷的图模型输出缺陷所在的 mask 区域。项目里给了两个 part 的数据part1 是黑灰图part2 是白边包围加纯黑图都算正样本测试集 TC_image 里混了正常样本和负样本负样本覆盖凸起、块状、线状、缺口几类缺陷。背景单一、属于平坦区域所以模型结构不复杂适合拿来当毕设、课程设计或大作业的落地骨架。如果你正在找一份能跑通、能改、能写进论文的缺陷检测源码这份资源的定位就是「正样本训练 缺陷 mask 输出」的最小可用闭环。2. 无监督正样本训练的原理与数据组织为什么平坦图能这么干2.1 正样本训练的核心逻辑把重建误差当缺陷分数无监督正样本训练的主流思路是自编码器或重建网络。模型只见过良品训练目标是把输入图压缩再还原让重建误差在正样本上尽可能小。推理时喂一张带缺陷的图缺陷区域因为没在训练分布里出现过重建质量差误差图自然在缺陷位置隆起。项目里背景单一、纹理平坦这个假设成立得比较稳——平坦区域的像素分布集中模型容易学到「正常长什么样」缺陷一旦打破局部一致性误差就冒出来。常见做法是编码器-解码器结构中间加一个瓶颈层限制信息容量逼模型只保留正样本的主要模式。损失函数用 MSE 或 SSIM前者对像素级差异敏感后者对结构差异更稳。这份源码走的是较简单的模型路线没有堆复杂注意力模块原因就在数据本身黑灰图、纯黑图、白边图纹理信息少上重模型反而容易过拟合到噪声。提示正样本训练不是万能钥匙。背景复杂、纹理丰富的图简单重建模型会把缺陷也当正常纹理重建掉漏检率飙升。项目正文里专门提到复杂图像数据集要用另一套思路就是这个原因。2.2 数据目录与文件角色r.bmp、t.bmp、k.bmp、s.bmp 各是什么项目根目录下的几个 bmp 文件和 Dockerfile、description.ipynb 构成了最小工程。r.bmp、t.bmp、k.bmp、s.bmp 是正样本图或测试图0_defect_repair_val.jpg 是缺陷修复后的验证输出图。description.ipynb 是项目说明 notebook通常包含数据加载、训练、推理的完整流程演示。Dockerfile 用来固化环境避免 Python 版本和依赖库版本漂移。文件/目录角色使用阶段r.bmp / t.bmp / k.bmp / s.bmp正样本或测试图训练与推理输入0_defect_repair_val.jpg缺陷修复验证输出推理后可视化description.ipynb流程说明与演示全流程参考Dockerfile环境构建部署与复现project.imlIDE 工程配置开发环境.gitignore版本控制忽略规则代码管理数据分两个 partpart1 黑灰图part2 白边包围加纯黑图。测试集 TC_image 里正常和负样本混在一起需要先判定哪些是负样本再把负样本的缺陷 mask 标出来。负样本缺陷类型包括凸起、块状、线状、缺口这四类在平坦背景下的形态差异明显mask 标注时要注意边界贴合。2.3 环境搭建Python 依赖与 Dockerfile 两条路环境搭建有两条路。第一条是本地 Python 虚拟环境第二条是直接用 Dockerfile。本地路线适合调试Docker 路线适合交付和复现。# 创建虚拟环境避免污染系统 Python python -m venv venv # Linux/macOS 激活 source venv/bin/activate # Windows 激活 venv\Scripts\activate # 安装核心依赖版本按项目实际 requirements 调整 pip install numpy opencv-python torch torchvision matplotlib scikit-image这段命令先建隔离环境再装依赖。numpy 和 opencv-python 负责图像读写与预处理torch 和 torchvision 提供网络与训练工具matplotlib 做可视化scikit-image 常用于 SSIM 等结构相似度计算。参数上torch 版本要和 CUDA 匹配没有 GPU 就用 CPU 版训练会慢但平坦图数据量小能跑通。# 基于官方 Python 镜像固定版本保证复现 FROM python:3.9-slim WORKDIR /app COPY . /app RUN pip install --no-cache-dir numpy opencv-python torch torchvision matplotlib scikit-image CMD [python, train.py]Dockerfile 的逻辑是固定基础镜像、复制代码、装依赖、设启动命令。--no-cache-dir 减小镜像体积CMD 里的 train.py 按项目实际入口替换。用 Docker 的好处是换机器不用重新配环境坏处是镜像构建时网络不稳会卡在 pip 安装建议先本地拉好 wheel 再构建。注意项目里没有明确写 requirements.txt 时依赖版本以 description.ipynb 里实际 import 和运行结果为准不要盲目装最新版torch 和 numpy 的大版本不兼容会直接报错。3. 训练与推理全流程从正样本到缺陷 mask 的代码落地3.1 数据加载与预处理把 bmp 图转成模型能吃的张量数据加载的第一步是读图、转灰度或保持通道、归一化。平坦图背景单一归一化到 [0,1] 或 [-1,1] 都行关键是训练和推理用同一套预处理。import cv2 import numpy as np import torch from torch.utils.data import Dataset, DataLoader class DefectDataset(Dataset): def __init__(self, image_paths, img_size256): self.image_paths image_paths self.img_size img_size def __len__(self): return len(self.image_paths) def __getitem__(self, idx): # 以灰度方式读取平坦图不需要彩色信息 img cv2.imread(self.image_paths[idx], cv2.IMREAD_GRAYSCALE) # 统一尺寸避免网络输入维度不一致 img cv2.resize(img, (self.img_size, self.img_size)) # 归一化到 [0,1]并增加通道维度 img img.astype(np.float32) / 255.0 img np.expand_dims(img, axis0) return torch.tensor(img) # 假设正样本路径列表已准备好 train_dataset DefectDataset([r.bmp, t.bmp, k.bmp, s.bmp]) train_loader DataLoader(train_dataset, batch_size4, shuffleTrue)这段代码定义了数据集类读图用灰度模式resize 到 256×256归一化后扩通道。参数 img_size 决定网络输入分辨率太小缺陷细节丢失太大显存吃紧平坦图 256 够用。batch_size 按显存调4 是保守值。shuffleTrue 打乱顺序避免模型学到文件顺序的伪相关。3.2 模型定义一个够用的卷积自编码器模型不用复杂卷积自编码器在平坦图上表现稳定。编码器逐层降维解码器逐层还原中间瓶颈层控制信息容量。import torch.nn as nn class ConvAutoencoder(nn.Module): def __init__(self): super().__init__() # 编码器3 层卷积逐步提取特征并降采样 self.encoder nn.Sequential( nn.Conv2d(1, 16, 3, stride2, padding1), # 256 - 128 nn.ReLU(), nn.Conv2d(16, 32, 3, stride2, padding1), # 128 - 64 nn.ReLU(), nn.Conv2d(32, 64, 3, stride2, padding1), # 64 - 32 nn.ReLU(), ) # 解码器转置卷积逐步还原尺寸 self.decoder nn.Sequential( nn.ConvTranspose2d(64, 32, 3, stride2, padding1, output_padding1), nn.ReLU(), nn.ConvTranspose2d(32, 16, 3, stride2, padding1, output_padding1), nn.ReLU(), nn.ConvTranspose2d(16, 1, 3, stride2, padding1, output_padding1), nn.Sigmoid(), # 输出归一到 [0,1] ) def forward(self, x): z self.encoder(x) out self.decoder(z) return out编码器每层 stride2 把空间尺寸减半通道数翻倍从 1 通道到 64 通道。解码器用 ConvTranspose2d 还原output_padding1 补齐奇数尺寸的还原偏差。最后一层 Sigmoid 把输出压到 [0,1]和输入归一化范围对齐。这个结构参数量小训练快适合平坦图。如果缺陷极细可以把第一层通道数提到 32但别盲目加深容易过拟合。3.3 训练循环损失、优化器与早停训练目标是最小化重建误差。损失用 MSE优化器用 Adam学习率 1e-3 起步。import torch.optim as optim device torch.device(cuda if torch.cuda.is_available() else cpu) model ConvAutoencoder().to(device) criterion nn.MSELoss() optimizer optim.Adam(model.parameters(), lr1e-3) epochs 100 best_loss float(inf) for epoch in range(epochs): model.train() total_loss 0 for batch in train_loader: batch batch.to(device) optimizer.zero_grad() recon model(batch) loss criterion(recon, batch) loss.backward() optimizer.step() total_loss loss.item() avg_loss total_loss / len(train_loader) # 简单早停连续多轮不降就停 if avg_loss best_loss: best_loss avg_loss torch.save(model.state_dict(), best_autoencoder.pth) print(fEpoch {epoch1}, Loss: {avg_loss:.6f})这段循环里zero_grad 清梯度forward 算重建criterion 算 MSEbackward 反传step 更新。每轮记录平均损失损失创新低就存模型。早停没写全实际可以加 patience 计数连续 10 轮不降就 break。学习率 1e-3 是 Adam 常用起点损失震荡就降到 1e-4。3.4 推理与缺陷 mask 生成误差图阈值化推理时加载模型输入测试图算重建误差阈值化得到 mask。import matplotlib.pyplot as plt model.load_state_dict(torch.load(best_autoencoder.pth)) model.eval() def detect_defect(img_path, threshold0.05): img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) img cv2.resize(img, (256, 256)).astype(np.float32) / 255.0 tensor torch.tensor(img).unsqueeze(0).unsqueeze(0).to(device) with torch.no_grad(): recon model(tensor) # 计算逐像素误差 error torch.abs(recon - tensor).squeeze().cpu().numpy() # 阈值化生成 mask mask (error threshold).astype(np.uint8) * 255 return img, error, mask img, error, mask detect_defect(TC_image/sample.bmp) plt.subplot(1,3,1); plt.imshow(img, cmapgray); plt.title(Input) plt.subplot(1,3,2); plt.imshow(error, cmaphot); plt.title(Error) plt.subplot(1,3,3); plt.imshow(mask, cmapgray); plt.title(Mask) plt.show()推理流程是读图、预处理、前向、算误差、阈值化。threshold 是关键参数0.05 是起点误检多就调高漏检多就调低。error 图用 hot 色图看缺陷位置会亮。mask 是二值图白色为缺陷。实际项目里可以加形态学开闭运算去噪或者用连通域过滤小面积误检。提示阈值不是固定值不同批次图的光照和对比度会变。稳妥做法是在一批正常图上跑推理统计误差分布取 95% 或 99% 分位数当阈值。4. 避坑与排查正样本训练里最容易翻车的五个点4.1 现象训练损失降得很低推理却全是误检原因通常是正样本太少或太单一模型把训练集背下来了泛化差。解决是加数据增强旋转、翻转、亮度扰动或者用更强的瓶颈约束。平坦图增强幅度别太大否则正常样本被增强成「异常」。4.2 现象缺陷 mask 断断续续连不成片原因是误差图阈值化后噪声多或者缺陷本身对比度低。解决是先对 error 图做高斯模糊再阈值化或者用形态学闭运算把断开的区域连起来。阈值调低能连片但误检会增加需要权衡。4.3 现象白边包围的 part2 图被当成缺陷原因是白边在训练集里出现次数少模型没学好。解决是把 part2 的白边图充分纳入训练或者预处理时裁掉白边区域只保留纯黑部分。项目正文专门区分 part1 和 part2说明这两类正样本的分布差异需要显式处理。4.4 现象换一台机器跑报 numpy 或 torch 版本不兼容原因是依赖版本没锁。解决是用 Dockerfile 或 requirements.txt 固定版本别用 pip install 不带版本号。torch 和 numpy 的大版本升级经常改 API旧代码直接崩。4.5 现象推理速度慢产线节拍跟不上原因是模型在 CPU 上跑或者输入分辨率太高。解决是导出 ONNX 或 TensorRT 加速或者把 img_size 从 256 降到 128。平坦图降分辨率对精度影响有限速度提升明显。5. 进阶技巧把误差图变成可交付的缺陷报告5.1 用连通域分析给缺陷分类和计数阈值化得到 mask 后用 OpenCV 的 connectedComponentsWithStats 做连通域分析能拿到每个缺陷的面积、外接矩形、质心。结合形态特征可以粗分类线状缺陷长宽比大块状缺陷面积大且紧凑凸起和缺口看边界曲率。num_labels, labels, stats, centroids cv2.connectedComponentsWithStats(mask, connectivity8) for i in range(1, num_labels): area stats[i, cv2.CC_STAT_AREA] x, y, w, h stats[i, cv2.CC_STAT_LEFT], stats[i, cv2.CC_STAT_TOP], stats[i, cv2.CC_STAT_WIDTH], stats[i, cv2.CC_STAT_HEIGHT] # 过滤小面积噪声 if area 20: continue aspect_ratio max(w, h) / max(min(w, h), 1) defect_type 线状 if aspect_ratio 3 else 块状 print(f缺陷 {i}: 类型{defect_type}, 面积{area}, 位置({x},{y}))这段代码遍历连通域过滤小面积噪声按长宽比粗分线状和块状。area 阈值 20 是经验值按实际分辨率调。aspect_ratio 大于 3 判线状否则块状。凸起和缺口需要更细的轮廓分析可以用轮廓近似或曲率计算。5.2 用 SSIM 替代 MSE 提升结构缺陷敏感度MSE 对像素级差异敏感但对结构变化不敏感。SSIM 衡量结构相似度对线状、缺口这类结构缺陷更灵敏。scikit-image 的 structural_similarity 可以直接用。from skimage.metrics import structural_similarity as ssim def ssim_error_map(img, recon): # SSIM 返回相似度图和总分 score, sim_map ssim(img, recon, fullTrue, data_range1.0) # 相似度取反得到误差图 error_map 1 - sim_map return error_mapSSIM 的 fullTrue 返回逐像素相似度图1 减去它得到误差图。data_range1.0 对应归一化输入。SSIM 误差图比 MSE 更平滑阈值化后 mask 更完整但计算量稍大。实际可以 MSE 和 SSIM 加权融合兼顾像素和结构。5.3 验证方法用正常样本统计误检率模型上线前必须验证误检率。拿一批没参与训练的正常图跑推理统计有多少像素被误判为缺陷。误检率高于 1% 就要调阈值或加后处理。验证项方法合格标准误检率正常图推理统计误判像素占比 1%漏检率已知缺陷图推理统计未检出缺陷占比 5%推理耗时单张图端到端计时满足产线节拍mask 完整度人工核对缺陷区域是否连片无明显断裂验证要在独立测试集上做不能拿训练集自测。TC_image 里正常和负样本混在一起正好用来做这个验证。先跑正常样本统误检再跑负样本统漏检两个指标都达标才算可用。5.4 我踩过的坑阈值调参别在训练集上做刚做这个项目时我在训练集上调阈值看着 mask 很漂亮换到 TC_image 上误检一片。后来才明白训练集上的误差分布和测试集不一样阈值必须在独立正常样本上统计。从那以后我每次调阈值都强制走一遍独立验证集先跑正常图看误检再跑缺陷图看漏检两个都过了才敢往下走。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?