---
title: pp-lcnet-doc-ori
canonical_url: "https://www.modelscope.cn/models/farming789/pp-lcnet-doc-ori"
md_url: "https://www.modelscope.cn/models/farming789/pp-lcnet-doc-ori.md"
repository: farming789/pp-lcnet-doc-ori
chinese_name: "文档整图方向分类器 ONNX 版"
last_updated: 2026-05-16
license: apache-2.0
pipeline_tag: image-classification
tasks:
  - image-classification
library_name:
  - onnx
  - other
frameworks:
  - other
language:
  - zh
  - en
downloads: 9
stars: 2
tags:
  - pp-lcnet
  - paddleocr
  - onnx
  - document-orientation
  - image-classification
  - ocr-preprocessing
  - legal-document
---

# pp-lcnet-doc-ori

> pp-lcnet-doc-ori - farming789 在 ModelScope 开源的模型。本模型源自 **PaddleX / PaddleOCR** 生态的 **[PP-LCNet_x1_0_doc_ori](https://github.com/PaddlePaddle/PaddleOCR)** 文档方向分类器，已转换为 **ONNX** 格式，专为**离线本地推理**场景优化。

farming789/pp-lcnet-doc-ori 是 ModelScope 魔搭社区上的image-classification模型，采用 apache-2.0 许可。

- **Repository**: farming789/pp-lcnet-doc-ori
- **License**: apache-2.0
- **Tasks**: image-classification
- **Tags**: pp-lcnet, paddleocr, onnx, document-orientation, image-classification, ocr-preprocessing, legal-document
- **Downloads**: 9
- **Stars**: 2
- **Last updated**: 2026-05-16

Source: https://www.modelscope.cn/models/farming789/pp-lcnet-doc-ori

---

# PP-LCNet DocOri —— 文档整图方向分类器 ONNX 版

## 模型简介

本模型源自 **PaddleX / PaddleOCR** 生态的 **[PP-LCNet_x1_0_doc_ori](https://github.com/PaddlePaddle/PaddleOCR)** 文档方向分类器，已转换为 **ONNX** 格式，专为**离线本地推理**场景优化。

在 OCR 流水线中，扫描件、手机拍照、传真件等文档图像可能存在 90°、180° 甚至 270° 的旋转。如果直接将歪斜图像送入 OCR 引擎，文本识别准确率会大幅下降。本模型作为 OCR 前置步骤，**自动判断文档图像的朝向并矫正为正向**。

**核心价值**：在纯 CPU 环境下，以毫秒级延迟完成文档方向四分类（0°/90°/180°/270°），提升下游 OCR 识别质量，无需依赖 GPU 或 PaddlePaddle 运行时。

---

## 模型文件

| 文件 | 大小 | 说明 |
|------|------|------|
| `model.onnx` | ~6.5 MB | ONNX 模型（包含图结构与权重） |

> 单一文件，无外部数据依赖，直接加载即可推理。

---

## 模型架构

- **基础模型**：PP-LCNet_x1_0_doc_ori（百度 PaddleClas）
- **网络结构**：PP-LCNet（轻量级 CPU 友好 CNN）
- **输入规格**：
  - 尺寸：`224 × 224` RGB 图像
  - 预处理：短边等比缩放至 256 → 中心裁剪 224 → ImageNet 归一化
  - 归一化参数：`mean=[0.485, 0.456, 0.406]`, `std=[0.229, 0.224, 0.225]`
- **输出**：`float32[1, 4]` 四类 softmax 分数
- **类别映射**：

| class index | 角度 | 含义 |
|:-----------:|:----:|------|
| 0 | 0° | 正向（无需旋转） |
| 1 | 90° | 顺时针旋转 90° |
| 2 | 180° | 倒置 |
| 3 | 270° | 逆时针旋转 90°（或顺时针 270°） |

工作流程：

```
输入图像（任意朝向、任意分辨率）
    │
    ▼
┌─────────────────────────────────┐
│ 预处理                           │
│  resize(短边=256)                │
│  → centerCrop(224×224)          │
│  → normalize(ImageNet mean/std)  │
│  → NCHW float32[1,3,224,224]   │
└─────────────┬───────────────────┘
              ▼
┌─────────────────────────────────┐
│ ONNX 推理 (PP-LCNet)            │
│  输出: float32[1, 4]            │
└─────────────┬───────────────────┘
              ▼
┌─────────────────────────────────┐
│ argmax → 角度                    │
│  → 反向旋转矫正                  │
│  → 输出正向图像                  │
└─────────────────────────────────┘
```

---

## 快速开始

### 环境要求

- `onnxruntime` >= 1.24.3
- 图像预处理：`numpy` + `PIL` / `opencv-python`（Python），或 `@napi-rs/canvas` + `sharp`（Node.js）

### Python 示例

```python
import onnxruntime as ort
import numpy as np
from PIL import Image

# 1. 加载模型
model_path = "./pp-lcnet-doc-ori/model.onnx"
session = ort.InferenceSession(model_path)

# 2. 图像预处理
def preprocess(image_path: str) -> np.ndarray:
    """短边缩放 256 → 中心裁剪 224 → ImageNet 归一化 → NCHW"""
    img = Image.open(image_path).convert("RGB")
    w, h = img.size

    # 短边等比缩放
    scale = 256 / min(w, h)
    new_w, new_h = int(round(w * scale)), int(round(h * scale))
    img = img.resize((new_w, new_h), Image.BILINEAR)

    # 中心裁剪 224
    left = (new_w - 224) // 2
    top = (new_h - 224) // 2
    img = img.crop((left, top, left + 224, top + 224))

    # 归一化 (ImageNet)
    mean = np.array([0.485, 0.456, 0.406], dtype=np.float32)
    std  = np.array([0.229, 0.224, 0.225], dtype=np.float32)
    arr  = np.array(img, dtype=np.float32) / 255.0
    arr  = (arr - mean) / std

    # HWC → NCHW
    return np.expand_dims(arr.transpose(2, 0, 1), axis=0)

# 3. 推理
input_tensor = preprocess("document.jpg")
input_name  = session.get_inputs()[0].name
outputs     = session.run(None, {input_name: input_tensor})
scores      = outputs[0][0]  # [4]

# 4. argmax → 角度
ANGLE_MAP = {0: 0, 1: 90, 2: 180, 3: 270}
pred_class = int(np.argmax(scores))
angle = ANGLE_MAP[pred_class]

print(f"预测角度: {angle}°")
print(f"各类置信度: 0°={scores[0]:.3f}, 90°={scores[1]:.3f}, 180°={scores[2]:.3f}, 270°={scores[3]:.3f}")
```

### TypeScript / Node.js 示例

```typescript
import * as ort from "onnxruntime-node";
import { createCanvas, loadImage } from "@napi-rs/canvas";

// 归一化参数
const MEAN = [0.485, 0.456, 0.406];
const STD  = [0.229, 0.224, 0.225];
const INPUT_SIZE = 224;
const RESIZE_SHORT = 256;

async function preprocessImage(buffer: ArrayBuffer): Promise<Float32Array> {
  const img = await loadImage(Buffer.from(buffer));
  const w = img.width, h = img.height;

  // 短边缩放
  const scale = RESIZE_SHORT / Math.min(w, h);
  const newW = Math.round(w * scale), newH = Math.round(h * scale);
  const resizeCanvas = createCanvas(newW, newH);
  resizeCanvas.getContext("2d").drawImage(img, 0, 0, newW, newH);

  // 中心裁剪
  const sx = Math.max(0, Math.floor((newW - INPUT_SIZE) / 2));
  const sy = Math.max(0, Math.floor((newH - INPUT_SIZE) / 2));
  const cropCanvas = createCanvas(INPUT_SIZE, INPUT_SIZE);
  cropCanvas.getContext("2d").drawImage(resizeCanvas, sx, sy, INPUT_SIZE, INPUT_SIZE, 0, 0, INPUT_SIZE, INPUT_SIZE);

  const pixels = cropCanvas.getContext("2d").getImageData(0, 0, INPUT_SIZE, INPUT_SIZE).data;

  // NCHW + 归一化
  const plane = INPUT_SIZE * INPUT_SIZE;
  const out = new Float32Array(3 * plane);
  for (let i = 0; i < plane; i++) {
    out[i]            = (pixels[i * 4] / 255 - MEAN[0]) / STD[0];
    out[plane + i]    = (pixels[i * 4 + 1] / 255 - MEAN[1]) / STD[1];
    out[plane * 2 + i] = (pixels[i * 4 + 2] / 255 - MEAN[2]) / STD[2];
  }
  return out;
}

// 使用
const session = await ort.InferenceSession.create("./pp-lcnet-doc-ori/model.onnx");
const tensorData = await preprocessImage(imageBuffer);
const tensor = new ort.Tensor("float32", tensorData, [1, 3, 224, 224]);

const inputName  = session.inputNames[0];
const outputName = session.outputNames[0];
const outputs = await session.run({ [inputName]: tensor });
const scores = outputs[outputName].data as Float32Array;

const ANGLE_MAP = { 0: 0, 1: 90, 2: 180, 3: 270 };
const predClass = scores.indexOf(Math.max(...scores));
console.log(`预测角度: ${ANGLE_MAP[predClass]}°`);
```

---

## 应用场景

### OCR 前置方向矫正

- **扫描件处理**：律所扫描的合同、判决书、证据材料可能存在各种朝向
- **手机拍照**：律师用手机拍摄的案卷材料常常歪斜
- **传真件数字化**：传真机输出的文档角度不可控
- **批量文档自动化**：大批量文档入库前自动统一朝向，无需人工逐页检查

### 降级策略

当模型文件未下载或推理失败时，系统自动跳过方向矫正，按 0°（正向）处理，不阻断下游 OCR 流程：

```
模型可用 → classify() → rotateToUpright() → 矫正图像 → OCR
模型不可用 → 跳过矫正 → 原图直接送 OCR
```

---

## 技术细节

### 网络结构

PP-LCNet（PP-Lightweight CPU Network）是百度提出的**轻量级图像分类网络**，设计目标为 CPU 友好：

- 使用深度可分离卷积 + SE 注意力模块
- 比 MobileNetV3 更快的推理速度
- 专为 ARM / x86 CPU 优化
- 在本项目中，单次推理耗时 < 50ms（桌面 CPU）

### 训练数据

原模型由 PaddleX 团队使用**文档图像方向分类数据集**训练，覆盖中英文文档、表格、票据、证件等多种文档类型。

### 与官方 Paddle 模型的关系

| 方面 | 官方 Paddle 模型 | 本模型 (ONNX) |
|------|:---------------:|:------------:|
| 运行时 | PaddlePaddle | ONNX Runtime |
| 格式 | inference.pdmodel | model.onnx |
| 依赖 | paddlepaddle (~500MB) | onnxruntime (~30MB) |
| GPU | 支持 | CPU only |
| 推理速度 (CPU) | ~80ms | ~50ms |
| 适合桌面应用 | ❌ 运行时过大 | ✅ 轻量集成 |

### ONNX 转换说明

本模型从 PaddlePaddle 原生格式通过 `paddle2onnx` 工具转换而来：

- **paddle2onnx**：2.1.0
- **paddlepaddle**：2.6.2 / 3.0.0 nightly
- **转换命令**：`paddle2onnx --model_dir ./ --model_filename inference.pdmodel --save_file model.onnx`

> **已知问题**：PaddlePaddle 3.0 部分模型包使用 `inference.json` 替代 `inference.pdmodel`，Windows 环境下 `paddle2onnx 2.1.0` 要求 paddlepaddle ≥ 3.0.0.dev，且可能因 MSVC 运行时缺失导致转换中断。

---

## 许可证

本模型遵循 [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)，与 PaddleOCR / PaddleX 官方保持一致。

---

## 引用

如果你在研究中使用了本模型，请引用 PaddleOCR：

```bibtex
@misc{paddleocr,
  title={PaddleOCR: Awesome Multilingual OCR Toolkits Based on PaddlePaddle},
  author={PaddlePaddle Authors},
  howpublished={\url{https://github.com/PaddlePaddle/PaddleOCR}},
  year={2024}
}

@article{pp-lcnet,
  title={PP-LCNet: A Lightweight CPU Convolutional Neural Network},
  author={Cui, Cheng and Gao, Tingquan and Wei, Shengyu and Du, Yuning and Guo, Ruoyu and Dong, Shuilong and Lu, Bin and Zhou, Ying and Lv, Xueying and Liu, Qiwen and others},
  journal={arXiv preprint arXiv:2109.15099},
  year={2021}
}
```

---

## 致谢

- 原始模型：[PaddleOCR / PaddleX](https://github.com/PaddlePaddle/PaddleOCR)（百度）
- ONNX 转换：基于 [twmht/PP-LCNet_x1_0_doc_ori_infer_onnx](https://huggingface.co/twmht/PP-LCNet_x1_0_doc_ori_infer_onnx) 二次整理
- 转换工具：[paddle2onnx](https://github.com/PaddlePaddle/paddle2onnx)
- 运行时：[ONNX Runtime](https://onnxruntime.ai/)
