简介:这份资源面向计算机、人工智能、自动化等专业的在校学生、教师及企业开发者,提供一套基于Python实现的工具零件表面高精度缺陷检测源码,可用于毕业设计、课程设计、项目立项演示或算法进阶学习。压缩包共8个文件,约10KB,以3个Python脚本为核心,分别承担模型训练与测试推理任务,另含requirements.txt依赖清单、setup.py与setup.cfg打包配置、MANIFEST.in清单及.gitignore版本管理文件,结构精简、便于快速部署与二次开发。项目代码均经测试运行成功后才上传,功能完整可用,适合基础尚可的读者在此基础上修改扩展,实现更多检测功能。目前已有386人学习关注,可作为缺陷检测方向入门与实战借鉴的轻量参考。
1. 从一堆零件照片到可复现的缺陷检测:这套 Python 源码到底能干什么
工厂质检线上,一个轴承套圈从相机下经过,表面有没有划痕、凹坑、锈斑,靠人眼盯着屏幕看,一天下来眼睛发花,漏检率还压不住。这套「基于 Python 实现的各种工具零件表面高精度缺陷检测源码」要解决的,就是把这个判断过程交给模型:输入一张零件表面图,输出它是否合格、缺陷落在哪个区域。压缩包里给的不是一篇论文,而是一套能跑起来的工程骨架——train.py、test.py、setup.py、requirements.txt、setup.cfg、MANIFEST.in、.gitignore、save-loss.txt,从环境声明到训练入口到测试脚本都在,适合计算机、人工智能、自动化方向的在校学生做毕设或课程设计,也适合刚转进工业视觉方向的工程师拿来当第一个可改的基线。它不承诺你下载完就 SOTA,但它把「数据怎么进、模型怎么训、指标怎么存、结果怎么测」这条链路铺平了,你缺的是自己的零件图和调参耐心。
2. 拆开压缩包先看结构:train.py 与 test.py 的分工逻辑
2.1 文件清单背后的工程约定
拿到一个源码包,我习惯先不急着pip install,而是把目录树和每个文件的职责过一遍。这套资源的文件构成很典型,属于「最小可运行工程」的配置:
| 文件 | 作用 | 你需要关注的点 |
|---|---|---|
train.py | 训练主入口 | 数据加载、模型定义、损失、优化器、epoch 循环都在这 |
test.py | 推理/评估入口 | 加载权重、跑验证集、输出指标或可视化 |
requirements.txt | 依赖声明 | 决定你装哪个版本的 torch、opencv |
setup.py+setup.cfg | 打包配置 | 说明它按可安装包组织,不是随手脚本 |
MANIFEST.in | 打包附带文件声明 | 影响非 .py 资源是否被打进去 |
save-loss.txt | 训练损失记录 | 用来判断训练有没有收敛、有没有震荡 |
.gitignore | 版本忽略 | 侧面反映作者本地跑过哪些产物 |
save-loss.txt这个文件值得单独说一句。很多学生交上来的代码只有.py,训练过程全靠print,跑完什么都不剩。这里作者把 loss 落盘成文本,意味着你可以拿它画曲线、对比不同超参下的收敛速度。常见做法是用它配合 matplotlib 画 train loss 曲线,判断是否过拟合。
2.2 训练脚本的典型骨架与参数落点
train.py是整套代码的心脏。工业缺陷检测的训练循环,结构上和通用图像分类大同小异,差别在数据增强策略和类别不平衡处理。下面是我按这类工程常见写法还原的骨架,你对照自己的train.py看结构是否一致:
import torch import torch.nn as nn from torch.utils.data import DataLoader from dataset import DefectDataset # 数据加载类,按实际文件名替换 from model import DefectNet # 模型定义,按实际文件名替换 # 超参集中管理,方便复现 EPOCHS = 50 BATCH_SIZE = 16 LR = 1e-3 DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") train_set = DefectDataset(root="data/train", mode="train") val_set = DefectDataset(root="data/val", mode="val") train_loader = DataLoader(train_set, batch_size=BATCH_SIZE, shuffle=True, num_workers=4) val_loader = DataLoader(val_set, batch_size=BATCH_SIZE, shuffle=False, num_workers=4) model = DefectNet(num_classes=2).to(DEVICE) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=LR) with open("save-loss.txt", "a") as f: for epoch in range(EPOCHS): model.train() running_loss = 0.0 for imgs, labels in train_loader: imgs, labels = imgs.to(DEVICE), labels.to(DEVICE) optimizer.zero_grad() outputs = model(imgs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() avg_loss = running_loss / len(train_loader) f.write(f"epoch={epoch}, loss={avg_loss:.6f}\n") # 落盘,供后续画曲线 print(f"epoch {epoch} loss {avg_loss:.4f}")逻辑说明:DefectDataset负责把零件图读进来并做增强,DefectNet是网络主体,训练循环把每个 batch 的 loss 累加后求平均写进save-loss.txt。参数说明:BATCH_SIZE受显存限制,工业图分辨率高时通常只能开到 8 或 16;LR用 1e-3 是 Adam 的常规起点,若 loss 震荡就降到 1e-4;num_workers在 Windows 上设 0 更稳,Linux 上设 4 能提速。save-loss.txt用追加模式"a",多次训练会累积,想干净对比就每次训练前清空或改名。
2.3 测试脚本怎么读指标
test.py的职责是加载训练好的权重,在验证集或测试集上跑一遍,输出准确率、混淆矩阵或缺陷区域可视化。工业场景里,单纯看准确率会被类别不平衡骗——如果 95% 的零件都是合格品,模型全判合格也有 95% 准确率,但漏检的缺陷件才是要命的。所以看test.py时重点确认它有没有输出召回率(Recall)和精确率(Precision),尤其是缺陷类的召回。常见做法是加一段:
from sklearn.metrics import classification_report # 收集所有预测和标签后 print(classification_report(all_labels, all_preds, target_names=["ok", "defect"]))这样你能看到缺陷类的召回是多少。如果缺陷召回低于 0.9,说明漏检严重,得回头查数据增强够不够、正负样本比例是否失衡。
3. 环境搭建与依赖安装:requirements.txt 里的版本坑
3.1 用虚拟环境隔离,别污染全局
这类源码包最容易翻车的地方不是模型,是环境。requirements.txt里如果写的是torch、opencv-python这种不带版本号的依赖,你直接pip install -r很可能装到最新版,而最新版和作者当时的 API 对不上,报错报到怀疑人生。血泪经验是:先建虚拟环境,再装依赖。
# 创建并激活虚拟环境(Windows 与 Linux 命令不同) python -m venv venv # Windows venv\Scripts\activate # Linux / macOS source venv/bin/activate # 升级 pip 后安装依赖 python -m pip install --upgrade pip pip install -r requirements.txt逻辑说明:虚拟环境把项目依赖和系统 Python 隔开,避免不同项目互相打架。参数说明:python -m venv venv里的第二个venv是目录名,可以改成.venv。如果requirements.txt没锁版本,装完后用pip freeze > requirements-lock.txt把实际版本记下来,下次复现就稳了。
3.2 版本对齐:torch 与 CUDA 的匹配
深度学习项目最玄学的一环就是 torch 和显卡驱动的匹配。requirements.txt里如果只写torch,pip 默认装 CPU 版或和你的 CUDA 不匹配的版本。正确做法是先确认显卡驱动支持的 CUDA 版本,再去装对应 wheel。
# 查看显卡和驱动支持的 CUDA 版本 nvidia-smi # 按官方索引装指定 CUDA 版本的 torch(示例,按实际版本替换) pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118逻辑说明:nvidia-smi右上角显示的 CUDA Version 是驱动支持的上限,装 torch 时不能超过它。参数说明:cu118代表 CUDA 11.8,换成cu121就是 12.1。装完在 Python 里跑import torch; print(torch.cuda.is_available()),返回True才算成功。如果返回False,要么是装了 CPU 版,要么是驱动太旧,别急着改代码,先解决环境。
3.3 opencv 与图像读取的细节
缺陷检测离不开图像处理,opencv-python几乎是标配。注意两个点:一是opencv-python和opencv-contrib-python别同时装,会冲突;二是 OpenCV 读进来是 BGR 通道,而 torchvision 的 transforms 和预训练模型通常按 RGB 处理,通道顺序错了模型精度会莫名其妙下降。
import cv2 img = cv2.imread("part_001.jpg") # 默认 BGR img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 转 RGB 再送模型逻辑说明:cv2.imread第二个参数默认是IMREAD_COLOR,读成三通道 BGR。参数说明:如果图是灰度缺陷图,用cv2.IMREAD_GRAYSCALE读成单通道,再在 dataset 里expand成三通道喂给预训练网络。这一步不做,模型看到的颜色是反的,训练 loss 可能降得很慢,属于典型的隐性坑。
4. 数据组织与训练调参:让 save-loss.txt 真正有用
4.1 数据集目录怎么摆
源码里的DefectDataset通常按ImageFolder风格组织,也就是按类别分文件夹。工业零件缺陷检测常见两类:合格(ok)和缺陷(defect),缺陷里还可能细分划痕、凹坑、锈斑。目录结构建议这样:
data/ ├── train/ │ ├── ok/ │ └── defect/ └── val/ ├── ok/ └── defect/逻辑说明:ImageFolder会自动按子文件夹名生成类别标签,省去手写 label 映射。参数说明:train 和 val 的类别文件夹名必须完全一致,否则标签对不上。如果缺陷类型多,可以按defect_scratch、defect_pit细分,但类别越多,每类样本越少,小样本类别容易训崩,新手建议先做二分类跑通再细分。
4.2 数据增强:工业图的增强不能照搬自然图像
自然图像分类常用的随机裁剪、翻转、色彩抖动,放到工业缺陷图上要谨慎。零件表面的划痕是有方向性的,水平翻转可能把一条真实划痕变成不存在的方向;色彩抖动可能把锈斑的颜色特征抹掉。我一般会这样配增强:
from torchvision import transforms train_tf = transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomHorizontalFlip(p=0.3), # 降低概率,缺陷有方向性 transforms.RandomRotation(10), # 小角度旋转,模拟摆放偏差 transforms.ToTensor(), transforms.Normalize(mean=[0.5]*3, std=[0.5]*3), ])逻辑说明:Resize统一尺寸,RandomHorizontalFlip概率压到 0.3 减少方向性破坏,RandomRotation限制在 10 度内。参数说明:Normalize的均值和方差如果用的是预训练模型,要换成 ImageNet 的mean=[0.485,0.456,0.406]、std=[0.229,0.224,0.225],否则预训练权重的优势发挥不出来。这一步是很多新手忽略的,导致微调效果还不如从头训。
4.3 用 save-loss.txt 判断收敛与过拟合
训练跑起来后,save-loss.txt就是你的黑匣子。把它读进来画曲线,比盯着终端刷屏靠谱得多。
import matplotlib.pyplot as plt epochs, losses = [], [] with open("save-loss.txt") as f: for line in f: parts = dict(p.split("=") for p in line.strip().split(", ")) epochs.append(int(parts["epoch"])) losses.append(float(parts["loss"])) plt.plot(epochs, losses) plt.xlabel("epoch"); plt.ylabel("loss"); plt.title("train loss") plt.savefig("loss_curve.png")逻辑说明:逐行解析epoch=x, loss=y格式,画成曲线。参数说明:如果 loss 前期快速下降后长期平躺,说明收敛了,可以停;如果 loss 上下剧烈震荡,多半是学习率太大或 batch size 太小;如果 train loss 一直降但验证指标变差,就是过拟合,该加数据或加正则。save-loss.txt只记了 train loss,严谨做法是在训练循环里同时算 val loss 一起落盘,两条曲线对比才看得出过拟合。
5. 避坑与排查:这套源码最容易卡住的五个地方
5.1 现象:运行 train.py 报 ModuleNotFoundError
原因:requirements.txt没装全,或者你用的 Python 解释器和虚拟环境不是同一个。VS Code 里尤其常见,右下角解释器还指着系统 Python。
解决:确认虚拟环境已激活,pip list看依赖在不在;VS Code 里Ctrl+Shift+P选Python: Select Interpreter,指到venv里的 python。装依赖时如果某个包编译失败,优先找有没有预编译 wheel,别硬编译。
5.2 现象:loss 一直是 nan 或者不下降
原因:学习率太大、输入没归一化、标签越界,或者数据里有损坏图片。工业数据集常混进读不出来的图,cv2.imread返回None,送进网络就炸。
解决:先把学习率降到 1e-4 试;检查Normalize有没有加;在 dataset 的__getitem__里加一句if img is None: return self.__getitem__((idx+1) % len(self))跳过坏图。nan 出现时用torch.autograd.set_detect_anomaly(True)定位是哪一层出的问题。
5.3 现象:显存不够,CUDA out of memory
原因:工业图分辨率高,batch size 开太大,或者没释放中间变量。
解决:先把BATCH_SIZE降到 4 或 2;用torch.cuda.empty_cache()清理缓存;训练循环里用with torch.no_grad()包住验证部分。如果还不行,把输入尺寸从 512 降到 256,精度会掉一点但能跑起来,先跑通再优化。
5.4 现象:测试准确率很高但实际漏检严重
原因:正负样本极度不平衡,模型学会了全判合格。这是工业缺陷检测最典型的翻车方式。
解决:看test.py有没有输出缺陷类召回率,没有就自己加classification_report;训练时给缺陷类加权,nn.CrossEntropyLoss(weight=torch.tensor([1.0, 5.0]));或者用重采样让每个 batch 里缺陷样本占比不低于三成。
5.5 现象:换了自己的数据集后 test.py 报维度不匹配
原因:模型最后一层全连接输出类别数写死成 2,你的数据集类别数不一样;或者输入通道数对不上(灰度图 vs 三通道)。
解决:找到模型定义里num_classes参数,改成你的类别数;灰度图在 dataset 里img = img.repeat(3,1,1)扩成三通道。改完重新训练,别拿旧权重直接测。
6. 从跑通到改出自己东西:迁移学习与指标验证的实操
跑通原版只是起点,真正让这套源码为你所用,是把它改成能检测你自己零件的东西。最省力的路径是迁移学习:拿 ImageNet 预训练的主干网络,换掉最后的分类头,用你自己的数据微调。下面是我常用的改法,以 ResNet 为例:
import torch.nn as nn from torchvision import models def build_model(num_classes=2, freeze_backbone=True): model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) if freeze_backbone: for p in model.parameters(): p.requires_grad = False # 冻结主干,只训分类头 model.fc = nn.Linear(model.fc.in_features, num_classes) # 换头 return model逻辑说明:weights=...IMAGENET1K_V1加载预训练权重,freeze_backbone控制是否冻结主干。参数说明:数据量少于一千张时,先冻结主干只训分类头,训几个 epoch 后再解冻全部微调,学习率调小到 1e-4;数据量上万张可以直接全量微调。num_classes按你的缺陷类别数改,二分类就是 2。
改完模型,验证环节不能只看一个准确率数字。我一般会强制走一遍这套检查:先看混淆矩阵,确认缺陷类有没有被大量误判成合格;再看缺陷类召回率,低于 0.9 就不交付;最后拿几张训练集里没出现过的真实零件图做盲测,看模型在没见过的样本上稳不稳。这一步是后悔药——等上线了才发现漏检,回头查数据分布就晚了。
还有一个容易被忽略的技巧:把test.py的输出从单纯打印指标,改成同时保存缺陷区域的可视化图。做法是把模型预测为缺陷的图,叠加一个热力图或框,存到output/目录。质检场景里,光告诉工人「这张不合格」没用,得告诉他「缺陷在这」,人家才信你。常见做法是用 Grad-CAM 生成热力图,或者简单点,把原图和预测标签一起存成对比图。这样你拿去答辩或者给产线看,说服力完全不一样。
从那以后我每次拿到一个新的缺陷检测源码包,都强制先跑通原版、确认save-loss.txt有正常曲线、再用自己的小数据集替换跑一遍,三步都过了才敢动模型结构。这套流程帮我省下过无数次「改了半天发现是环境问题」的时间。希望帮到你。
本文还有配套的精品资源,点击获取