桥梁裂缝数据集处理:COCO转YOLO格式与训练避坑指南

发布时间:2026/10/4 14:07:36
桥梁裂缝数据集处理:COCO转YOLO格式与训练避坑指南 简介一份面向桥梁结构健康监测与计算机视觉研究者的图像分割数据集采用COCO标注格式聚焦混凝土桥梁表面裂缝缺陷的精准定位与分割。数据集包含约4500张训练图像和200张验证图像覆盖不同光照、角度、拍摄距离及复杂背景下的裂缝形态可为深度学习模型提供充足的正负样本可直接用于训练或评估U-Net、DeepLab、Mask R-CNN等主流分割网络有效解决桥梁巡检中裂缝样本稀缺、人工标注成本高等痛点。压缩包内共2000个文件其中1998张jpg图像配合2个json标注文件整体体积约557.74MB标注文件采用标准COCO结构包含categories、images、annotations等字段便于用户借助现成工具链完成解析与训练。资源发布以来已有317人学习下载适合高校实验室、工程检测单位及算法工程师复现实验、调整模型或开展桥梁病害自动化识别研究。1. 桥梁裂缝缺陷数据集.zip解压后先别急着训模型标注格式决定后续一切拿到一个“桥梁裂缝缺陷数据集.zip”大多数人的第一反应是解压、看文件夹、把图片丢进训练脚本。我见过太多人在这一步翻车——花了半天把数据塞进YOLO结果发现标注是COCO格式类别id对不上、segmentation没处理、坐标归一化算错训练出来的模型把整座桥都画满框。这个zip包背后是一套完整的COCO标注体系images、annotations、categories三个字段把图像、裂缝轮廓和类别绑定在一起。下面按从解压到训练再到评估的顺序把这条链路上的每一步拆开讲适合做结构健康监测、无人机巡检和路面病害检测的工程师照着复现——尤其是那些刚拿到数据包、不知道从哪下手的团队。2. COCO标注格式拆解读懂json里的三个核心字段才能谈转换2.1 先看文件树和顶层字段别急着跑代码拿到一个裂缝数据集的zip文件解压之后先别急着写训练脚本。我习惯先扫一眼目录结构确认图像和标注文件的组织方式。一个规范的COCO格式裂缝数据集目录结构通常长这样bridge_crack_dataset/ ├── images/ │ ├── train/ # 训练图像 │ └── val/ # 验证图像 └── annotations/ ├── instances_train.json └── instances_val.json实际的目录结构可能千奇百怪有些把全部图片放在一个目录里json命名也不统一。真正重要的是json内部的数据结构。我习惯用几十行脚本先把标注文件读进来打印顶层字段再决定后续操作。import json with open(annotations/instances_train.json, r, encodingutf-8) as f: coco json.load(f) print(顶层字段:, list(coco.keys())) print(图像记录数:, len(coco[images])) print(标注记录数:, len(coco[annotations])) print(类别数:, len(coco[categories]))COCO格式的json顶层固定有五个字段info、licenses、images、annotations、categories。info和licenses是描述性元数据跟训练没有直接关系可以暂时不管。真正决定模型上限的是images、annotations和categories这三个字段。images里的每个元素是一张图的元信息包括id、file_name、width、heightannotations里每个元素是一条目标标注包含id、image_id、category_id、bbox、segmentation、areacategories就是类别表桥梁裂缝数据集一般会定义“横向裂缝”“纵向裂缝”“网状裂缝”等细分类别具体类别名取决于发布者的标注规范。提示先看categories里类别id是否从1开始连续编号。COCO官方约定类别id从1开始但不少自建数据集从0开始整个训练流程里所有类别映射都以这里的id为基准一旦搞错后续全部白训。2.2 segmentation是裂缝检测的核心bbox只是辅助桥梁裂缝检测和普通目标检测有一个显著区别裂缝是细长结构轮廓在图像里占像素比值很小一条裂缝可能跨越几十公分但宽度只有几个像素。如果用bbox代表一条裂缝框里既包含裂缝本身也包含大量混凝土背景。模型学到的特征会被背景严重稀释定位精度自然上不去。COCO格式里segmentation字段弥补了这个短板它保存裂缝的精确轮廓主要有两种编码polygon和RLE。桥梁裂缝数据集里绝大多数采用polygon因为轮廓由人工标注或半自动标注工具生成RLE多数用于分割标注图直接转换过来的数据。你可以先看任意一条标注的具体结构。ann coco[annotations][0] print(标注字段:, list(ann.keys())) print(类别ID:, ann[category_id]) print(bbox:, ann[bbox]) print(segmentation类型:, type(ann[segmentation])) print(area:, ann[area])polygon格式的segmentation是一个二维列表每个子列表是一组坐标点的扁平数组按[x1, y1, x2, y2, ...]排列。注意这是绝对像素坐标不是归一化到[0,1]的比例值转YOLO格式时一定要除以图像宽高。另外area字段在COCO定义里是轮廓包围的像素面积不是bbox宽乘高很多人在这里会混。对细长裂缝来说bbox面积和真实面积可以差几十倍如果转换脚本用bbox面积代替area做过滤条件会误伤大量细长裂缝样本。所以用COCO数据前先搞清area是怎么算出来的。2.3 用可视化确认标注没有错位在写任何转换代码之前我强烈建议花几分钟把标注画出来看一眼。这一步能筛掉后面所有环节的连锁翻车。我见过数据集里有图像被EXIF旋转过但标注坐标没有跟着旋转直接训练出来的模型在推理阶段给出的裂缝框全部偏移到边缘怎么看怎么别扭。可视化脚本不复杂import cv2 import numpy as np import matplotlib.pyplot as plt def draw_coco_annotation(img_path, ann, categories): img cv2.imread(img_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 画bboxCOCO的bbox是[x, y, width, height]左上角加宽高 x, y, w, h [int(v) for v in ann[bbox]] cv2.rectangle(img, (x, y), (x w, y h), (255, 0, 0), 2) # 画segmentation多边形 for seg in ann[segmentation]: pts np.array(seg, dtypenp.float32).reshape(-1, 2) cv2.polylines(img, [pts.astype(np.int32)], True, (0, 255, 0), 2) plt.figure(figsize(12, 8)) plt.imshow(img) plt.title(fCategory: {categories.get(ann[category_id], unknown)}) plt.axis(off) plt.show() img_info coco[images][ann[image_id]] img_path fimages/train/{img_info[file_name]} draw_coco_annotation(img_path, ann, {c[id]: c[name] for c in coco[categories]})segmentation的第一个子列表的坐标数量一定是偶数reshape成(-1, 2)之后表示一组有序的轮廓点。传给cv2.polylines的第二个参数是点数组第三个参数传True表示闭合多边形对裂缝轮廓通常闭合。如果画出来绿色折线和裂缝实际位置对不上最常见的两个原因图像被EXIF方向信息旋转过或者json里记录的width/height和实际图像尺寸不一致。这两个原因在第5章还会展开说。2.4 图像id、标注id和类别id的映射关系COCO格式里存在三套独立的id很多人在这上面翻车。image_id是图像维度的编号annotation的id是标注维度的编号category_id是类别维度的编号。三者不在同一个命名空间不能互换使用。例如不能拿annotation id去找图片也不能拿category id直接当annotation id用。我在处理数据集时习惯先把三套映射关系构建清楚。from collections import defaultdict image_id_to_info {img[id]: img for img in coco[images]} category_id_to_name {cat[id]: cat[name] for cat in coco[categories]} anns_by_image defaultdict(list) for ann in coco[annotations]: anns_by_image[ann[image_id]].append(ann) # 找到有标注的第一张图 first_img_id next(iter(anns_by_image)) img image_id_to_info[first_img_id] anns anns_by_image[first_img_id] print(f图像: {img[file_name]}, 尺寸: {img[width]}x{img[height]}) print(f标注数量: {len(anns)}) for a in anns: print(f ann_id{a[id]}, cat{category_id_to_name.get(a[category_id], ?)})这里的原则是用哪个id当字典key就必须保证这个id在整份数据里指向同一个对象。image_id、annotation id、category_id分别在各自的空间里编顺序号严格独立。如果某个category_id在category_id_to_name里查不到说明categories表和annotations表对不上要么是类别定义漏了要么是标注时用了不存在的类别id。这类问题在自制数据集里比想象中常见尽早发现比训练跑一半报错要好得多。到这里先别急著作转换。先确认每张图平均有多少条标注、类别分布是否均衡。桥梁裂缝数据集的常见问题是横向裂缝远多于纵向裂缝或者只有细裂缝没有宽裂缝这两个分布偏差会在后面直接影响模型泛化和评价指标。3. 解压与数据体检zip完整性检查和标注一致性校验3.1 zip解压不是双击就完事先验EOCD很多数据集zip包是从学术平台、网盘或者内部服务器下载的文件在传输过程中被截断或损坏的情况不算罕见。如果你解压时遇到could not find EOCD或者invalid zip archive: could not find eocd这类报错说明文件尾部的中央目录记录找不到了常见原因是下载不完整也有可能是服务器端文件本身已经损坏。我拿到zip包后的第一件事不是解压而是做完整性校验这一步能帮你省掉后面两小时的排错时间。# Linux/macOS下校验zip完整性 unzip -t bridge_crack_dataset.zip # 或者用Python的zipfile模块 python -c import zipfile; print(zipfile.ZipFile(bridge_crack_dataset.zip).testzip())unzip -t会对zip内每个文件计算CRC校验值所有文件通过会输出ok中途遇到损坏会直接报出文件名。ZipFile.testzip()返回第一个损坏文件的文件名全部正常返回None。如果返回了具体的文件名建议删掉重新下载而不是指望某款修复工具能救回来。网络传输导致的数据损坏往往不是单个字节的问题即便强制修复成功后面读图时也会出现像素花屏这种更隐蔽的问题。另外要注意如果zip包里面还嵌套了zipPython的zipfile在解压嵌套压缩包时不会自动递归处理需要先解压外层再单独处理内层还有的发布者会把图像和标注放在两个zip里这种必须保证两个包都完整只校验其中一个是不行的。3.2 遇到带密码的zip包怎么处理部分桥梁裂缝数据集zip会设置密码通常是发布者为了避免数据集被随意扩散。遇到这种情况最直接也最正当的办法是联系原发布者获取密码。不要花时间在线搜所谓“密码移除”的第三方方案我见过不少同行在这上面栽跟头——下载的破解工具本身就带病毒还有的跑了一整夜也没拆开最后还得回去找发布者。如果你已经知道密码用Python处理非常简单import zipfile # 已知密码时读取zip内容 with zipfile.ZipFile(bridge_crack_dataset.zip) as zf: zf.extractall(path./dataset, pwdbyour_password)注意pwd参数需要字节串而不是字符串密码输错会抛出RuntimeError: Bad password for file。对于不知道密码的情况联系发布者获取是唯一的正道这个环节没有捷径可走。3.3 图像和标注对账数量不一致才是大问题解压完成后不要急着看图片先把三件事对齐图像文件数、json里images列表长度、annotations里image_id的去重数量。这三个数字如果对不上说明数据集内部不一致训练前不处理的话会在训练过程中出现“找不到jpg”或者索引越界这类诡异报错。import os from collections import Counter img_files os.listdir(images/train) print(实际图像文件数:, len(img_files)) json_img_ids set(img[id] for img in coco[images]) print(json图像记录数:, len(json_img_ids)) ann_img_ids set(ann[image_id] for ann in coco[annotations]) print(有标注的图像数:, len(ann_img_ids))三个数字对不上有两种情况。一种是图像文件数大于json记录数说明部分图片根本没进json这些图片可能是负样本也可能是发布者后期加入但没来得及更新标注文件。另一种是json记录数大于实际文件数说明zip包内文件缺失或者file_name带子目录前缀但你漏做了路径拼接。关于file_name再多提醒一句数据集里file_name经常带相对路径前缀比如train/bridge_001.jpg。如果你先用os.path.join拼成images/train/bridge_001.jpg能够正常读图如果file_name只有bridge_001.jpg而train和val的图片全在一个目录里直接拼接就会把两边文件混在一起导致后面找图时张冠李戴。先检查file_name是否带路径分隔符再写拼接逻辑。3.4 统计裂缝宽高比分布决定模型选型桥梁裂缝数据集和通用目标检测数据集有个显著差异裂缝的长宽比极其悬殊。我见过太多项目组拿到数据集就往YOLO里塞训练完看mAP还行但部署时发现模型对又细又长的裂缝完全招架不住。原因很简单没有先做数据分布统计模型选型错了。bbox宽高比是个便宜好用的统计量import numpy as np widths, heights, ratios [], [], [] for ann in coco[annotations]: _, _, w, h ann[bbox] if w 0 and h 0: widths.append(w) heights.append(h) ratios.append(w / h) widths np.array(widths) ratios np.array(ratios) print(bbox宽度分位数: 10%{:.0f}, 50%{:.0f}, 90%{:.0f}.format( np.percentile(widths, 10), np.percentile(widths, 50), np.percentile(widths, 90))) print(宽高比 10 的标注占比: {:.1%}.format((ratios 10).mean()))如果宽高比大于10的标注占比超过一半说明数据集以横向宽幅裂缝为主模型需要更强的长条特征表达能力。如果bbox宽度中位数在50像素以下说明裂缝在整图里占比很小属于小目标检测范畴直接整图训练的效果通常很差。常见做法是先把大图裁剪成若干个patch在patch上做检测或分割推理时再把预测结果映射回原图坐标。这个统计结果也影响模型架构选择YOLOv8这类anchor-free模型对极端宽高比有一定容忍度但仍有上限如果数据里存在大量接近整图宽度的横向裂缝我会优先考虑RTMDet或者带注意力机制的变体。不要盲信某个backbone在COCO上刷了多高的分桥梁裂缝和自然生活照片完全是两个世界。4. 从COCO转到YOLO格式类别映射、坐标归一化和数据集划分4.1 为什么首选转YOLO而不是直接训MMDetection桥梁裂缝落地项目里用YOLO系的工程师最多原因很直接推理速度快部署容易同一套导出流程可以直接落到无人机边缘设备上。但裂缝本身的细长特征又决定了带分割头的模型才能把COCO里的segmentation信息用足YOLOv8-seg这类模型同时具备检测和分割输出是首选。问题在于YOLO系列原生不支持COCO的json必须先把标注转成它的格式。转换脚本里最大的坑有两个类别id重新编号和坐标归一化。下面这个精简脚本处理了两者。4.2 转换脚本COCO转YOLO检测框的最简可用版import json import os from collections import defaultdict def coco_to_yolo(coco_path, output_dir, categories_keepNone): with open(coco_path, r, encodingutf-8) as f: coco json.load(f) os.makedirs(output_dir, exist_okTrue) # 1. 构建旧类别id到新类别id的映射YOLO类别id从0开始 cats coco[categories] id_to_name {cat[id]: cat[name] for cat in cats} if categories_keep is not None: filtered [cat for cat in cats if cat[name] in categories_keep] else: filtered cats old_to_new {cat[id]: idx for idx, cat in enumerate(filtered)} name_to_new {cat[name]: idx for idx, cat in enumerate(filtered)} img_id_to_info {img[id]: img for img in coco[images]} # 2. 按图像分组只保留需要参与训练的类别 anns_by_image defaultdict(list) for ann in coco[annotations]: if ann[category_id] in old_to_new: anns_by_image[ann[image_id]].append(ann) # 3. 逐张图像生成txt标签 for img_id, anns in anns_by_image.items(): img img_id_to_info[img_id] w, h img[width], img[height] if w 0 or h 0: continue base os.path.splitext(os.path.basename(img[file_name]))[0] txt_path os.path.join(output_dir, base .txt) lines [] for ann in anns: new_id old_to_new[ann[category_id]] x, y, bw, bh ann[bbox] # YOLO格式类别 归一化中心坐标和宽高注意bbox是左上角x宽高 cx (x bw / 2) / w cy (y bh / 2) / h nw bw / w nh bh / h # 越界保护边缘标注常见 cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) nw min(max(nw, 0.0), 1.0) nh min(max(nh, 0.0), 1.0) lines.append(f{new_id} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines) \n) # 4. 生成classes.txt顺序必须和上面重新编号一致 with open(os.path.join(output_dir, classes.txt), w, encodingutf-8) as f: for cat in filtered: f.write(cat[name] \n) print(f完成: {len(anns_by_image)} 张图像类别映射: {name_to_new})这段脚本有几个关键点。第一类别id必须重新编号成从0开始的连续整数直接沿用COCO的id会错位或者多出空类别。第二归一化必须除以json里记录的width和height而不是运行时读图得到的尺寸两者在旋转图像场景下可能不一致。第三bbox坐标在COCO里是左上角x、y加宽高YOLO需要的是中心点坐标加宽高转换时记得加bw / 2。我没有在脚本里写太多防御分支因为裂缝标注常见贴边的情况越界保护那四行min-max大概率会用到。如果你要转的是YOLOv8-seg每行标签的格式是“类别id 归一化后的多边形顶点”def coco_seg_to_yolo_seg(seg, img_w, img_h): 把COCO polygon转成YOLO分割标签格式 coords [] for polygon in seg: # polygon是扁平坐标列表 for i in range(0, len(polygon), 2): coords.append(polygon[i] / img_w) coords.append(polygon[i 1] / img_h) return coords注意转seg时要保留polygon的顶点顺序和闭合性不要自己做过多的坐标平滑。聚类算法在超点上有时会丢顶点导致轮廓变形裂缝的锯齿边缘是重要特征保持原样最稳妥。4.3 数据集划分train、val、test别混在一起很多桥梁裂缝数据集的zip包已经划分好了train和val但test很少见。我习惯从val里再切出一小部分作为最终评估的hold-out集合因为验证集在训练过程中被反复用来挑模型存在选型偏差用同一批数据评估最终模型会高估精度。切分时按图像id而不是按标注id这是底线。import random import shutil import os random.seed(42) # 从val集中随机抽20%作为test val_anns json.load(open(annotations/instances_val.json, encodingutf-8)) val_img_ids [img[id] for img in val_anns[images]] random.shuffle(val_img_ids) test_count int(len(val_img_ids) * 0.2) test_ids set(val_img_ids[:test_count]) # 移动对应图像到test目录 for img in val_anns[images]: if img[id] in test_ids: src os.path.join(images/val, img[file_name]) dst os.path.join(images/test, img[file_name]) os.makedirs(os.path.dirname(dst), exist_okTrue) shutil.move(src, dst)如果原数据集没有划分我通常按0.7、0.2、0.1分配train、val、test。关键点是同一张图的多个裂缝标注必须整体属于同一个集合按image_id划分能天然保证这一点。另外别忽略test里也要有对应的标注json很多人在切分时只移动了图像忘掉同步更新标注文件导致评估时找不到ground truth。4.4 针对裂缝的增强策略旋转要克制标准数据增强对裂缝数据集有一定效果但参数需要调整。桥梁裂缝对旋转扰动非常敏感因为裂缝在图像里的朝向本身承载类别信息——横向裂缝和纵向裂缝是不同类别。如果标注做90度旋转横向裂缝就变成了纵向类别标签却没变模型会学到混乱的特征训练损失可能一直下不去。我一般把旋转限制在±15度以内尽量避免裂缝方向发生本质改变。马赛克增强(mosaic)也要把关拼接后图像分辨率骤变裂缝这类细长目标容易被切碎尺度分布会被打乱。实际项目里我用得比较稳的增强组合是轻度旋转、亮度扰动、对比度扰动和随机裁剪裁剪区域尽量保留完整裂缝。如果你用的是带分割头的模型还可以对segmentation mask做同样的仿射变换保证输入图和mask始终对齐这一步很多框架自带但手工实现时别漏了。5. 桥梁裂缝训练的避坑清单从zip解压到训练的五条踩坑记录5.1 现象解压报错could not find EOCD数据集看似到手实际是坏的拿到数据包执行unzip -t直接报could not find EOCD第一反应是文件坏了。原因是下载不完整最常见其次是FTP传输时用了文本模式导致二进制数据被篡改。解决先看发布页面有没有给MD5校验值给了就本地算一下对比不一致立刻删掉重下别指望修复工具。没给MD5的话重新下载一次并换个下载源比如回原始服务器而不是云盘转存很多时候转存环节就已经坏了。这个坑最浪费时间的点在于你选的别的环节排查半天最后发现文件从源头就不完整。5.2 现象训练出的模型把每个裂缝都框成一个大方块只用了bbox信息没有用segmentation模型输出的检测框整体偏大一条裂缝甚至框了半个桥面。原因桥梁裂缝本就细长bbox宽高比动辄几十目标相对于输入图像尺寸极小模型在多次下采样后丢失了细节。解决优先换YOLOv8-seg或Mask R-CNN这类有分割输出的模型让模型直接回归裂缝轮廓。如果坚持用不带分割头的YOLO先做切图把大图裁成512×512的patch再训练推理时把patch的预测框映射回原图坐标效果远比整图训练好。这个问题容易和mAP不高混在一起实际是两个不同的毛病一个是框不紧一个是压根没检出。5.3 现象loss不降或者看着在降mAP始终为零桥梁裂缝的数据里没有裂缝的桥面图像往往占多数正样本偏少模型很容易把全部预测偏向背景类loss曲线还在缓慢下降但mAP一直为零。原因是类别不均衡正负样本比例悬殊。解决给正样本增加损失权重或者把无裂缝图像单独提出来在训练中作为负样本的hard example补充不能直接丢弃。Focal Loss比普通交叉熵在这种数据上更稳YOLOv8里可以调整类别损失权重如果细分类别之间样本量差异也大建议先合并成“裂缝”一个类训练一个基线模型再用细分类进一步微调。先用二分类痛点最小。5.4 现象图像显示方向正常标注画上去整体偏移手机或无人机拍摄的照片可能带EXIF方向信息图像查看器会自动旋转显示但标注坐标是标注工具在原始像素坐标系里打的于是挪位。原因是两套坐标系不一致。解决在数据预处理阶段统一处理好方向先把所有图像转正并同步更新json里的width、height和所有标注坐标后面才做转换和训练。另一个容易混淆的原因是json里记录的尺寸和实际图像尺寸不匹配比如原图是1920×1080json里误写成1080×1920归一化时坐标就会整体错位。可视化验证那一步如果跳过了这类问题通常要到训练收敛后或部署阶段才暴露排查成本高得多。5.5 现象导出ONNX部署时类别数量对不上推理直接崩溃训练时用了3个类别部署配置里写的是4个或者2个模型输出维度对不上推理端直接报错或输出无意义结果。原因是配置一致性没管好类别映射文件没有跟着模型一起走。解决COCO转换时生成的classes.txt要作为模型包的组成部分一并保存和传递。我在这上面栽过跟头训练时忘了把自定义类别文件拷到部署端结果无人机上跑推理输出头对不上白白浪费了一天排查。建议在训练脚本一开始打印类别数量和类别名部署脚本开头也同样打印一次两边逐项对照没问题再往下走。这类问题属于典型的看代码看不出错看配置才知道差在哪。6. 用COCO评估脚本验证模型从mAP到裂缝宽度的工程化度量6.1 用pycocotools跑标准mAP训练完模型需要一个不掺杂感情的评估标准。pycocotools里的COCOeval能直接读原格式标注json输出mAP、mAR等标准指标。第一步是用模型对验证集做推理把预测结果写成一个和COCO annotations同结构的json。from pycocotools.coco import COCO from pycocotools.cocoeval import COCOeval import json # 加载ground truth标注和模型预测 coco_gt COCO(annotations/instances_val.json) coco_dt coco_gt.loadRes(model_predictions.json) coco_eval COCOeval(coco_gt, coco_dt, bbox) coco_eval.evaluate() coco_eval.accumulate() coco_eval.summarize() # 常用指标IoU0.50的mAP和IoU0.75的mAP mAP_50 coco_eval.stats[0] mAP_75 coco_eval.stats[5]预测json里每条记录至少要包含image_id、category_id、bbox、score四个字段bbox格式仍然要写成[x, y, w, h]的绝对像素坐标。COCOeval会按score降序排列预测结果计算不同IoU阈值下的精确率和召回率。pycocotools的坑在于对输入格式极其敏感比如score字段必须是浮点数bbox的每一项必须是可转成float的数值字符串之类会给莫名其妙的错误。6.2 裂缝场景的特殊验证像素级IoU和宽度量化mAP在裂缝检测项目里只能反映“框准不准”而工程上更关心两个指标裂缝像素级分割的IoU以及裂缝宽度的测量误差。如果最终部署的是分割模型可以用预测mask对比ground truth的segmentation按像素算IoU。import numpy as np from pycocotools import mask as coco_mask def polygon_to_mask(seg, height, width): rles coco_mask.frPyObjects(seg, height, width) rle coco_mask.merge(rles) return coco_mask.decode(rle).astype(bool) def compute_mask_iou(pred_mask, gt_mask): intersection np.logical_and(pred_mask, gt_mask).sum() union np.logical_or(pred_mask, gt_mask).sum() return intersection / union if union 0 else 0.0frPyObjects把COCO polygon格式转成RLEmerge合并多个孤立的轮廓decode再解码成bool mask。如果项目要求测量裂缝宽度比如何时灌浆、何时修补会涉及一个后处理流程沿分割轮廓的中线做宽度采样统计中位数和方差。这个指标COCOeval算不出来要自己写但它才是最终客户真正关心的物理量。训练时mAP也许中规中矩但宽度误差能控制在一定范围内这个模型在业务上才算站得住。6.3 数据质量决定模型天花板从我自己的经验看桥梁裂缝数据集的坑往往不在模型而在数据这个黑匣子里。COCO格式看起来只是一个json但每个字段背后都是业务语义。解压、对账、统计分布、可视化确认、转换格式、验证结果每一步都值得留出时间。我曾经拿过一个标注质量还不错的裂缝数据集跳过可视化直接训折腾了三周才发现是类别id从0开始导致预测整体错位而从打印类别表到肉眼发现问题其实只需要十分钟。这个教训让我养成了固定习惯数据集到手先写一个data_validation.py一口气做完整性检查、对账、统计和可视化全部通过再进训练迭代。你的数据包不一定叫这个名字但这个流程值得复制。希望这些经验能帮到你。本文还有配套的精品资源点击获取