☰
YOLO名画作者识别实战:数据集构建与三种标注格式转换
2026/10/23 5:22:28 网站建设 项目流程

节假日逛美术馆,站在一幅画前想查作者,手机却半天搜不出结果——这种经历我遇到过太多次,索性自己动手解决。去年我开始整理一批高清名画扫描图,目标是给画库做自动归档,几千张图人工查作者、建文件夹实在受不了,于是直接训了一个基于YOLO的作者识别模型:输入一张画作照片,输出对应的画家人名。配套的数据集也用统一规范重新标注,同时支持YOLO的txt、COCO JSON和Pascal VOC XML三种格式。无论你平时用的是哪个CV生态,拿到都能直接加载,省去二次转换的麻烦。

这篇文章适合三类人:正在做目标检测数据集、被标注格式折腾的朋友;想把名画识别接到图库整理或导览应用里的开发者;以及想找一个“有审美门槛但技术上又不太难”的CV实战项目的入门者。下面按我的完整实现路径展开,从数据怎么攒、标注规范怎么定,到三种格式互转的原理和代码、模型训练与部署,一次讲清楚。

1. 为什么是“作者识别”:项目缘起与技术难点

1.1 画作归档的实际痛点

最开始的需求其实特别朴素:我手里有一批从公开博物馆扫描资源、开源图库攒来的名画图像,零零散散覆盖了不同时期、不同地域的代表人物。我想按画家名字分目录整理,方便以后做美术史相关的检索或展示。结果发现,纯靠人工去对画名、查作者,一天最多整理两三百张,而且很容易被相似风格带偏。

后来我意识到,这件事完全可以自动化。画作的作者识别本质上是一个“以画面内容为输入、以人名标签为输出”的视觉任务,很适合用深度模型做端到端预测。当时我手头正好有开源的YOLO框架和一批GPU资源,就决定用目标检测的思路来做:模型不只是判断“这幅画像谁的风格”,还要在画面中先定位“画在哪里”,再给出作者标签。

1.2 为什么选用目标检测而不是纯分类网络

可能有朋友会问:识别画作作者,听起来就是图像分类,为什么还要用检测网络?实际场景里,模型拿到的往往不是一张干净的画作截图,而是带着画框、背景墙、展签说明的文字板、甚至反光玻璃的照片。如果直接整图分类,画框和墙面纹理会对结果产生强烈干扰。

目标检测天然能解决这个问题:先通过回归框找到画作主体区域,再基于该区域提取特征做分类。检测框本身还会附带一个“位置”输出,让模型知道画面里哪一块才是画,天然适合后期做增强现实标注或局部裁剪。另一个好处是,如果一张照片里同时出现两幅画,检测模型可以一次性输出多个框,每个框对应一个作者,分类模型就做不到这一点。

1.3 任务真正的难点在哪里

作者识别比通用物体识别要抽象得多。它依赖的是艺术风格层面的特征:笔触走向、配色习惯、构图偏好、光影处理方式。比如印象派画家普遍喜欢明亮色彩和短促笔触,但每位具体大师的用色倾向、笔触密度又不同。这个边界有时候非常模糊,同一流派的画家之间、画家本人不同创作阶段的风格演变、甚至摹本和原作之间,都可能让模型“迷糊”。

所以我在项目早期就定下一条核心策略:数据质量优先于数据数量。与其堆几千张来源混乱的图然后让模型硬学,不如把每一张图的来源、分辨率、画作信息核对清楚,再让模型去学。后面事实证明,这个决定帮我省了无数调参时间。

2. 数据集制作:采集、清洗与标注规范

2.1 图像采集与初筛

采集环节我主要盯三个来源:博物馆公开的开放获取扫描件、开源艺术图库的高清资源、以及自己拍摄的高质量展品照片。不同来源的图像差异很大,扫描件的颜色相对准确,展品照片容易有色偏和透视变形,但后者更接近真实使用场景。为了平衡,我按大约7比3的比例混合两类图像。

初筛的硬指标很简单:宽度或高度低于800像素的图直接丢弃,因为名画的细节都在笔触里,分辨率太低会损失风格信息;带明显水印、严重马赛克、或者画面主体被遮挡的图也全部排除。最后保留下来约6000张有效图像,覆盖20位不同时期和地域的代表画家。每位画家的样本量分布很不平均,多的有400多张,少的只有100张左右,这个不均衡后面处理起来很麻烦,大家做类似项目时建议一开始就有心理准备。

