道路裂缝检测数据集怎么用:YOLOv8目标检测数据预处理与避坑指南
发布时间:2026/10/1 10:40:15来源:尧图网络
简介面向道路裂缝检测与路面病害识别场景提供一套目标检测专用数据集可直接用于YOLO系列模型训练与算法验证。数据按YOLO标准目录结构保存标注格式为yolo格式包含crack单一类别及对应classes文件并已做好数据划分训练集约520张图片及标签、验证集约130张图片及标签标签文件与图片一一对应解压后即可投入训练。资源包共1323个文件其中661个txt标签、660张jpg原图为主体配合1个Python可视化脚本和1张类别示意图整体仅7.3MB轻量易用便于快速下载与部署。配套可视化脚本无需修改即可运行随机传入一张图片便能绘制边界框并保存结果方便自查标注质量与数据均衡性。目前已有88人学习下载适合目标检测入门者、算法工程师及道路检测项目开发者用于模型训练、性能对比与教学演示。1. 道路裂缝检测数据集拿来就能训但先别急着跑训练城市道路巡检、高速路面检测、桥梁健康监测这类场景里道路裂缝检测数据集是最常见的目标检测落地样本之一。这个数据集已经把图片、标签、类别class文件和数据可视化脚本打包好了并且预先做了数据划分意思是你解压之后理论上配好YOLOv8就能开始训练。适合想快速跑通yolov8目标检测数据集处理流程的入门者也适合做裂缝检测方案预研、想先拿现成数据验证模型选型的人。但我见过太多人拿到这类“开箱即用”的数据集直接跑train.py最后被标签错位、类别顺序对不上、划分失衡折腾到怀疑人生。下面按拿到数据集后的落地顺序来先拆格式再做可视化体检然后决定要不要重新划分最后把常见翻车点列出来。这套流程往前多花半小时后面调参至少省两天。2. 拆开数据集看格式数据、标签、class文件是怎么对齐的拿到压缩包第一步不是解压完就跑训练而是先把目录结构看明白。规范的数据集10秒内就能看出组织方式。裂缝检测数据集最常见的做法是images和labels同级里面再按train/val分好class文件单独放。这个结构对YOLO系列训练框架很友好因为它们默认的标签查找方式就是把图片路径里的“images”替换成“labels”再把图片后缀换成.txt。也有人会把train.txt、val.txt这类文件列表一起放进来那说明训练脚本是走文件列表加载的。两种模式没有优劣但你要先确认自己用的是哪一种别拿着文件列表数据集去套目录扫描的配置。下面按最常见的目标检测数据集处理方式展开。2.1 目录结构与命名规则训练脚本认的是路径不是文件名好不好看一个典型的裂缝数据集目录长这样dataset_root/ ├── images/ │ ├── train/ │ │ ├── 000001.jpg │ │ └── ... │ └── val/ │ ├── 000101.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 000001.txt │ │ └── ... │ └── val/ │ ├── 000101.txt │ └── ... ├── classes.txt ├── visualize.py └── train.txt / val.txt可选这个结构里有两套约定比较隐蔽。第一images和labels是同级但彼此独立的目录训练框架靠路径替换去找标签所以/images和/labels这两个目录名最好别改改了就得同步改配置文件。第二图片和标签的匹配靠的是文件主名也就是去掉扩展名后的部分比如000001.jpg对应000001.txt。我发现有人会把训练图片重命名为看起来更直观的名字比如IMG_20231214_082531.jpg结果标签全对不上光清理就对了一整天。另外要注意像CUB这类学术鸟类数据集组织方式是文件夹名当类别名而道路裂缝这类目标检测工程数据集类别语义全部放在class文件里文件夹只承担train/val划分功能。很多刚转来做检测的人会在这点上绕晕。还有jpg、png、jpeg混存的情况训练脚本一般能认但可视化脚本里要显式过滤后缀后面3.3节会给出写法。拿到数据集先做一次对齐检查脚本不复杂但能一次性暴露文件名不一致的问题# 先看总体数量是否一致 find images/train -type f \( -name *.jpg -o -name *.png \) | wc -l find labels/train -type f -name *.txt | wc -l # 列出有图片但没有对应标签的文件一张都不该有 for f in $(find images/train -type f \( -name *.jpg -o -name *.png \)); do base$(basename $f .jpg) base$(basename $base .png) if [ ! -f labels/train/${base}.txt ]; then echo MISSING $f fi done这段脚本的逻辑是取出图片文件名剥掉.jpg或.png后缀再去labels下找同名txt找不到就打印。注意basename命令连剥两次是应付.jpg和.png混存的情况。如果发现输出了一大堆MISSING先检查是不是目录拼写错了比如images和labels的train子目录不配对如果确认路径没问题那就是文件名真的对不上需要重新做匹配。2.2 标签txt里那五个数字class_id与归一化坐标的读法YOLO格式的标签文件是每行一个目标的txt一行五个数字顺序固定。拿这个裂缝数据集里的某一行举例0 0.5231 0.4312 0.0324 0.1185第一个0是类别编号后面四个分别是归一化中心点x、中心点y、归一化宽度、归一化高度。归一化指的是相对图片宽高的比例取值范围0到1不是像素坐标。比如一张4096×3072的检测原图0.5231乘以4096等于2142像素0.1185乘以3072等于364像素还原成像素坐标要这样乘回来。读取标签最直接的办法是纯Python解析不依赖任何第三方库from pathlib import Path def read_yolo_label(path): boxes [] for line in Path(path).read_text().strip().splitlines(): parts line.split() if len(parts) 5: continue cls int(parts[0]) cx, cy, w, h map(float, parts[1:5]) boxes.append((cls, cx, cy, w, h)) return boxes boxes read_yolo_label(labels/train/000001.txt) print(len(boxes), boxes[:2])这段代码有几个细节值得注意。第一空标签文件会让read_text返回空字符串strip之后没有splitlines返回空列表这正是负样本的正确表现不要当成错误。第二用split()按空白字符分割能自动处理行尾有多余空格的情况。第三如果某一行解析时报错大概率是混入了COCO格式的json数据那个不是这种纯文本结构。裂缝数据里常见的长宽比很大的框五个数字里的w和h会相差很多倍这是正常现象不代表标签异常。2.3 class文件与类别顺序从classes.txt到“软标签”的关联class文件通常叫classes.txt一行一个类别名行号和标签txt里的class_id一一对应。如果这个裂缝数据集只标了一类那classes.txt内容很简单crack如果按裂缝走向细分成了横向、纵向、网状那class文件会是三行分别对应标签里的0、1、2crack_horizontal crack_vertical crack_network这个对应关系是整个数据集的“编号表”。class_id只是序号不携带语义所以这个文件就是“硬标签编号表”。你训练时YOLO配置里的names参数必须和这个文件逐行对齐。如果把标注工具里的类别重排一遍或者往中间插入一个新类已有标签的编号整体错位这是后面5.1节要展开的大坑。知识蒸馏里说的“软标签”指模型输出的概率分布和这里完全是两码事这个数据集里我们只认class文件和标签id的硬绑定。很多目标检测常用标注工具比如LabelImg、roLabelImg在标注前就要先加载class文件保存txt时回填的就是这个顺序。所以拿到数据集后第一件事就是打开classes.txt确认它的顺序没有被改动过。类别命名上我建议遵循一个工程习惯英文小写加下划线不要用中文不要带空格。因为后续转COCO、训练、部署时类别名会被写进很多配置文件和日志里带空格容易在解析时断掉。如果class文件已经是这种命名就保持原样。你要是想合并自己的数据比如把燃气管道图像数据集也一起训练合并的第一步也是把两个class文件对齐不是把图片拷到一个目录就完事。3. 可视化脚本先跑通画框确认标签真的和图片对齐数据集自带的“数据可视化脚本”通常就是画框脚本。我的建议是不要直接拿来就用先自己动手写一个最小的画框程序。可视化不只是为了看效果而是给数据做体检框和裂缝是否贴合、有没有漏标、类别颜色是否合理。用100张画框图去检查比训练完之后看100张预测图值钱得多。如果你用的还是YOLOv8这类目标检测数据集处理流程可视化这一步跳过后面训练时出现的很多怪问题都没法定位是数据问题还是模型问题。下面给出一个可以改一改就用于检查的版本。3.1 最小可视化脚本OpenCV读图、画框、另存基于OpenCV的画框脚本是所有检查工作的基础设施import cv2 from pathlib import Path data_root Path(.) img_path data_root / images/train/000001.jpg lab_path data_root / labels/train/000001.txt img cv2.imread(str(img_path)) if img is None: raise FileNotFoundError(f图片读不出来: {img_path}) h, w img.shape[:2] for line in lab_path.read_text().strip().splitlines(): parts line.split() cls, cx, cy, bw, bh ( int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]), ) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText( img, str(cls), (x1, max(y1 - 5, 0)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2, ) cv2.imwrite(check_000001.jpg, img)画框颜色用的是BGR顺序的(0,0,255)纯红裂缝在灰色路面上红色最显眼。坐标还原时要注意bw和bh是归一化宽高必须乘图片宽高中心点坐标减半才能换算成左上角。线宽固定2对4096宽的大图会偏细可以改成max(2, w // 1500)让大图小图都能看。putText的y坐标如果太靠近顶部文字会画出画布所以用max(y1 - 5, 0)兜底。3.2 按类别染色与漏标检查一眼看出标签噪声当数据不止一个类别时全部画成红色看不出类别归属按class_id染色更直观COLORS [(0, 0, 255), (0, 255, 0), (255, 0, 0), (0, 255, 255)] for line in lab_path.read_text().strip().splitlines(): parts line.split() cls int(parts[0]) color COLORS[cls % len(COLORS)] # 坐标换算同 3.1省略 cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText( img, fcls{cls}, (x1, max(y1 - 8, 0)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2, )染色逻辑是让不同类别用不同颜色肉眼扫一遍就能发现“这个框颜色不对”。裂缝这类细长目标正常框应该紧贴裂缝边缘。如果发现大量框和裂缝错位、框内没有裂缝、或者同一条裂缝被框了两次基本可以判定标签有问题。还有一种更隐蔽的坏标签框是对的但缩得很小只框住裂缝的一小段这通常是标注时用了不同的坐标系约定。漏标比错标更难发现。错标至少有个框在那里漏标则是图片上明明有一条明显裂缝一个框都没有。检查漏标的方法是先看原图心里数一遍裂缝数量再切到带框图对比。几百张图里抽查50张重点关注大于80像素的裂缝是否都有框。这个阈值可以自己按项目精度要求定。3.3 批量生成到输出目录几百张图的快速巡检单张检查太慢把整个目录跑一遍输出到check目录import cv2 import numpy as np from pathlib import Path images_dir Path(images/train) labels_dir Path(labels/train) out_dir Path(check) out_dir.mkdir(exist_okTrue) COLORS [(0, 0, 255), (0, 255, 0), (255, 0, 0)] for img_p in sorted(images_dir.iterdir()): if img_p.suffix.lower() not in (.jpg, .png, .jpeg): continue lab_p labels_dir / (img_p.stem .txt) img cv2.imread(str(img_p)) if img is None: print(读图失败:, img_p) continue h, w img.shape[:2] if lab_p.exists(): for line in lab_p.read_text().strip().splitlines(): parts line.split() cls, cx, cy, bw, bh ( int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]), ) color COLORS[cls % len(COLORS)] x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText( img, str(cls), (x1, max(y1 - 5, 0)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2, ) cv2.imwrite(str(out_dir / img_p.name), img) print(done:, img_p.name)关键点在几个边界处理suffix.lower()过滤掉非图片文件避免把隐藏文件也读进来lab_p.exists()保证缺标签的图片不报错读图失败时打印日志并跳过而不是让整个脚本中断。Windows下如果图片路径带中文cv2.imread会返回None却不会抛异常就会打印一堆“读图失败”解决办法在第5章展开这里先提一句用np.fromfile配合cv2.imdecode可以解决。批量跑完别急着收工打开check目录按文件名顺序翻重点挑两类看一类是很多框挤在一起的小图一类是完全没有框的图。如果时间有限每类随机抽20张就够了不需要每张都过。4. 重新做数据划分随机和分层差在哪验证集缺类怎么查“已做数据划分”不等于这个划分适合你的场景。原始划分可能是纯随机打乱也可能验证集只占了很小的比例而你想在不同光照、不同路面材质上验证泛化能力那就需要重新划分。特别是处理数据集用于yolov8训练时划分这一步出问题的概率比想象中大得多。先确认原划分比例用wc命令数一下train和val下的图片数量大概知道原划分是8:2还是9:1。如果只有train和val两堆没有独立的test那val承担的是验证和调参双重任务划分时就要更长点心。4.1 复现原划分固定随机种子与文件列表打乱最简单可靠的划分方式是固定随机种子对完整文件列表做shuffle再按比例切分。这样任何人在任何机器上运行得到的结果都是一样的实验对比才不会打架。import random import shutil from pathlib import Path random.seed(42) # 固定种子保证两次运行结果一致 src_img Path(images/all) src_lab Path(labels/all) dst Path(split_dataset) imgs sorted(list(src_img.glob(*.jpg)) list(src_img.glob(*.png))) random.shuffle(imgs) val_ratio 0.2 n_val int(len(imgs) * val_ratio) val_imgs, train_imgs imgs[:n_val], imgs[n_val:] for split, split_imgs in [(val, val_imgs), (train, train_imgs)]: img_out dst / split / images lab_out dst / split / labels img_out.mkdir(parentsTrue, exist_okTrue) lab_out.mkdir(parentsTrue, exist_okTrue) for img_p in split_imgs: lab_p src_lab / (img_p.stem .txt) if not lab_p.exists(): print(f标签缺失跳过: {img_p.name}) continue shutil.copy2(img_p, img_out / img_p.name) shutil.copy2(lab_p, lab_out / (img_p.stem .txt))几个参数值得说清楚。random.seed(42)不是玄学是工程必需不固定种子每次跑出的train/val都不同模型对比实验就没有基线。val_ratio0.2是常用起步值裂缝数据如果是几千张图20%能拿到几百张验证图足够评估如果总图数不到500val建议提到25%不然验证集AP曲线抖动会很严重。标签缺失时直接跳过而不是把图片单独扔到train里这点很重要无标签图片进YOLO训练会被当成全背景图模型会学到错误信息。复制图片会占磁盘空间但换来的是目录干净、工程独立。如果图片量特别大改成写文件列表更合适训练脚本走txt列表加载with (dst / train.txt).open(w) as f: for img_p in train_imgs: f.write(str(img_p.resolve()) \n)YOLO系列和很多基于mmdetection的工程都支持这种txt列表模式把图片路径逐行写进去就行。4.2 按类别分层划分避免验证集里缺类纯随机划分最大的坑是数量少的类别在验证集里可能一个样本都抽不到。裂缝数据集常见2到5个类别有的类只有一两百张随机划分后该类在验证集里为0的概率不低。结果就是训练时模型见过这个类验证时却从不考核它整体mAP看着不错部署到市政道路上立刻翻车。简单的办法是按主类别分层抽样from collections import defaultdict def main_class(img_p, src_lab): lab_p src_lab / (img_p.stem .txt) if not lab_p.exists(): return None cls_counts defaultdict(int) for line in lab_p.read_text().strip().splitlines(): cls_counts[int(line.split()[0])] 1 if not cls_counts: return -1 # 空标签 return max(cls_counts, keycls_counts.get) grouped defaultdict(list) for img_p in imgs: c main_class(img_p, src_lab) if c is not None: grouped[c].append(img_p) train_imgs, val_imgs [], [] for c, group in grouped.items(): random.shuffle(group) cut int(len(group) * (1 - val_ratio)) train_imgs.extend(group[:cut]) val_imgs.extend(group[cut:])这段逻辑是先把所有图片按主类别分组再在每个组内单独做随机划分。这样每个类别在验证集里的比例都一致不会出现某类在验证集里为0。它的缺点是只按主类归属一张图同时含多个类别时归类有误差但对以裂缝为主体的道路数据集这个误差可以接受。如果类别数很少还可以在验证集里做补样让最少的一类也有30个实例以上但要注意别让同一张图既在train又出现在val。4.3 划分完必须做的两个校验数量一致与类别均衡划分结束后要做校验这一步不该省。数量校验用ls数目录echo train imgs: $(ls split_dataset/train/images | wc -l) echo train labels: $(ls split_dataset/train/labels | wc -l) echo val imgs: $(ls split_dataset/val/images | wc -l) echo val labels: $(ls split_dataset/val/labels | wc -l)两个数字应该一致。如果不一致回到2.1的互查脚本找出是哪个文件没配对。类别均衡校验用一行awk统计验证集里每个class_id的框数量for f in split_dataset/val/labels/*.txt; do awk {print $1} $f done | sort | uniq -c输出会像这样134 0 42 1 0 2如果某个类别显示为0这一类的验证AP就是无效的。处理办法是回到4.2把这一类的最小验证数量卡到至少30或者手动从训练集里挪几张进val。裂缝数据里容易出现“修补痕迹”这种少样本类别恰恰是实际业务最关心的类宁可验证集整体小一圈也不能让这行是0。提示这里的“空标签文件”和“缺失标签文件”是两回事。空标签是文件存在但0字节是合法负样本缺失标签是根本没有txt文件是脏数据。4.1脚本只跳过了缺失情况不会误删空标签。5. 避坑记录裂缝数据使用中翻过车的5个真实案例下面这五个坑不完全是我一个人的记录基本是团队里做这个方向绕不开的坎。每条按现象、原因、解决三步写对着检查比自己瞎猜快。5.1 class_id和class文件顺序错位损失正常预测全错现象训练时损失曲线正常下降验证mAP也有0.8以上但一跑推理所有的横向裂缝都预测成了纵向裂缝。整体错位一个位置不是个别图片的问题。原因数据集的classes.txt被重排过但标签txt里的class_id没有同步更新。比如原始顺序是crack_horizontal(0)、crack_vertical(1)后来有人想把crack_network插到第一位改了class文件标签id却还停留在旧顺序。模型本身没有学错它学的是“0号目标长什么样”只是0号含义被换掉了。解决先找出新旧class文件的对应关系生成旧id到新id的映射然后把labels下所有txt逐行替换from pathlib import Path mapping {0: 1, 1: 2, 2: 0} # 旧class_id - 新class_id for lab_p in Path(labels/train).glob(*.txt): lines [] for line in lab_p.read_text().strip().splitlines(): parts line.split() parts[0] str(mapping[int(parts[0])]) lines.append( .join(parts)) lab_p.write_text(\n.join(lines))这个脚本的关键是把mapping先写清楚再动手。替换完必须重新跑一次可视化确认横向裂缝框上显示的是“cls1”而不是“cls0”。血泪经验是这个坑在训练阶段几乎不露馅损失正常、mAP正常只有部署时才炸所以拿到数据集的第一步就核class顺序。5.2 图片与标签文件名不匹配漏检一张都发现不了现象训练日志里train的图片数量比标签数量多出十几张可视化时某些图片完全没有任何框。原因有人重命名了部分图片或者标注工具导出时给文件名加了批次前缀导致图片主名和标签主名对不上。YOLO训练框架对无标签图片的默认处理方式是不计算该图的标签损失等于把这张图当纯背景训练模型会从中学到“这里没目标”。解决跑双向互查一次列出有图无标签一次列出有标签无图# 有图无标签 for f in images/train/*.jpg images/train/*.png; do base$(basename $f) base${base%.*} [ -f labels/train/${base}.txt ] || echo no label: $base done # 有标签无图 for f in labels/train/*.txt; do base$(basename $f .txt) [ -f images/train/${base}.jpg ] || [ -f images/train/${base}.png ] || echo no image: $base done两段命令都要跑。对“有图无标签”的处理要果断要么找到原标签放回来要么直接从训练集里删掉这张图绝不能留在那里当背景。对“有标签无图”的情况多半是图片被误删或移动找到放回来即可。整批整理完再用2.1的脚本复查一遍。5.3 中文路径导致可视化脚本黑屏cv2.imread返回None现象脚本运行不报错但输出的检查图是黑色的或者文件根本没生成日志里也没有明显异常。原因OpenCV的cv2.imread在Windows下遇到非ASCII码路径时会返回None而cv2.imwrite不会因此报错于是黑图被写出来。数据集放在D:\道路裂缝\images这类目录下就会触发。解决读图时改用np.fromfile读二进制数据再用cv2.imdecode解码绕开imread的路径处理逻辑import cv2 import numpy as np def load_image(path): data np.fromfile(str(path), dtypenp.uint8) img cv2.imdecode(data, cv2.IMREAD_COLOR) return img写文件也建议用imencode加tofile避免保存路径带中文时再次翻车ret, buf cv2.imencode(.jpg, img) buf.tofile(str(out_path))这个坑在Linux下几乎不出现所以很多开源可视化脚本没做兼容。如果你是在Windows上做数据集处理直接把load_image封装成工具函数全项目统一调用。5.4 验证集类别缺失mAP看着高部署就翻车现象验证mAP0.5看着有0.9但现场测试时模型对少见类别完全不认识比如“修补痕迹”这类框一个都预测不出来。原因划分数据时用了纯随机某个类别样本太少验证集里一个都没分到。训练时模型见过这个类但验证流程里没有它的AP计算项mAP被其他类别拉高了问题被掩盖。解决回到4.2做分层划分并且对每个类别单独看AP。验证集里单个类别的实例数低于30时AP的方差会很大一次验证跑出0.5一次跑出0.9都不奇怪。处理办法是把该类少量样本从训练集挪进验证集也可以对少样本类别做复制增强但复制时只能加进训练集不要复制进验证集否则验证结果虚高。注意市政路况里“修补痕迹”出现频率低不代表甲方不关心。少样本类别的漏检往往是项目验收时被diss最多的问题。5.5 空标签文件别乱删负样本是训练的一部分现象labels目录里有一些0字节的txt文件清理“脏数据”时把空标签删了训练完模型在干净路面上疯狂误检把阴影、伸缩缝、路面积水都标成了裂缝。原因空标签对应的是没有裂缝的路面图片是刻意采集的负样本。YOLO的损失计算里背景项权重不低负样本让模型学会“图片里可以没有目标”。把所有空标签删掉等于告诉模型每张图都必须有目标于是它开始乱标。解决保留空标签并且在训练配置里把mosaic增强比例调低一点。mosaic会把多张图拼在一起负样本图会和正样本图拼成一张负样本的“纯背景”语义被稀释。如果用的是YOLOv8这类工程mosaic默认值比较高跑裂缝这种细长目标数据集时建议从0.5起步试。如果你确实要删空标签必须连同对应的图片一起删除绝不能只删txt不删图。6. 进阶把裂缝数据集转COCO格式再用mmrotate做旋转目标检测普通目标检测流程跑通之后裂缝这类细长目标还有一个常见升级方向旋转目标检测。水平框在斜向裂缝上会框进大量背景NMS时相邻框互相抑制定位精度上不去。把数据转成COCO JSON接mmrotate做旋转框训练是业界比较常见的做法。想玩开放词汇目标检测那套也建议先把这个有监督旋转框基线跑出来。6.1 转COCO JSON把YOLO txt换成通用交换格式转换脚本的核心是构造images、annotations、categories三块结构import json from pathlib import Path def convert(images_dir: Path, labels_dir: Path, output: Path, class_names: list): out_imgs, out_anns, ann_id [], [], 0 for i, img_p in enumerate(sorted(images_dir.iterdir())): img load_image(img_p) # 用5.3的兼容读法 h, w img.shape[:2] out_imgs.append({id: i, file_name: img_p.name, width: w, height: h}) lab_p labels_dir / (img_p.stem .txt) if not lab_p.exists(): continue for line in lab_p.read_text().strip().splitlines(): cls, cx, cy, bw, bh map(float, line.split()) x (cx - bw / 2) * w y (cy - bh / 2) * h bw_px, bh_px bw * w, bh * h out_anns.append({ id: ann_id, image_id: i, bbox: [x, y, bw_px, bh_px], area: bw_px * bh_px, category_id: int(cls), iscrowd: 0, }) ann_id 1 output.write_text(json.dumps({ images: out_imgs, annotations: out_anns, categories: [{id: i, name: name} for i, name in enumerate(class_names)], }, indent2))COCO和YOLO的坐标表示不同COCO的bbox是左上角x、左上角y、像素宽、像素高YOLO是归一化中心点加宽高转换时要做坐标原点和尺度的双重变换。area字段值是像素面积iscrowd在裂缝数据里统一填0表示没有群体目标。6.2 旋转框是裂缝检测的加分项从水平框到minAreaRectmmrotate不能直接消费水平COCO框但它支持把水平框转成旋转框作为baseline。对每个bbox的四个顶点用cv2.minAreaRect求最小外接矩形就能得到带角度信息的旋转框import cv2 import numpy as np box [x, y, bw_px, bh_px] # 6.1的COCO bbox pts np.array([ [x, y], [x bw_px, y], [x bw_px, y bh_px], [x, y bh_px], ], dtypenp.float32) rotated cv2.minAreaRect(pts) # 返回 (cx, cy), (w, h), angle这个转换不依赖新标注直接从水平框生成旋转框粗粒度但足够当baseline。我之前用YOLO水平框做了一版裂缝检测到弯道和斜接缝场景里NMS抑制特别严重换旋转框后mAP提升了近3个百分点代价是训练参数变多、收敛变慢。我的习惯是拿到数据集先做可视化体检再固定种子重划数据最后才碰train.py。这套流程看起来笨但能让你后面调参时少遇一次“看起来正常却不知从何下手”的玄学问题。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网