# Design: OCR Track Gap Filling

## Context

PP-StructureV3 版面分析模型在處理某些掃描文件時會嚴重漏檢。實測顯示 Raw PaddleOCR 能偵測 56 個文字區域，但 PP-StructureV3 僅輸出 9 個元素（遺失 84%）。

問題發生在 PP-StructureV3 內部的 Layout Detection Model，這是 PaddleOCR 函式庫的限制，無法從外部修復。但 Raw OCR 的 `text_regions` 資料仍然完整可用。

### Stakeholders
- **End users**: 需要完整的 OCR 輸出，不能有大量文字遺失
- **OCR track**: 需要整合 Raw OCR 與 PP-StructureV3 結果
- **Direct/Hybrid track**: 不應受此變更影響

## Goals / Non-Goals

### Goals
- 偵測 PP-StructureV3 漏檢區域並以 Raw OCR 結果補回
- 確保補回的文字不會與現有元素重複
- 維持正確的閱讀順序
- 僅影響 OCR track，不改變其他 track 的行為

### Non-Goals
- 不修改 PP-StructureV3 或 PaddleOCR 內部邏輯
- 不處理圖片/表格/圖表等非文字元素的補漏
- 不實作複雜的版面分析（僅做 gap filling）

## Decisions

### Decision 1: 覆蓋判定策略
**選擇**: 優先使用「中心點落入」判定，輔以 IoU 閾值

**理由**:
- 中心點判定計算簡單，效能好
- IoU 閾值作為補充，處理邊界情況
- 建議 IoU 閾值 0.1~0.2，避免低 IoU 被誤判為未覆蓋

**替代方案**:
- 純 IoU 判定：計算量較大，且對部分重疊的處理較複雜
- 面積比例判定：對不同大小的區域不夠公平

### Decision 2: 補漏觸發條件
**選擇**: 當 PP-Structure 覆蓋率 < 70% 或元素數顯著低於 Raw OCR

**理由**:
- 避免正常文件出現重複文字
- 70% 閾值經驗值，可透過設定調整
- 元素數比較作為快速判斷條件

### Decision 3: 補漏元素類型
**選擇**: 僅補 TEXT 類型，跳過 TABLE/IMAGE/FIGURE/FLOWCHART/HEADER/FOOTER

**理由**:
- PP-StructureV3 對結構化元素（表格、圖片）的識別通常較準確
- 補回原始 OCR 文字可能破壞表格結構
- 這些元素需要保持結構完整性

### Decision 4: 重複判定與去重
**選擇**: IoU > 0.5 的 Raw OCR 區域視為與 PP-Structure TEXT 重複，跳過

**理由**:
- 0.5 是常見的重疊閾值
- 避免同一文字出現兩次
- 對細碎的 Raw OCR 框可考慮輕量合併

### Decision 5: 座標對齊
**選擇**: 使用 `ocr_dimensions` 進行 bbox 換算

**理由**:
- OCR 可能有 resize 處理
- 確保 Raw OCR 與 PP-Structure 的座標在同一空間
- 避免因尺寸不一致導致覆蓋誤判

## Data Flow

```
┌─────────────────┐     ┌──────────────────────┐
│  Raw OCR Result │     │ PP-StructureV3 Result│
│  (56 regions)   │     │    (9 elements)      │
└────────┬────────┘     └──────────┬───────────┘
         │                         │
         └────────────┬────────────┘
                      │
              ┌───────▼───────┐
              │ GapFillingService │
              │ 1. Calculate coverage
              │ 2. Find uncovered regions
              │ 3. Filter by confidence
              │ 4. Deduplicate
              │ 5. Merge if needed
              └───────┬───────┘
                      │
              ┌───────▼───────┐
              │ OCRToUnifiedConverter │
              │ - Combine elements
              │ - Recalculate reading order
              └───────┬───────┘
                      │
              ┌───────▼───────┐
              │ UnifiedDocument │
              │ (complete content)
              └───────────────┘
```

## Algorithm: Gap Detection

```python
def find_uncovered_regions(
    raw_ocr_regions: List[TextRegion],
    pp_structure_elements: List[Element],
    iou_threshold: float = 0.15
) -> List[TextRegion]:
    """
    Find Raw OCR regions not covered by PP-Structure elements.

    Coverage criteria (either one):
    1. Center point of raw region falls inside any PP-Structure bbox
    2. IoU with any PP-Structure bbox > iou_threshold
    """
    uncovered = []

    # Filter PP-Structure elements: only consider TEXT, skip TABLE/IMAGE/etc.
    text_elements = [e for e in pp_structure_elements
                     if e.type not in SKIP_TYPES]

    for region in raw_ocr_regions:
        center = get_center(region.bbox)
        is_covered = False

        for element in text_elements:
            # Check center point
            if point_in_bbox(center, element.bbox):
                is_covered = True
                break

            # Check IoU
            if calculate_iou(region.bbox, element.bbox) > iou_threshold:
                is_covered = True
                break

        if not is_covered:
            uncovered.append(region)

    return uncovered
```

## Configuration Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `gap_filling_enabled` | bool | True | 是否啟用 gap filling |
| `gap_filling_coverage_threshold` | float | 0.7 | 覆蓋率低於此值時啟用 |
| `gap_filling_iou_threshold` | float | 0.15 | 覆蓋判定 IoU 閾值 |
| `gap_filling_confidence_threshold` | float | 0.3 | Raw OCR 信心度門檻 |
| `gap_filling_dedup_iou_threshold` | float | 0.5 | 去重 IoU 閾值 |

## Risks / Trade-offs

### Risk 1: 補漏造成文字重複
**Mitigation**: 設定 dedup_iou_threshold，對高重疊區域進行去重

### Risk 2: 閱讀順序錯亂
**Mitigation**: 補回元素後重新計算整頁的 reading_order（依 y0, x0 排序）

### Risk 3: 效能影響
**Mitigation**:
- 先做快速的覆蓋率檢查，若 > 70% 則跳過 gap filling
- 使用 R-tree 或 interval tree 加速 bbox 查詢（若效能成為瓶頸）

### Risk 4: 座標不對齊
**Mitigation**: 使用 `ocr_dimensions` 確保座標空間一致

## Migration Plan

1. 新增功能為可選（預設啟用）
2. 可透過設定關閉 gap filling
3. 不影響現有 API 介面
4. 向後相容：不傳參數時使用預設行為

## Open Questions

1. 是否需要 UI 開關讓使用者選擇啟用/停用 gap filling？
2. 對於細碎的 Raw OCR 框，是否需要實作合併邏輯？（同行、相鄰且間距很小）
3. 是否需要在輸出中標記哪些元素是補漏來的？（debug 用途）