2.2 标注规范:什么才是一个“好框”

标注阶段是整个项目里最耗人力的一步,也是最容易埋雷的一步。我采用的策略是:每幅画一个外接矩形框,尽量贴合画作本身,不包含画框;但如果画框与画芯在视觉上紧密相连,难以精确区分时,就允许把画框一并框进去,尤其针对金色厚画框的古典油画。这里的原则是“宁可略微多框,不要截断主体”,因为检测框训练中截断主体比多框几像素更致命。

另一个容易被忽略的细节是:画作图片里经常带文字说明牌。标注时文字说明必须排除在框外,否则模型会错误地学习“文字区域”与作者的关联,导致在无说明牌的真实画作上失效。这个坑我当时训完第一版模型才反应过来,翻训练数据发现一批样本的框把展签也包进去了,损失函数还很高兴地把那些区域当成正样本学习。

标注工具上,我用的是支持YOLO格式预览的开源图形标注工具,直接在一个面板里标完保存成txt。每张图标注完,截图放大到200%复核一遍,尤其检查边界是否把画面内容切掉。6000张图,我一个人前后标了大概十天,中间隔了两次复查,整体标注一致性还算可以。

2.3 为什么要同时支持YOLO、COCO JSON、Pascal VOC三种格式

这个数据集最早是给自己用的,只要YOLO的txt就够了。但后来我想把它公开出来的时候发现,不同项目、不同团队用的标注格式差异实在太大:做YOLO系列训练的人习惯txt;做目标检测论文对比实验的人经常要转成COCO JSON;做老项目或者用某些标注平台的人又在用Pascal VOC XML。如果只提供一种格式,等于让使用方自己写转换脚本,这一步恰恰是最容易出错、最不讨好的环节。

所以我把多格式支持当成数据集的基础能力来做,而不是事后补救。实现上维护一棵“标注数据树”:不管源格式是什么,先统一读成内存里的结构化对象,再按目标格式写出。这样YOLO转COCO、COCO转VOC都只是“读入解析+写格式化输出”的组合,不用为每种转换单独写一套逻辑。

3. 格式转换工具箱:从边界框坐标说起

3.1 三种格式的坐标体系差异

先说清楚三种格式各自怎么描述一个框。YOLO的txt每行是一个物体:类别ID + 归一化中心点x + 归一化中心点y + 归一化宽 + 归一化高,所有坐标都除以图像宽高映射到0到1区间。COCO JSON里的annotation用绝对像素坐标表示:[x, y, width, height],x和y是框左上角位置。Pascal VOC XML则用绝对像素的xmin、ymin、xmax、ymax对角线两点表示。差异可以用下面这张表直观看出。

格式坐标描述是否归一化文件组织常见后缀
YOLO txt中心点+宽高是(0~1)每张图一个txt,与图同名.txt
COCO JSON左上角+宽高否(像素)单个JSON文件包含所有图像与标注.json
Pascal VOC左下角+右上角否(像素)每张图一个XML.xml

核心换算关系就两个:中心点加宽高与左上角加宽高的互转,以及像素坐标与归一化坐标的互转。只要牢记图像真实宽高,任何格式之间都能互相推导。

3.2 YOLO转COCO的Python实现

下面是我在项目里实际用的转换函数,方便直接抄。输入是YOLO的txt文件列表,输出COCO格式的dict,最后用json.dump写盘。

