简介:面向广东工业智造大赛布匹瑕疵检测复赛的完整Python工程,包含源码、文档说明与赛题数据,可帮助计算机视觉、人工智能方向的在校学生或竞赛选手快速复现检测流程,也适用于毕业设计、课程设计的二次开发。包内主要文件类型包括311个Python脚本、170个pyc编译文件,以及用于模型加速的C++/CUDA扩展算子(如deform_conv、roi_align、nms等),另有Markdown说明文档、JSON配置、Shell脚本和Dockerfile,覆盖环境搭建、数据预处理、模型训练与推理部署等常见环节。整个压缩包共543个文件,大小约11.48MB,目录结构简洁,便于按模块查阅。项目代码均经过运行测试,平均评审得分96分,下载后可参考README快速上手;内容涵盖布匹瑕疵数据集的加载、模型训练、验证及推理全流程,包含NMS、可变形卷积、焦点损失等关键模块的实现参考,便于深入理解检测算法细节。目前已有168人学习使用,是算法复现与工程实战的实用素材。
1. 布匹瑕疵检测项目:从算子文件反推模型结构
拿到这包复赛源码,我第一反应不是看 train.py,而是翻 src 目录。soft_nms_cpu.cpp、deform_conv_cuda.cu、roi_align_cuda.cu、sigmoid_focal_loss.cu 这一串文件说明两件事:项目不是纯 Python,训练前要重新编译 C++/CUDA 算子;模型结构里同时出现了 Deformable Conv、ROIAlign、Focal Loss,属于比较典型的强检测器组合。复赛主题是布匹瑕疵检测,背景纹理复杂、目标小且密集,这类配置比普通 Faster R-CNN 更扛得住。这个包适合做工业视觉算法、要自己调参的工程师,也适合拿去做课程设计和毕设,但要先把环境跑通再谈改模型。
2. 环境准备与自定义算子编译:版本匹配是第一道坎
源码里的.cpp/.cu文件最终会编成.so供 Python 调用,编译失败的概率远高于模型训练本身。我见过最多的问题出在 PyTorch、CUDA、mmcv 三者版本错位,而不是代码写错。先列一组我自己验证过能跑通的组合。
2.1 依赖版本与 CUDA 匹配
以下组合适合 20~30G 数据规模的复赛,显存 11G 也能撑住训练:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| CUDA Toolkit | 10.2 / 11.3 | 10.2 稳定,11.3 对 Ampere 显卡更友好 |
| PyTorch | 1.7.1 / 1.10.2 | 与 mmcv-full 版本强绑定 |
| mmcv-full | 1.3.17 / 1.5.3 | 必须预编译对应 CUDA 与 PyTorch |
| GCC | 7.5 / 9.4 | 过高或过低都会在编译算子时报错 |
安装顺序上,先把 conda 环境建好,再装 torch,最后装 mmcv-full。顺序反了会出现 torch 的 include 目录找不到,torch/extension.h直接报错。
conda create -n fabric python=3.8 -y conda activate fabric # pytorch 版本必须与后续 mmcv 匹配 pip install torch==1.10.2 torchvision==0.11.2 --extra-index-url https://download.pytorch.org/whl/cu113 # 用 openmim 安装对应版本的 mmcv-full mim install mmcv-full==1.5.3说明:--extra-index-url指定 cu113 的 wheel 源,避免装到 CPU 版;mim是 openmim 自带的安装器,它会先读取当前 torch 版本,再校验 mmcv 的预编译包是否匹配。如果你已经装了系统级 opencv,这里最好用pip uninstall opencv-python避免 mmcv 编译时拿到冲突的 include。
之后进入源码目录编译自定义算子:
cd /path/to/fabric_det python setup.py develop这里develop是软链接安装,改 Python 代码不用重新 install;第一次编译大概 10~20 分钟,视 CPU 和 GPU 卡型号而定。编译结束要检查build/lib.linux-x86_64-3.8里出现*.so文件,训练时不再报Not compiled才算通过。
2.2 编译失败的典型报错与处置
第一类报错是fatal error: torch/extension.h: No such file or directory,说明当前解释器所用的 PyTorch 不在PYTHONPATH里,或 conda 环境创建后没有重新激活。执行python -c "import torch; print(torch.__version__)"能输出版本就基本排除。
第二类报错是nvcc fatal : Unsupported gpu architecture 'compute_86',常见于新显卡 + 旧 torch/nvcc。编译前手动声明架构:
export TORCH_CUDA_ARCH_LIST="7.5;8.0" python setup.py develop两个参数分别对应 Turing 和 Ampere 的算力,compute_86是 RTX 30 系列的代号,如果只用 RTX 3090,就写8.6。设这个环境变量还能缩短编译时间,因为编译器不再为所有架构生成中间码。
第三类报错是运行时undefined symbol,例如_ZN2at6detail19getBackendDeviceType...。这说明 mmcv-full 的编译版本和当前 torch 不一致,回退到与 torch 对应的 mmcv 版本即可。注意这里不解决 mmdet 本身的 Python 错误,那些要在代码调试阶段看。
提示:源码包里如果已经带
setup.py和version.py,不要随便改__init__.py里的get_compiler_version,很多编译失败是自己改了版本号导致的。
3. 数据组织与标签格式:从原始布匹图到 COCO 标注
复赛数据一般分 train 和 val 两个目录,标注文件可能是 txt、xml 或 json。源码的配置是按 COCO 读取的,所以第一步是把所有标注统一成 COCO json。如果跳过这一步直接拿 txt 喂给模型,训练时会因为字段缺失直接中断。
3.1 原始数据目录结构
一个常见的目录布局是:
data/ ├── train/ │ ├── img_001.jpg │ ├── img_002.jpg │ └── ... ├── val/ │ ├── img_101.jpg │ └── ... └── annotations/ ├── label_train.txt └── label_val.txtlabel_train.txt里每行通常是:图片名 左边x 上边y 右边x 下边y 类别id。注意这里的坐标是像素坐标,而 COCO 需要的是[x, y, width, height],右下角坐标必须换算成宽高,不能直接填进去。
3.2 标注格式转换脚本
下面的脚本把 txt 转换成 COCO 格式,这里以三类瑕疵为例:
import json, os from PIL import Image from tqdm import tqdm def convert_txt_to_coco(txt_path, image_root, out_json): coco = { "images": [], "annotations": [], "categories": [ {"id": 1, "name": "hole"}, {"id": 2, "name": "stain"}, {"id": 3, "name": "broken_warp"} ] } ann_id = 1 for line in tqdm(open(txt_path)): parts = line.strip().split() img_name, x1, y1, x2, y2, cls = parts img_path = os.path.join(image_root, img_name) img = Image.open(img_path) w, h = img.size image_id = len(coco["images"]) coco["images"].append({ "id": image_id, "file_name": img_name, "width": w, "height": h }) box = [int(x1), int(y1), int(x2) - int(x1), int(y2) - int(y1)] coco["annotations"].append({ "id": ann_id, "image_id": image_id, "category_id": int(cls), "bbox": box, "area": box[2] * box[3], "iscrowd": 0 }) ann_id += 1 with open(out_json, "w") as f: json.dump(coco, f)这段代码的核心是image_id必须和images列表里的 id 对应,错一位就会导致模型学的框和标注完全对不上。coco 的area字段用于按尺寸分桶统计 mAP,复赛评测如果按小中大目标分开算,这个值错一点,最后差异很大。iscrowd一定要给 0,布匹数据里没有密集人群那种 crowd 标注。
提示:如果原始标注里同一个目标有多个顶点 polygon,先把它转成外接正框,否则不少 mmdet 的 head 不支持 polygon 直接训练。
3.3 布匹长图的滑窗处理而不是缩放
布匹照片经常是长宽比例失衡,比如 4000×1000。直接 resize 到 1333×800,细微的断经和破洞会缩小 3 倍以上,检测器基本学不到。我一般先做滑窗切块再训练,推理阶段同样滑窗,再把结果合并回原图坐标。下表给出了我常用的几组切图参数:
| 滑窗尺寸 | 重叠率 | 适用场景 |
|---|---|---|
| 1024×1024 | 0.2 | 小瑕疵密集,尽量不跨窗 |
| 1333×800 | 0.1 | 常规缺陷,显存不够时 |
| 2000×500 | 0.0 | 布匹长条形状,需要很快推理 |
切块时要同步平移每个框的坐标。最简单的方式是以目标的中心点为准,中心落在哪个窗就把框归到哪个窗。如果目标横跨两个窗,只保留中心所在窗,避免同一目标在多个窗口里被重复计为多个真值。
可以用 PIL 直接裁剪:
from PIL import Image img = Image.open("data/train/img_001.jpg") W, H = img.size win_w, win_h = 1024, 1024 stride_x, stride_y = int(win_w * 0.8), int(win_h * 0.8) tiles = [] for y in range(0, H - win_h + 1, stride_y): for x in range(0, W - win_w + 1, stride_x): tiles.append(img.crop((x, y, x + win_w, y + win_h)))stride等于窗口宽度的 80%,意味着重叠 20%,这个值能有效避免瑕疵正好落在窗口边缘被截断。切完图后,对应的 coco 标注也应做同样的偏移裁剪,窗口之外的框直接丢弃。重叠率别设太高,否则训练时正样本重复太多,收敛慢;低于 10% 又会漏掉边缘目标。
4. 模型训练与参数调整:anchor、Focal Loss 与正负样本
模型结构从算子文件能反推出来:backbone 用了 ResNet50,且带了 DCN;neck 是 FPN;proposal 靠 RPN 生成;分类分支用 Focal Loss 而不是常规 CrossEntropy。这个组合在布匹瑕疵场景下比原版 Faster R-CNN 好的原因是布面瑕疵往往形变不规则、类别不平衡严重,DCN 能拉大感受野去适应形状,Focal Loss 能把注意力压在小样本类别上。
4.1 在配置里改 anchor 与 loss
打开模型配置文件,核心片段像这样:
model = dict( type='FasterRCNN', backbone=dict( type='ResNet', depth=50, dcn=dict(type='DCNv2', deformable_groups=1), stage_with_dcn=[False, True, True, True], out_indices=(0, 1, 2, 3)), neck=dict( type='FPN', in_channels=[256, 512, 1024, 2048], out_channels=256, num_outs=5), rpn_head=dict( type='RPNHead', anchor_scales=[4, 8, 16, 32], anchor_ratios=[0.5, 1.0, 2.0], loss_cls=dict(type='CrossEntropyLoss', use_sigmoid=True), loss_bbox=dict(type='SmoothL1Loss', beta=1.0 / 9.0)), roi_head=dict( type='StandardRoIHead', bbox_roi_extractor=dict( type='SingleRoIExtractor', roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=2)), bbox_head=dict( type='Shared2FCBBoxHead', num_classes=3, reg_class_agnostic=False, loss_cls=dict(type='FocalLoss', gamma=2.0, alpha=0.25), loss_bbox=dict(type='SmoothL1Loss', beta=1.0))))关键参数说明:
anchor_scales从 COCO 默认的 [8,16,32] 改成 [4,8,16,32],把最小 anchor 面积降到 4 像素级,否则 20~40 像素的小破洞在 FPN 高层特征上直接消失。stage_with_dcn=[False, True, True, True]前三个 res stage 用 DCN,最后一个不用,因为 dcn 在最后 stage 对高低频纹理的影响不好控。FocalLoss的gamma=2.0会让难例梯度权重变大,alpha=0.25是针对前景比例低时的常用配置。若手里的复赛数据正负样本比更极端,把 alpha 提到 0.5 试试。
4.2 学习率与批次调整
复赛数据量一般不大,几百到几千张图,用 12 个 epoch 足够。下面是经验值:
| 参数 | 推荐值 | 备注 |
|---|---|---|
| base_lr | 0.01(单卡)/ 0.02(双卡) | 卡数变化时 lr 线性调整 |
| weight_decay | 0.0001 | 不需要改动 |
| total_epochs | 12 | 数据少于 500 张时减到 8 |
| batch_size | 4 / 卡 | 16G 显存能跑,但 footprint 大 |
启动训练命令:
python tools/train.py configs/fabric/faster_rcnn_r50_fpn.py \ --work-dir work_dirs/fabric \ --launcher pytorch \ --gpus 2--work-dir是日志与 checkpoint 的保存目录;--launcher pytorch告诉脚本走 DistributedDataParallel;--gpus 2要和配置文件里的dist_params对应,DD 并行时 learning rate 也要按卡数倍增,否则有效 batch 变大而 lr 不变,模型震荡。
4.3 训练失败时先查数据而非模型
我遇到训练 loss 掉不下去的情况,七成是数据,三成是环境。具体表现与排查方向:
loss_cls稳定在 0.7 附近不动:多半是标注 json 的image_id错位,或者所有框都被 filter 掉了。先跑一小批数据,ann_file只有一个样本,看配置文件里samples_per_gpu和 dataloader 是否能读出来。loss_bbox几乎为 0:说明回归分支没收到有效 gt,检查bbox是否出现负数或宽高为 0。- mAP 低但 loss 正常:先不调 anchor,去检查验证集是不是也做了滑窗,如果训练是滑窗、验证是整图,尺度不一致直接掉点。
布匹数据里我个人不建议频繁加随机旋转增强,布面纹理方向性很强,旋转 90 度之后断经和破洞的语义会变,模型容易学到错误的方向先验。水平翻转可以保留,但垂直翻转要看具体瑕疵类型。
5. 推理验证与本地可视化:用 Soft NMS 找回密集瑕疵
这个项目最终要提交到复赛平台,所以验证方式必须跟训练脱开。先把模型在验证集上跑一遍,拿到标准评测结果,再针对密集难样本调后处理。
5.1 提交前先按瑕疵尺寸分桶看 AP
用官方脚本测试并保存预测结果:
python tools/test.py configs/fabric/faster_rcnn_r50_fpn.py \ work_dirs/fabric/latest.pth \ --eval bbox \ --out work_dirs/fabric/pred.pkl--eval bbox会输出 mAP、mAP50、mAP75 以及 small/medium/large 三个尺寸的 AP;--out把预测结果保存为 pkl。布匹瑕疵大半落在 small 桶里,如果 small 的 AP 低于 medium 一截,就说明 anchor 或输入分辨率还有问题,此时改模型结构不如先回头查滑窗重叠率。
5.2 把后处理换成 soft_nms
复赛中密集排布的破洞、重经、污渍,两两之间 IOU 经常超过 0.5,普通 NMS 会把相邻的置信度低的那一个直接删掉,漏检明显。项目源码里带了soft_nms_cpu.cpp,说明设计者本来就打算启用 Soft NMS。在测试配置里改写测试阶段的后处理:
model = dict( test_cfg=dict( rpn=dict(nms_across_levels=False, nms_pre=1000, nms_post=1000), rcnn=dict( score_thr=0.05, nms=dict(type='soft_nms', iou_threshold=0.5, sigma=0.6), max_per_img=100) ) )sigma是高斯惩罚系数,值越小对邻接框的抑制越温和;iou_threshold=0.5表示超过这个交集就算“邻接框”。通常sigma=0.5~0.7适合布匹这种密集小目标,别用太大,否则会引入大量重复框。score_thr从默认的 0.05 调成 0.1 可以少输出一些背景噪声,但会牺牲部分 recall,需要在提交前用验证集试一次。
本文还有配套的精品资源,点击获取