## 背景
当前 L3 直接预测/维护「每小节矩形区域」,在真实敬拜简谱上准确率不理想,常见问题包括:
- 整行被当成一个小节
- 小节框左右边界漂移、与视觉小节线不对齐
- 编辑时拖整框成本高,且框与框之间易重叠/漏缝
- 评估若只看框 IoU,对「少一条线导致整行合并」不够敏感
简谱排版事实:
- 小节边界在主旋律行内主要表现为 **纵向分割(小节线)**
- 和弦区、歌词区往往 **没有** 贯通竖线
- 小节矩形可由 **L2 行区域 + 有序竖线 x 坐标** 推导,不必作为一级自由矩形来回归
因此将 L3 的数据与交互中心从「矩形集合」改为「行内纵向分割线集合」,识别与编辑都围绕分割线。
---
## 目标
1. **数据模型**:L3 在 L2 每一谱行上增加有序 **纵向分割线**(x 位置 + 元数据)。
2. **识别任务**:在给定 **L2 行区域** 上检测小节分割线,再用分割线把该行切成若干 **小节矩形**(派生结果)。
3. **编辑交互**:L3 编辑改为 **左右拖动分割线**;支持 **增加 / 删除** 分割线;小节矩形随线实时更新。
4. **评估标准** 在原有能力上增加线级指标:
- 分割线 **数量** 是否与 GT 一致(或数量误差)
- 分割线 **位置**(匹配后的 **x 坐标偏差**)
### 非目标
- 本 Issue 不要求一次做完美跨行小节语义(可保留扩展字段,实现可二期)
- 不改变 L1/L2 的主职责(页面/谱行)
- 不在本 Issue 重训通用 OCR
- 不强制删除所有旧「measure bbox」存储(可派生兼容,见迁移)
---
## 数据模型变更
### L2(保持为父级)
```text
StaffLine / Row {
line_id,
bbox: {x0, y0, x1, y1}, # 行区域
...
}
L3(主存分割线,矩形为派生)
RowSplitModel {
line_id, # 所属 L2
splits: [
{
split_id,
x, # 图像坐标,相对全图或相对行内需统一约定
source: "detect" | "user",
confidence?: number
}
], # 按 x 升序
# 可选派生缓存(可序列化也可运行时计算)
measures?: [
{ measure_id, x0, x1, y0, y1 } # y 取自 L2 行 bbox;x 由相邻 split 或行边界得到
]
}
切分规则(需写死并单测):
- 对每个 L2 行:有效竖线集合 =
[x_left_bound, ...splits.x, x_right_bound]
- 相邻竖线形成 measure:
[x_i, x_{i+1}] × [y0, y1](y 来自 L2)
- 行首/行尾无用户线时,用 L2 左右边界作为端点
- 删除/移动线后必须重算
measures,保证不重叠、不反向(x 严格递增)
坐标约定(实现前定一种并文档化):
- 推荐:
x 使用与全图一致的像素坐标,便于叠图与双谱;
- 或行内归一化
t ∈ [0,1],展示时再乘行宽——二选一,禁止混用。
识别任务变更
输入
- 原图 + L2 行 bbox 列表(必要)
- 可选:已有粗检测或上一轮 splits
输出
- 每行
splits[](有序 x)
- 派生
measures[] 供下游 L4、Score 对齐
算法要点(实现指引,细节可迭代)
- 在 L2 行内估计 主旋律垂直带(数字密集区),竖线检测主要在此带内进行。
- 垂直形态学 / 投影峰 → 候选 x。
- 过滤:过粗(像数字竖笔画)、过密粘连、过弱噪声。
- 非极大值抑制,按最小间距约束。
- 禁止 再把「直接回归互不约束的 measure 矩形」作为 L3 主路径。
检测失败时:
- 可
splits=[] → 整行一个 measure,并标记低置信 / whole_line,供 UI 提示用户加线。
编辑交互变更
| 操作 |
行为 |
| 拖动分割线 |
仅改 x,夹紧在行内 (x0, x1),不能越过相邻线(或越过则交换序并重排) |
| 增加分割线 |
单击行内位置或「在选中小节中间插入」;写入 source=user |
| 删除分割线 |
选中线删除;端点边界线不可删(若有) |
| 显示 |
主视觉为竖线;小节矩形可半透明派生显示,避免主拖矩形 |
编辑结果写回工程 GT/当前层标注,供评估与单层调优使用。
评估标准变更
在现有「框 IoU」等指标之外,L3 主指标改为线级(以手动编辑的分割线为 GT)。
1. 分割线数量
n_pred, n_gt
count_error = |n_pred - n_gt|
count_exact_rate:数量完全相等的行/页比例
2. 分割线位置(x 偏差)
- 将 pred splits 与 gt splits 做一对一匹配(建议按 x 排序后匈牙利或贪心:代价 =
|x_p - x_g|,超过 max_match_dist 则不匹配)
- 对匹配对:
mean_abs_x_error
median_abs_x_error
- 可选:
p95_abs_x_error
- 未匹配:计入 FP / FN(与数量指标一致)
3. 派生框指标(次要,回归用)
- 由 splits 派生的 measure 与 GT 派生 measure 的 IoU,作为辅助,不取代线级指标。
报告字段示例
{
"layer": "l3",
"split_count_mae": 0.0,
"split_count_exact_rate": 1.0,
"split_mean_abs_x_error": 3.2,
"split_fp": 0,
"split_fn": 0,
"derived_measure_mean_iou": 0.87
}
自动调优单层目标可改为最小化:
loss = w1 * split_count_mae + w2 * split_mean_abs_x_error + w3 * (fp+fn 归一化)
任务清单
验收标准
- 模型:工程中 L3 可序列化保存每行
splits;measures 由 splits + L2 bbox 推导且与 UI 一致。
- 识别:输入 L2 行区域,输出该行 splits;无「主路径直接拟合互不约束的独立小节矩形」。
- 编辑:仅通过左右拖动及增删分割线即可改变小节划分;拖动后派生框正确、无重叠。
- 评估:对同一页手动调线 GT,报告含 分割线数量指标 与 匹配后 mean_abs_x_error。
- 回归:至少 1 个「整行误作一节」样张,在正确 GT 线下评估能反映 pred 少线;人工补线后派生小节数正确。
- 兼容:旧工程若有 measure 框,提供迁移或只读打开策略,并在文档说明。
迁移说明
- 旧数据仅有 measure 矩形:按行聚类后,取相邻矩形的共享边界 x 生成
splits(去重、排序)。
- Score 层若仍消费
measures,保持下游接口读派生 measures,减少 L4 一次性改动。
风险
| 风险 |
缓解 |
| 数字竖笔画被当成分割线 |
限制在旋律带;最小间距;与锚点柱对齐惩罚 |
| 跨行小节 |
模型可留 open_end;本 Issue 先做好行内线 |
| 只改模型不改评估 |
验收强制线级指标进 UI/报告 |
| 编辑与识别坐标不一致 |
统一坐标约定 + 叠图调试 |
相关
- L3 小节划分(旋律带、整行单节、跨行)
- Score 与 L3 对齐
- 单层参数调优循环(GT 改为线后,目标函数切到线级)
- L4 数字锚点(依赖稳定的 measure ROI 派生)
docs/eval-baseline.md
L3(主存分割线,矩形为派生)
切分规则(需写死并单测):
[x_left_bound, ...splits.x, x_right_bound][x_i, x_{i+1}] × [y0, y1](y 来自 L2)measures,保证不重叠、不反向(x 严格递增)坐标约定(实现前定一种并文档化):
x使用与全图一致的像素坐标,便于叠图与双谱;t ∈ [0,1],展示时再乘行宽——二选一,禁止混用。识别任务变更
输入
输出
splits[](有序 x)measures[]供下游 L4、Score 对齐算法要点(实现指引,细节可迭代)
检测失败时:
splits=[]→ 整行一个 measure,并标记低置信 /whole_line,供 UI 提示用户加线。编辑交互变更
(x0, x1),不能越过相邻线(或越过则交换序并重排)source=user编辑结果写回工程 GT/当前层标注,供评估与单层调优使用。
评估标准变更
在现有「框 IoU」等指标之外,L3 主指标改为线级(以手动编辑的分割线为 GT)。
1. 分割线数量
n_pred,n_gtcount_error = |n_pred - n_gt|count_exact_rate:数量完全相等的行/页比例2. 分割线位置(x 偏差)
|x_p - x_g|,超过max_match_dist则不匹配)mean_abs_x_errormedian_abs_x_errorp95_abs_x_error3. 派生框指标(次要,回归用)
报告字段示例
{ "layer": "l3", "split_count_mae": 0.0, "split_count_exact_rate": 1.0, "split_mean_abs_x_error": 3.2, "split_fp": 0, "split_fn": 0, "derived_measure_mean_iou": 0.87 }自动调优单层目标可改为最小化:
任务清单
splits为主;文档说明与 L2 关系splits → measures纯函数 + 边界单测architecture/ ROADMAP 中 L3 描述验收标准
splits;measures由 splits + L2 bbox 推导且与 UI 一致。迁移说明
splits(去重、排序)。measures,保持下游接口读派生 measures,减少 L4 一次性改动。风险
open_end;本 Issue 先做好行内线相关
docs/eval-baseline.md