ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

光学音乐识别数据集全攻略:从选型到标注转换与训练避坑

光学音乐识别数据集全攻略:从选型到标注转换与训练避坑 简介面向光学音乐识别OMR研究者的数据集集合整合了多种风格与历史时期的乐谱图像及标注信息适用于算法训练、模型评估与预处理流程开发。包内附带 omrdatasettools 工具链包含图像生成、数据集下载、边界框与符号标注处理等功能可复现 Homus、DeepScores、Muscima 等公开数据集样本帮助用户快速建立标准化实验流程。资源共 85 个文件以 PNG 示例图像、Python 脚本、Markdown 文档及配置文件为主压缩包仅 6.23MB轻量但功能完整。Python 脚本覆盖生成器、下载器、测试与打包发布流程PNG 样例直观展示各类乐谱图像效果适合希望在本地快速搭建 OMR 数据管线的中高级研究者。目前已有 149 人学习浏览。这一集合不仅提供多类数据集入口还包含完整的项目文档、变更记录、行为准则与 PyPI 发布脚本从数据处理到工具分发均有覆盖是开展 OMR 研究与复现实验的实用基础资源。1. 光学音乐识别数据集集合先弄清楚这个领域卡在哪光学音乐识别Optical Music RecognitionOMR数据集集合是把一批用于训练和评估 OMR 系统的乐谱图像数据集集中整理、统一描述的资源。OMR 要解决的核心问题是让机器看懂扫描版或拍摄版的乐谱像 OCR 之于文字一样把五线谱、音符、强弱记号、歌词这些视觉元素转换成可检索、可播放、可编辑的结构化内容MEI、MusicXML 这类格式。这项技术在数字化馆藏乐谱、音乐教学、古籍乐谱整理等场景里都有实际需求。但 OMR 和 OCR 最大的不同在于乐谱是二维符号系统音符的时值由符头形状和符干方向决定音高由五线谱上的位置决定同一个符号脱离上下文可能根本没有意义所以数据集的质量和标注深度直接决定模型上限。对从业人员来说这份数据集集合的价值在于替你做了一次领域筛选不用再分散去各家论文的附录里翻数据集地址也不用纠结某个数据集到底是单页还是整本、是印刷体还是手写体、标注是像素级还是符号级。适合谁用呢一类是做乐谱数字化系统的算法工程师另一类是音乐信息检索方向的研究生还有一类是想做小规模验证但不想从零扫描标注的独立开发者。接下来的内容会按数据集的类型、格式、转换方法、评估口径、复用技巧一路拆开讲每个环节我都会给出可以直接上手的做法。2. OMR 数据集选型单页到整本、印刷到手写到底怎么挑2.1 先做分类OMR 数据集不完全是一回事OMR 数据集从任务形态上可以粗暴分成四类单页图像分类/检测、序列转录、语义分割、整本乐谱文档级处理。所谓“数据集集合”Collection这类项目常见做法是把这些数据集做成一张索引表附上图像格式、标注格式、声部数量、是否含歌词、版权状态等字段。实际使用时从这张表里挑数据集的顺序应该是先看任务——你是做检测还是识别再看图像——是印刷还是手写、是单声部还是多声部最后看标注——是否有像素级标注还是只有符号级序列。以我处理过的乐谱数据集为例PrIMuS 是印刷体单声部乐谱的序列转录数据集标注是语义化的符号序列MUSCIMA 是手写体乐谱的图形/符号级数据集带像素级掩码和符号边界框适合做语义分割或目标检测Capitan 是整本书级别的数据集包含完整的乐谱页面和对应的语义标注DoReMi 则偏向倾斜校正和版面分析不适合做端到端识别。如果读者对乐谱种类的差异没概念最容易踩的坑是拿着印刷体模型去直接推手写体。印刷乐谱的符头形状、连线曲率、符干长度都相对规整手写乐谱带有个人笔迹变化同一个作者的同一页都能出现符干长短不一致、符头大小浮动更不要提墨迹深浅。所以第一层选型逻辑是你要解决的问题里输入图像是扫描件还是相机拍摄件是 19 世纪雕版印刷还是电脑制版后的打印稿这些直接决定你该用哪个子集。2.2 标注格式带来的兼容性成本OMR 数据集的标注格式五花八门常见的有 PAGE XML、MusicXML、MEI、语义字符串序列以及针对目标检测的边界框 JSON。PAGE XML 主要用在文档分析层面描述版面区域、五线谱区域、谱线位置MEI 是音乐编码标准适合描述音乐内容本身MusicXML 更适合交换到打谱软件里回放。至于 PrIMuS 这类数据集标注通常是一串语义记号字符串——比如“note-whole_G3”把五线谱位置、时值、音名全部编码成一个符号模型在训练时把它当成序列生成任务来做。这意味着什么意味着你选定数据集后第一件要做的事不是搭模型而是统一标注口径。做检测任务需要的是符号边界框和类别做语义分割需要的是像素级掩码做序列转录需要的是图像到符号序列的对齐。如果你只有一个计算平台和有限的标注预算我的建议是优先选带像素级标注的数据集比如 MUSCIMA因为它的标注可以向下兼容成边界框也可以聚合语义信息生成序列灵活性最高。反过来如果只有序列标注想反推回边界框就非常困难基本等于重新标注。2.3 数据集规模与训练实用性的折算OMR 数据集和 CV 里的 ImageNet 相比体量小一个量级。通常一个数据集只有几百页到几千页图像每页的符号数量几十到几百不等。以 PrIMuS 为例单页训练样本数量在万级但实际分到每个类别后低频符号比如装饰音、踏板记号可能只出现几十次。所以做 OMR 时训练一开始就要带上数据增强策略随机裁剪、透视扰动、加入噪声和墨迹污渍模拟这些操作能有效避免模型在小数据集上过拟合。另外一个实际问题是数据集格式的混乱有的给的是 PNG 原始扫描图有的给的是 PDF 页面有的给的是 TIFF 多页文件。我的做法是先把所有图像统一成 PNG分辨率统一到适合模型输入的尺度比如 800×600 或按比例缩放再按数据集来源分别建目录保留一份 manifest 文件记录原始路径和标注路径避免后续把来源搞混。这一层整理工作看起来很琐碎但后面做交叉验证和跨数据集评估时它的价值会成倍释放。3. 把数据集转成模型输入三种落地方案与标注对齐细节3.1 方案一目标检测——从 PIC 标注到 YOLO 格式如果你选的是带符号级边界框的数据集直接转 YOLO 格式是最快能跑通的做法。以 MUSCIMA 为例它的标注里包含符号类别、边界框和轮廓点集但 YOLO 只需要归一化的中心点坐标和宽高。需要注意MUSCIMA 的边界框是图形对象的像素范围有些对象如符干是细长条直接转成水平矩形框会包含大量背景影响正样本质量。常见做法是先取外接矩形再在训练时用较大的 IoU 阈值或者改用旋转框目标检测但旋转框并不是所有框架都方便落地我一般会先试水平框加上足够强的数据增强如果 mAP 上不去再换旋转框方案。# 用 Python 把 PAGE XML 里的边界框转成 YOLO 格式 import xml.etree.ElementTree as ET import os xml_dir path/to/page_xml out_dir path/to/yolo_labels os.makedirs(out_dir, exist_okTrue) class_map {notehead-filled: 0, notehead-empty: 1, stem: 2, beam: 3} for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() image_width int(root.find(.//{http://schema.primaresearch.org/PAGE/gui/v1}ImageWidth).text) image_height int(root.find(.//{http://schema.primaresearch.org/PAGE/gui/v1}ImageHeight).text) with open(os.path.join(out_dir, xml_file.replace(.xml, .txt)), w) as f: for coords in root.iter({http://schema.primaresearch.org/PAGE/gui/v1}Coords): points coords.attrib[points].split() xs [int(p.split(,)[0]) for p in points] ys [int(p.split(,)[1]) for p in points] x_min, x_max, y_min, y_max min(xs), max(xs), min(ys), max(ys) x_center (x_min x_max) / 2 / image_width y_center (y_min y_max) / 2 / image_height width (x_max - x_min) / image_width height (y_max - y_min) / image_height f.write(f{class_map[coords.attrib.get(label, notehead-filled)]} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n)这段代码的作用是把 PAGE XML 里存放的轮廓点集强制转换成 YOLO 需要的归一化矩形框。核心逻辑是先取所有点的最小和最大 x/y求出外接矩形再除以图像宽高完成归一化。参数说明里值得关注的是 class_map 字典类别的映射顺序必须和模型训练配置文件保持一致否则会出现类别错位——这是初学者最常犯的错XML 里类别名是字符串而模型用的是整数索引对不上时训练不会报错但推理结果会张冠李戴。另外PAGE XML 的命名空间前缀在解析时必须写全用花括号括起来否则找不到元素。3.2 方案二语义分割——一次性干完谱线检测和符号分离如果你要做的工作包含谱线移除或者版面分析那就得走语义分割路线。MUSCIMA 提供像素级掩码每个音符头、符干、符杠、连音线都有自己的掩码区域。这里常见的做法是把多类像素掩码合并成几个大类背景、谱线、音符符号、文本、其他。合并的理由很现实——有些类别比如谱线在模型看来并不需要单独成类把它和背景分离开反而会让损失函数更稳定。语义分割方案的输出直接用于后处理比如先分割出谱线掩码然后在原图上做减法得到去谱线的乐谱图像或者把音符掩码送到关联算法里生成符号实例。相比检测方案语义分割对重叠符号比如符头压着连线更鲁棒因为像素级标注天然能处理遮挡。缺点也很明显手工修正掩码的成本极高跨数据集时类别定义可能不一致比如同一个“符头”在一个数据集里被拆成“实心符头”和“空心符头”另一个数据集里统称“符头”。合并类别时一定要做可视化校验不能只对着类别名合并——我遇到过两个数据集的“文本”含义完全不同的情况一个是歌词一个是页眉页码直接合并会把训练集搞脏。# 语义分割模型训练时的损失函数选择以 U-Net 为例 import torch.nn as nn class DiceBCELoss(nn.Module): def __init__(self, smooth1e-6): super().__init__() self.smooth smooth self.bce nn.BCEWithLogitsLoss() def forward(self, logits, targets): probs torch.sigmoid(logits) intersection (probs * targets).sum(dim(2, 3)) union probs.sum(dim(2, 3)) targets.sum(dim(2, 3)) dice (2.0 * intersection self.smooth) / (union self.smooth) bce self.bce(logits, targets) return bce (1 - dice.mean())写这个自定义损失是因为 OMR 分割场景里前景像素占比通常远低于背景纯 BCE 会让模型偏向把一切预测为背景Dice 项则放大了前景区域的梯度贡献。smooth 参数建议保持默认值设置太小在首次训练时可能出现除零问题。注意这里 target 是 float 类型的掩码张量训练时需要同步做归一化和 resize掩码的插值方式建议用 nearest避免类别边界被平滑得模糊不清。3.3 方案三序列转录——把乐谱当成语言翻译来做如果目标是直接输出音乐编码比如 MEI 或语义字符串就属于图像到序列的转录任务。以 PrIMuS 为代表的序列转录数据集把每一页乐谱图像和对应的符号序列配对模型结构一般是 CNN 编码器加 Transformer 或 GRU 解码器。这里有一个关键细节序列标注的顺序不是从左到右简单排列而是按音乐逻辑顺序排的。例如一个和弦三个音可能共享一个符干序列里呈现的顺序和空间顺序不一定一致。所以训练时需要格外注意数据加载器是否每次保持相同的“视图顺序”。我的做法是把数据集里的原始序列原样使用不按图像坐标重排因为重排会破坏音乐语义。实际转换时还需要做一步图像预处理把原始扫描图先二值化再裁剪出五线谱区域否则模型会把扫描件上的污渍当成噪声学进去。# 从 PrIMuS 语义序列构建字典并生成训练样本 from collections import Counter train_sequences [] with open(primus_train.txt, r) as f: for line in f: image_path, sequence line.strip().split(\t) train_sequences.append((image_path, sequence.split())) # 统计符号频率用于构建词表 symbol_counter Counter() for _, seq in train_sequences: symbol_counter.update(seq) min_freq 2 vocab [s for s, c in symbol_counter.items() if c min_freq] vocab [pad, sos, eos, unk] vocab symbol2idx {s: i for i, s in enumerate(vocab)} idx2symbol {i: s for s, i in symbol2idx.items()}这里的 min_freq 参数控制在 2 到 5 之间比较合适。设得太低会把低频噪声符号带进词表增加解码难度设得太高又会把真实但少见的音乐记号比如花舌、震音直接变成unk导致模型永远学不会它们。序列结束符eos必须加否则解码器不知道什么时候停。构建好词表后需要确认所有训练序列里没有超出 max_length 的样本我的经验是 PrIMuS 这类数据集里单页最长序列在 300 个左右把 max_length 设成 400 留出余量避免训练到一半截断报错。4. 训练与评估口径三个指标、两种分割方式结果才不会失真4.1 数据划分整页切割还是随机切块OMR 模型的评估结果很容易受数据划分方式影响尤其是目标检测和语义分割任务。我的建议是先用整页图像的划分方式把每一页完整归入训练集或测试集不做跨页切块。理由很简单同一页内的版面风格、墨迹浓度、扫描噪声高度一致如果训练和测试都包含同一页的不同区域模型相当于提前看到了“考试环境”指标会虚高。随机切块更适合做数据增强或者局部符号识别不适合做最终性能评估。测试集的比例建议占 20%~25%同时保证测试集里包含低频符号类别的出现次数不低于阈值。如果某个符号类别在测试集里只出现一两次模型识别它达到 100% 或者 0% 都没有统计意义——这个在论文里很容易被 “bar-level accuracy” 这种聚合指标掩盖实际工程里不可信。4.2 三个核心指标符号级、音节级、序列级OMR 常用的评估指标分三个层级符号级准确率只算单独符号分类对不对音节级或小节级准确率要求同一个小节内所有符号全部正确才算对序列级准确率要求整页输出和标注完全一致才算对。工程上我通常报告前三者中的符号级和音节级因为序列级接近不可能达到——受限于行对齐和五线谱分页哪怕一个音符的时值点错整页就归零。符号级准确率对小错误宽容适合调参阶段对比模型结构音节级准确率更贴近实用场景毕竟音乐上的“错音”通常以小节为单位被人感知。除了准确率还需要关注未识别率把符号漏掉的比例和误识别率把背景当成符号的比例这两个指标分别对应召回率和精确率的反面。真正上线做数字化时误识别率更麻烦——漏识别还可以靠人工补误识别会被系统直接写进结构化文件后续修改成本高得多。# 计算符号级准确率与混淆矩阵 python - EOF from sklearn.metrics import classification_report, confusion_matrix import numpy as np y_true [note-G3, note-A3, rest, note-G3] y_pred [note-G3, note-A4, rest, note-G3] # 符号级准确率 acc np.mean([t p for t, p in zip(y_true, y_pred)]) print(fSymbol-level accuracy: {acc:.3f}) # 混淆矩阵便于观察高频错误对 report classification_report(y_true, y_pred, zero_division0) print(report) EOF这脚本适合在训练过程中快速验证输出格式。有一点要提醒符号级评估必须按“语义等价”做对齐比如note-G3和note-G3_quarter如果只是时值表示方式不同要归一化后再对比否则你会看到大量其实是正确的预测被判错。深度学习中经常出现模型学会了区分时值、音高、八度但标注文件里把某个休止符时值漏标了——这种“真值错误”在 OMR 数据集里非常多评估时要留出人工抽检环节。4.3 跨数据集验证真正衡量泛化能力如果只在单一数据集的测试集上跑出 90% 以上的准确率先不要高兴太早。OMR 模型的泛化瓶颈通常出现在换数据源时训练用的是合成乐谱渲染图测试用的是真实扫描古籍准确率可能直接掉 20 个百分点。我的做法是至少准备两套评测数据一套是源数据集留出的测试集评估模型在分布内表现另一套是外部数据集即使只有几十页评估跨域表现。这步在标题所述的数据集集合项目中尤其重要——正因为集合了多个来源的数据集才能做真正的跨数据集验证。具体实现时我在训练过程中每三个 epoch 就跑一次外部验证集的符号级准确率并记录到训练日志里。如果外部准确率在停止上升后显著下降说明模型开始记忆内部噪声就回退到最优 checkpoint同时增强数据扰动强度。这套做法能抵消小数据集带来的运气成分判断模型是否真的学到了可迁移的乐谱结构特征。5. 数据集使用避坑格式、缺标注、不均衡的五个教训5.1 标注错位XML 里的坐标单位和图像实际像素不一致这个坑在 OMR 数据集里非常普遍。现象是训练出来的检测模型在推理时所有边界框都偏左上方错位幅度固定。原因大多是 PAGE XML 里记录的坐标是在降采样后的缩略图上标注的或者是 DPI 信息没有写进 XML而代码默认按原始尺寸换算。解决方法是转换格式前先用一张已知图像手工核对坐标点——在原图上画出标注框和真值做肉眼比对。尤其是从数据集集合索引里下载的样本因为来源不统一很容易混入不同 DPI 的标注批次建议每个子数据集独立写一个坐标校验脚本而不是统一处理。5.2 类别不均衡音符头远多于装饰音训练损失被主类吃光现象是模型把所有符号都预测成音符头准确率居然还有 80%。原因是主类符头、符干占训练样本八成以上损失函数被主类支配。解决思路不是单纯改损失权重而是先做类别频率统计把低频类别倚音、琶音、踏板标记挑出来做复制粘贴增强——把低频符号区域裁剪出来随机贴在空白区域或同页其他位置。需要注意粘帖区域不能覆盖其他真实符号否则会造成新的标注错误。5.3 太老的扫描件带有背景伪影模型把斑点学成特征现象是模型在干净图像上表现好一到稍带霉斑、墨渍的古籍扫描件上就疯狂输出假符号。原因是训练时用了过于干净的预处理管线或者二值化阈值固定导致背景伪影被当作前景参与训练。解决方法是训练集中混入带噪声的负样本并且用阈值自适应二值化而不是全局固定阈值。这条在古籍乐谱数字化项目里尤其重要——真实馆藏扫描件什么噪声都有数据集集合里那些看起来漂亮的页面并不能代表真实上线环境。5.4 序列转录的标注顺序和图像上符号顺序不一致现象是模型输出的序列长度正确但音节级准确率极低单看符号级准确率又正常。原因是序列转录数据集的标注顺序遵循音乐逻辑先从左到右再从上到下而模型注意力输出倾向于按图像空间顺序。这类问题没有完全自动的解法只能在数据预处理时保持原始序列顺序不变同时在后处理阶段将预测序列重新排序后再计算正确率。评估时要明确说明用了哪种对齐方式否则和别人论文里的数字没有可比性。5.5 版权与使用边界数据集集合里的数据未必全部可以商用这个坑不在技术但比技术更致命。现象是模型开发顺利上线前被法务叫停。原因是部分数据集来自图书馆数字化项目或者受版权保护的出版物使用条款只允许学术研究。解决方法是选型阶段就阅读每个数据集的许可证单独记录是否允许商用、是否必须注明来源、是否允许修改后分发。整理成一张清单放在工程文档里而不是散落在各处。数据集集合项目一般会标注这些信息但有时也会漏必须自己核对原始站点。6. 进阶拿你自己的扫描件扩展数据集迁移与清洗一条龙当你已经能在现有数据集集合上跑通训练和评估下一件值得做的事就是用自己的扫描件把模型逼到真实场景。这里给一个可复制的扩展流程第一步把扫描件按页切割并命名第二步用现有的检测模型做预标注生成粗略边界框第三步人工抽检和修正错误框第四步把修正后的框作为伪标注加入训练集做一轮微调第五步重复一两次直到模型在误检率上不再有明显下降。具体到操作一定要把预标注置信度阈值设低而不是设高。阈值设成 0.3 会多出一堆误检框但至少不会漏掉真实符号你需要在修正阶段把误检的框删掉这个操作比手工画新框快得多。微调时的学习率建议比从头训练小一个量级比如原训练用 1e-3微调用 1e-4并且只解冻解码器和最后的卷积层避免破坏已经学好的底层视觉特征。如果发现微调后在新数据上识别率反而下降立刻回退到微调前模型再用数据增强强度减半的方式重试。# 预标注过滤与合并脚本控制伪标注质量 import json raw_preds json.load(open(raw_predictions.json)) filtered [] max_overlap 0.4 # 与高置信度框重叠超过此值则丢弃 high_conf [b for b in raw_preds if b[confidence] 0.7] low_conf [b for b in raw_preds if b[confidence] 0.7] def iou(a, b): x1 max(a[x], b[x]); y1 max(a[y], b[y]) x2 min(a[x] a[w], b[x] b[w]) y2 min(a[y] a[h], b[y] b[h]) inter max(0, x2 - x1) * max(0, y2 - y1) union a[w] * a[h] b[w] * b[h] - inter return inter / union if union 0 else 0 for box in low_conf: overlap_flag any(iou(box, ref) max_overlap for ref in high_conf) if not overlap_flag: filtered.append(box) all_boxes high_conf filtered脚本的逻辑是保留置信度高于 0.7 的检测结果对低置信度结果用 IoU 过滤避免与高置信度框重叠的重复框污染训练标签。max_overlap 参数是核心调节项设太大会把一些确实存在的、与邻近符号重叠的低置信度符号误删设太小又会让重复框大量保留。我在不同扫描质量上试下来 0.4 是个折中值如果你手里的扫描件更干净可以放宽到 0.5更脏则收紧到 0.3。最终得到的就是一份可用于微调的伪标注集再配合人工抽检修正就能在几天内把模型逐步迁移到你的目标场景里。最后说一个我自己的习惯不管新做的伪标注效果看着多顺我始终会在每个数据集来源上保留一个单独的验证集绝对不混合训练。原因很简单一旦混合训练模型性能的波动会让人分不清是数据变好还是模型变好。数据集的整理工作要像实验记录本一样每天发生的改动都能追溯回具体文件名。 OMR 这种小数据领域数据整理和模型调优几乎同等重要。希望帮到你。本文还有配套的精品资源点击获取
返回列表