Skip to content

refactor(core/desktop): L3 改为「L2 行内纵向分割线」模型(识别切分 + 拖线编辑 + 线级评估) #85

Description

@loootte
## 背景

当前 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 对齐

算法要点(实现指引,细节可迭代)

  1. 在 L2 行内估计 主旋律垂直带(数字密集区),竖线检测主要在此带内进行。
  2. 垂直形态学 / 投影峰 → 候选 x。
  3. 过滤:过粗(像数字竖笔画)、过密粘连、过弱噪声。
  4. 非极大值抑制,按最小间距约束。
  5. 禁止 再把「直接回归互不约束的 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 归一化)

任务清单

  • 更新 schema / 工程文件:L3 以 splits 为主;文档说明与 L2 关系
  • 实现 splits → measures 纯函数 + 边界单测
  • L3 识别改为「L2 行内检纵向分割线」主路径;输出 splits + 派生 measures
  • 桌面 L3 编辑:拖线、增线、删线;矩形只读或随动
  • GT 保存/加载与手动标注兼容(旧 measure 框可迁移:用相邻框边界生成 splits 的一次性转换脚本)
  • 评估:数量误差 + x 偏差 + 匹配 FP/FN;接入现有评估面板
  • (可选)单层调优空间改为 split 检测相关参数
  • 更新 architecture / ROADMAP 中 L3 描述

验收标准

  1. 模型:工程中 L3 可序列化保存每行 splitsmeasures 由 splits + L2 bbox 推导且与 UI 一致。
  2. 识别:输入 L2 行区域,输出该行 splits;无「主路径直接拟合互不约束的独立小节矩形」。
  3. 编辑:仅通过左右拖动及增删分割线即可改变小节划分;拖动后派生框正确、无重叠。
  4. 评估:对同一页手动调线 GT,报告含 分割线数量指标匹配后 mean_abs_x_error
  5. 回归:至少 1 个「整行误作一节」样张,在正确 GT 线下评估能反映 pred 少线;人工补线后派生小节数正确。
  6. 兼容:旧工程若有 measure 框,提供迁移或只读打开策略,并在文档说明。

迁移说明

  • 旧数据仅有 measure 矩形:按行聚类后,取相邻矩形的共享边界 x 生成 splits(去重、排序)。
  • Score 层若仍消费 measures,保持下游接口读派生 measures,减少 L4 一次性改动。

风险

风险 缓解
数字竖笔画被当成分割线 限制在旋律带;最小间距;与锚点柱对齐惩罚
跨行小节 模型可留 open_end;本 Issue 先做好行内线
只改模型不改评估 验收强制线级指标进 UI/报告
编辑与识别坐标不一致 统一坐标约定 + 叠图调试

相关

  • L3 小节划分(旋律带、整行单节、跨行)
  • Score 与 L3 对齐
  • 单层参数调优循环(GT 改为线后,目标函数切到线级)
  • L4 数字锚点(依赖稳定的 measure ROI 派生)
  • docs/eval-baseline.md

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions