# 自定义模型与字符集

> 如何加载自己训练的 ONNX 模型和自定义字符集文件，突破内置模型的限制。

- Repository: sml2h3/ddddocr
- GitHub: https://github.com/sml2h3/ddddocr
- Human wiki: https://grok-wiki.com/public/wiki/sml2h3-ddddocr-a34dd45d9f63
- Complete Markdown: https://grok-wiki.com/public/wiki/sml2h3-ddddocr-a34dd45d9f63/llms-full.txt

## Source Files

- `ddddocr/models/model_loader.py`
- `ddddocr/models/charset_manager.py`
- `ddddocr/core/ocr_engine.py`
- `ddddocr/compat/v1.py`

---

<details>
<summary>相关源文件</summary>
以下文件用于生成此维基页面：
- [ddddocr/models/model_loader.py](ddddocr/models/model_loader.py)
- [ddddocr/models/charset_manager.py](ddddocr/models/charset_manager.py)
- [ddddocr/core/ocr_engine.py](ddddocr/core/ocr_engine.py)
- [ddddocr/compat/v1.py](ddddocr/compat/v1.py)
- [ddddocr/core/base.py](ddddocr/core/base.py)
</details>

# 自定义模型与字符集

ddddocr 内置了预训练的 OCR 模型和字符集，覆盖常见的中文验证码场景。但当你面对特殊字符集（如纯数字、纯字母、自定义符号）或需要更高识别精度时，加载自己训练的 ONNX 模型和配套字符集文件是突破内置限制的核心方式。本页介绍自定义模型与字符集的加载机制、字符集文件格式要求，以及加载后的预处理行为变化。

## 入口与参数

加载自定义模型有两个关键参数：`import_onnx_path`（ONNX 模型文件路径）和 `charsets_path`（字符集 JSON 文件路径）。当 `import_onnx_path` 非空时，引擎跳过内置模型，转而加载用户指定的模型和字符集。

通过兼容层 `DdddOcr` 类使用时，这两个参数直接传入构造函数：

```python
import ddddocr

ocr = ddddocr.DdddOcr(
    import_onnx_path="/path/to/custom.onnx",
    charsets_path="/path/to/charset.json"
)
result = ocr.classification(image_bytes)
```

Sources: [ddddocr/compat/v1.py:26-42]()、[ddddocr/compat/v1.py:77-87]()

当 `import_onnx_path` 非空时，即使 `ocr=False`，引擎也会初始化 OCR 功能（v1 兼容层中的 `ocr or import_onnx_path` 判断）。

Sources: [ddddocr/compat/v1.py:77]()

## 自定义模型加载流程

`OCREngine.initialize()` 根据 `use_import_onnx` 标志走两条不同的初始化路径：

```text
┌─────────────────────────────────────────────────────────┐
│                  OCREngine.initialize()                  │
├──────────────────────┬──────────────────────────────────┤
│  use_import_onnx     │  use_import_onnx = False         │
│  = True              │                                  │
├──────────────────────┼──────────────────────────────────┤
│ load_custom_model()  │ load_ocr_model(old/beta)         │
│ → ONNX session       │ → ONNX session                   │
│                      │                                  │
│ charset_info['charset│ load_default_charset(old/beta)   │
│ '] → charset         │ → 硬编码字符集                    │
│                      │                                  │
│ charset_info['word'] │ word = False                     │
│ → self.word          │                                  │
│                      │                                  │
│ charset_info['image']│ resize = [64, 64]                │
│ → self.resize        │                                  │
│                      │                                  │
│ charset_info['       │ channel = 1                      │
│ channel'] → channel  │                                  │
└──────────────────────┴──────────────────────────────────┘
```

Sources: [ddddocr/core/ocr_engine.py:56-97]()

`ModelLoader.load_custom_model()` 负责实际的文件加载工作。它先调用 `load_model()` 加载 ONNX 文件，再读取并验证字符集 JSON 文件，最后返回 `(session, charset_info)` 元组。

Sources: [ddddocr/models/model_loader.py:170-204]()

## 字符集 JSON 文件格式

自定义字符集文件必须是 UTF-8 编码的 JSON 文件，且包含以下四个必需字段：

| 字段 | 类型 | 说明 |
|------|------|------|
| `charset` | `List[str]` | 字符列表，索引 0 对应 CTC blank 字符（通常为空字符串 `""`） |
| `word` | `bool` | 是否为单词/词组模式，影响图像预处理的缩放策略 |
| `image` | `List[int]` | 目标图像尺寸 `[width, height]`，`-1` 表示自适应宽度 |
| `channel` | `int` | 图像通道数，`1` 为灰度，`3` 为 RGB |

字符集文件在两处被独立验证——`ModelLoader.load_custom_model()` 和 `CharsetManager.load_custom_charset()` 都会检查这四个必需字段是否存在。

Sources: [ddddocr/models/model_loader.py:196-199]()、[ddddocr/models/charset_manager.py:67-71]()

### 示例字符集文件

```json
{
  "charset": ["", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
  "word": false,
  "image": [-1, 64],
  "channel": 1
}
```

这个示例定义了一个纯数字识别模型：字符集包含空字符（CTC blank）加 10 个数字，图像高度固定 64 像素、宽度自适应，灰度输入。

## 图像预处理差异

加载自定义模型后，图像预处理行为会根据字符集配置自动调整，主要体现在尺寸缩放和通道转换两方面：

**尺寸缩放**（由 `image` 和 `word` 字段共同控制）：

- `image[0] == -1` 且 `word == True`：按正方形缩放，边长为 `image[1]`
- `image[0] == -1` 且 `word == False`：高度固定为 `image[1]`，宽度按原始宽高比自适应
- `image[0] != -1`：强制缩放到 `image[0] × image[1]`

**通道转换**（由 `channel` 字段控制）：

- `channel == 1`：转换为灰度图
- `channel != 1`：保持原始通道（如 RGB 三通道）

Sources: [ddddocr/core/ocr_engine.py:182-197]()

相比之下，内置模型始终使用固定的 64 像素高度、按比例缩放宽度、灰度输入。

Sources: [ddddocr/core/ocr_engine.py:177-181]()

## CTC 解码与字符映射

模型输出经 CTC（Connectionist Temporal Classification）解码后，通过字符集索引映射为最终文本。解码过程分两步：

1. **索引级去重**：去除连续重复的预测索引，并跳过索引 0（blank 字符）
2. **字符映射**：将有效索引映射到字符集中的对应字符

```python
# CTC 解码核心逻辑
for idx in predicted_indices:
    idx = int(idx)
    if idx != prev_idx:      # 跳过连续重复
        if idx != 0:          # 跳过 blank（索引 0）
            decoded_indices.append(idx)
    prev_idx = idx
```

Sources: [ddddocr/core/ocr_engine.py:300-329]()

如果设置了字符集范围限制（`charset_range`），解码后还会过滤掉不在指定范围内的索引。

Sources: [ddddocr/core/ocr_engine.py:283-288]()

## 字符集范围限制

加载自定义模型后，仍然可以通过 `set_ranges()` 方法限制输出字符范围。支持三种传参方式：

| 参数类型 | 示例 | 行为 |
|----------|------|------|
| `int` | `set_ranges(10)` | 只使用字符集的前 11 个字符（索引 0-10） |
| `str` | `set_ranges("0123456789")` | 只输出字符串中包含的字符 |
| `List[str]` | `set_ranges(["0","1","2"])` | 只输出列表中指定的字符 |

Sources: [ddddocr/models/charset_manager.py:83-111]()

范围限制在推理时生效，不会修改原始字符集，调用 `clear_ranges()` 可恢复完整字符集。

## GPU 支持

自定义模型同样支持 GPU 加速。在构造 `DdddOcr` 时设置 `use_gpu=True` 即可，ONNX Runtime 会自动选择 CUDA 执行提供者。如果 GPU 不可用，会自动回退到 CPU 模式。

Sources: [ddddocr/models/model_loader.py:31-53]()

## 常见问题

**Q：字符集文件缺少字段会怎样？**
会抛出 `ModelLoadError`，提示缺少的具体字段名。

**Q：`charset` 列表的第一个元素必须是空字符串吗？**
推荐是空字符串，因为 CTC 解码假设索引 0 为 blank 字符。如果你的模型训练时 blank 对应其他值，需要相应调整。

**Q：能否同时使用 `old`/`beta` 和自定义模型？**
不能。当 `import_onnx_path` 非空时，`old` 和 `beta` 参数被忽略，引擎直接使用自定义模型。

**Q：字符集大小必须和模型输出维度匹配吗？**
是的。`charset` 列表的长度应与模型最后一层的输出类别数一致，否则索引映射会产生错误结果。

## 总结

ddddocr 的自定义模型支持通过 `import_onnx_path` 和 `charsets_path` 两个参数实现。核心要求是提供一个符合格式的字符集 JSON 文件（包含 `charset`、`word`、`image`、`channel` 四个字段），以及一个与字符集匹配的 ONNX 模型。加载后，引擎会根据字符集配置自动调整图像预处理策略，通过 CTC 解码完成从模型输出到文本的映射。字符集范围限制功能在自定义模型下同样可用，可用于进一步约束输出空间。