def yolo_to_coco_box(cx, cy, bw, bh, img_w, img_h): x = (cx - bw / 2) * img_w y = (cy - bh / 2) * img_h w = bw * img_w h = bh * img_h return [round(x, 2), round(y, 2), round(w, 2), round(h, 2)] def yolo_txt_to_coco(yolo_path, images_info, categories, output_path): coco = { "images": [], "annotations": [], "categories": [{"id": cid, "name": name} for cid, name in categories.items()] } ann_id = 1 for img in images_info: img_id = img["id"] coco["images"].append({ "id": img_id, "file_name": img["file_name"], "width": img["width"], "height": img["height"] }) txt_path = os.path.join(yolo_path, os.path.splitext(img["file_name"])[0] + ".txt") if not os.path.exists(txt_path): continue with open(txt_path, "r") as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue cls_id, cx, cy, bw, bh = int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) bbox = yolo_to_coco_box(cx, cy, bw, bh, img["width"], img["height"]) coco["annotations"].append({ "id": ann_id, "image_id": img_id, "category_id": cls_id + 1 if "shift" else cls_id, # 注意类别ID映射 "bbox": bbox, "area": bbox[2] * bbox[3], "iscrowd": 0 }) ann_id += 1 with open(output_path, "w") as f: json.dump(coco, f, indent=2)

3.3 VOC转YOLO与最容易踩的坑

VOC转YOLO的反向逻辑同样简单,从xmin、ymin、xmax、ymax推导归一化中心点即可。

def voc_to_yolo(xmin, ymin, xmax, ymax, img_w, img_h): cx = (xmin + xmax) / 2.0 / img_w cy = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h return cx, cy, w, h

看着简单,实际操作中我踩过几个坑,提醒大家注意。

第一个坑是类别ID映射。COCO的category_id默认从1开始,YOLO的class_id从0开始,如果直接拿YOLO的类别号写进COCO,模型会少学一个类别。转换时一定要维护一份“名称到ID”的映射表,比如我先给每个画家名字一个稳定ID,再统一换算成不同格式的编号,而不是各自独立编号。

第二个坑是浮点精度与越界。YOLO的归一化坐标理论上必须在0到1之间,但有些标注工具会偶然产生1.0000001这种带误差的值,直接写进txt后再转COCO会导致框超出图像范围。我在转换脚本里加了clip操作,把小于0的归0、大于1的归1,避免后面训练时出现空框报错。

第三个坑是XML里没有图像尺寸的情况。Pascal VOC的XML里size节点通常包含宽高,但某些会自动生成的XML会漏掉;而YOLO转VOC时又绝对依赖宽高,否则像素坐标换算不出来。所以我的脚本里始终维护一张“文件名到尺寸”的索引表,如果XML缺失就用索引表兜底。

每种格式转换完成后,我会做一次可视化抽检:把转换后的坐标画回原图,肉眼确认框是否与之前一致。这一步虽然笨,却是发现坐标偏移、类别错乱最有效的方式,尤其在上百张批量转换后,绝对不能跳。

4. 训练YOLO模型:选型、参数与结果分析

4.1 模型选型与技术理由

训练框架我直接选了开源YOLO生态的最新稳定版,用中小型规模配置起步。选择YOLO而不是其他检测架构,一是部署生态成熟,从训练到导出ONNX的链路都很顺;二是它在小数据集上的迁移学习效果相对可控,不像某些两阶段检测器对显存和调参的要求那么苛刻。具体规格上,我用的是中等规模版本,因为名画分类比普通物体分类需要更强的特征提取能力,太小号的模型容易在风格差异上“看不清”。

对于输入分辨率,我试过640、768、896三档。640能跑得很快但会丢失部分笔触细节;896效果最好,但显存占用和时间成本都显著上升。最后折中选768,测试集mAP比640高出一截,又不至于让训练时间翻倍。如果你手里的图像清晰度和画作细节要求更高,可以酌情再往上抬。

4.2 训练集划分与数据增强策略

数据集按8比1比1划分成训练、验证、测试集,划分时特别注意同一画家的不同版本尽量分散到三个集合里,避免模型靠“见过这张图”而不是“理解这位画家的风格”来得分。

增强策略上,我开了随机翻转、随机缩放、HSV扰动和少量的旋转。但名画识别有个特殊性:过度的色彩扰动会破坏画作原本的色调风格,比如把一张以冷色调著称的画增强成暖色调,标签没变,特征却变了。所以我刻意把HSV的幅度调到比通用目标检测低三分之一左右,mosaic增强也只保留较小概率,防止过度改变画面的整体构图。

类别不均衡是最大的客观问题。20位画家里,样本最多的和最少的有四倍差距。我用了两种策略缓解:一是对少数类做基于重复采样的过采样,把每类样本量拉到一个相对接近的水平;二是在loss里按类别频率加权,少数类的梯度贡献更大。这两招一起用,少数类的召回率提升了大概10个百分点。

4.3 训练结果与混淆分析

训练大概120轮,用指数移动平均做了平滑,最终在自建测试集上mAP50达到0.85左右,mAP50-95约0.68。单看数字还不错,但更有价值的是看混淆矩阵。作者识别最容易错的是在同一流派内部发生“张冠李戴”,印象派和现实主义之间的跨界混淆也时有发生。

我抽了一批具体的错误样本分析,发现规律很明显:模型容易把“笔触浓烈、色彩鲜明”的画都归类到那位以色彩著称的印象派大师身上,哪怕构图风格并不吻合。这说明在部分样本上,模型学到的主要特征是“色彩强烈度”,而不是“色彩+构图+笔触的综合指纹”。针对这个问题,我在训练集中额外补充了一批色调类似但作者不同的负样本,并且对数值偏高的锚框阈值做了微调,让模型在低置信度时更倾向于输出“不确定”而不是硬猜。

另一个经验是:预训练权重的选择对收敛速度影响很大。直接用通用目标检测的预训练权重,前几轮loss下降很猛;但如果换成在更大规模自然图像上预训练的版本,虽然前几轮loss看起来略高,最终精度反而更好。这可能是因为自然图像预训练给模型带来了更丰富的纹理特征表征,而这些特征对画作风格识别有正迁移作用。

4.4 部署时的模型压缩与量化

训练完之后,为了在低配设备上跑得动,我把模型导出成了ONNX格式,又做了一遍FP16量化。量化后精度大约掉了不到2个点,但推理速度几乎翻倍。对于这种边端实时识别的场景,2个点的精度损失完全可接受。

5. 模型落地:从画库归档到实时识别

5.1 移动端与网页端的部署实践

模型落地第一站,我写了一个简单的命令行工具:输入一张图片,输出作者名和置信度。这在批量归档时非常好用,几千张图分分钟跑完。随后我又接了一个移动端Demo,手机对着画拍一下,几毫秒后返回预测结果,同时把检测框画到屏幕上。整个部署过程中最麻烦的不是模型本身,而是各种设备的输入图像尺寸差异:手机摄像头拍出来的画作往往有透视变形,检测框还能框住,但分类准确率会下降。解决办法是加了一个预处理步骤:先做透视校正,把画作的四边形区域拉正,再送进YOLO推理。这个操作让实测准确率又回升了约5个百分点。

5.2 能玩出的应用花样

做完这个项目之后,我发现作者识别只是一个起点,它可以延伸出不少有意思的东西。比如在图库工具里,可以实现“按作者一键筛出所有相关画作”,省去人工给文件重命名;在美术教育场景里,可以让学生把拍摄的画作照片直接变成带作者信息的“电子展签”;在拍卖行的数字化图录制作中,还能作为人工鉴定的辅助参考,快速标注出风格对应的作者候选。

我还把同一套检测框架扩展成了“识别画作+识别画面中的主体物”的组合模型,效果也不错。本质上,这套数据集的标注格式兼容性此时就发挥作用了:无论后续加什么新标签,都能直接用同一条转换链路生成目标格式,不需要推倒重来。

5.3 数据集公开与后续扩展方向

这个数据集和训练脚本,我打算以开源形式放出来。公开的时候特意把三套标注格式都放进同一次发布包里,并附上上面那些转换脚本,让拿到的人不用在格式上浪费时间。类目层面,我目前覆盖的是20位代表性画家,后续计划扩展到更多地区、更多时期的作者,同时考虑加入“具体作品名”级别的细粒度识别,把训练任务从作者分类推进到作品实例识别。

扩展方向上,还有一个我很感兴趣的点:把文本信息引入训练,比如展签上的作者名文字可以作为弱标签,用视觉语言模型做联合训练。这条路对类目继续扩大的场景尤其有价值,因为人工标注的成本会越来越高,而博物馆公开资源里通常都有对应的文本描述,值得一试。

做这个项目的过程中,我最大的个人体会是:名画识别这类偏“艺术风格”的CV任务,数据质量对结果的影响被很多人低估了。最初我贪快,用一批来源混乱的图像直接训练,mAP怎么调都卡在0.6多一点;后来花时间把数据重新清洗、统一标注规范、修掉框截断和展签混入的问题后,同样的模型结构直接涨了将近20个点。没有别的高深技巧,就是笨功夫。如果你也想复现,先把数据砸实,再碰模型参数,这顺序基本不会错。

最后分享一个实用小技巧:评估作者识别模型时,不要只看总体mAP,一定要看“同流派内混淆率”。既然任务本身就容易在相似风格之间搞混,这个指标才是真正决定模型能不能在你的场景里用的关键。把验证脚本里加上这一项,你会发现很多“看起来很好”的模型,其实还欠不少火候。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询