0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PaddleOCRのモデルをONNXにエクスポートして推論する!

0
Last updated at Posted at 2026-05-21

PaddleOCRすげー!

最近OCRしたいと思っていて、いろいろグルグルと彷徨った結果、PaddleOCRが合ってるんじゃないか?と思って。
これをONNXにエクスポートして、C++で推論したい!

全然うまくいかないONNXエクスポート!

PaddleOCRはなんだか独自な感じで、paddle2onnxというパッケージを使うみたいなんですが、なんせ全然うまくいかない!
ONNXエクスポートできるまでかなり彷徨ったので、日本語の記録として残すものです。

私が欲しかったのはrecだけなのですが、detでもたぶん同じです。

環境!

Windows11
Python3.11

手順!

  1. PaddleOCR を GitHub から clone!

    git clone https://github.com/PaddlePaddle/PaddleOCR.git
    cd PaddleOCR
    

  2. 必要な依存をインストール!

    pip install -r requirements.txt
    

  3. エクスポートスクリプトを実行!

    python tools/export_model.py \
        -c .//configs//rec//PP-OCRv5//multi_language//en_PP-OCRv5_mobile_rec.yaml \
        -o Global.save_inference_dir=./inference/rec
    

    20260522追記:
    よくみたら
    下記に学習済みデータが置いてありました。これをダウンロードしてONNXにエクスポートするのが正解ぽいです

    https://www.paddleocr.ai/latest/en/version3.x/pipeline_usage/OCR.html


  4. Paddle2ONNXが正しく動くバージョンのパッケージをインストール!

    pip install PaddlePaddle==3.1.1 PaddleX==3.3.13 Paddle2onnx==2.1.0
    

  5. Paddle2ONNXでエクスポート!

    paddle2onnx_export.py
    import paddle2onnx
    
    paddle2onnx.export(
    model_filename=".//inference//rec//inference.json",
    params_filename=".//inference//rec//inference.pdiparams",
    save_file=".//ppocr-v5-rec.onnx",
    opset_version=11,
    enable_onnx_checker=True
    )
    

  6. 実行時のログ!
    途中、勝手にいろいろインストールされます。

    log
    (venv) PS D:\PaddleOCR> python test.py
    INFO: Could not find files for the given pattern(s).
    D:\PaddleOCR\venv\Lib\site-packages\paddle\utils\cpp_extension\extension_utils.py:717: UserWarning: No ccache found. Please be aware that recompiling all source files may be required. You can download and install ccache from: https://github.com/ccache/ccache/blob/master/doc/INSTALL.md
      warnings.warn(warning_message)
    [Paddle2ONNX] Start parsing the Paddle model file...
    [Paddle2ONNX] Use opset_version = 11 for ONNX export.
    [Paddle2ONNX] PaddlePaddle model is exported as ONNX format now.
    2026-05-21 21:27:04 [INFO]      Try to perform constant folding on the ONNX model with Polygraphy.
    [W] 'colored' module is not installed, will not use colors when logging. To enable colors, please install the 'colored' module: python3 -m pip install colored
    [I] Module: 'onnxruntime' is required, but not installed. Attempting to install now.
    [I] Running installation command: D:\PaddleOCR\venv\Scripts\python.exe -m pip install onnxruntime>=1.10.0
    [I] Folding Constants | Pass 1
    [I] Module: 'onnx_graphsurgeon' is required, but not installed. Attempting to install now.
    [I] Running installation command: D:\PaddleOCR\venv\Scripts\python.exe -m pip install onnx_graphsurgeon>=0.3.27 --extra-index-url=https://pypi.ngc.nvidia.com
    [I] Module: 'sympy' is required, but not installed. Attempting to install now.
    [I] Running installation command: D:\PaddleOCR\venv\Scripts\python.exe -m pip install sympy
    [I] Module: 'onnxruntime.tools.symbolic_shape_infer' is required, but not installed. Attempting to install now.
    [I] Running installation command: D:\PaddleOCR\venv\Scripts\python.exe -m pip install onnxruntime>=1.10.0
    [I] Folding Constants | Pass 1
    [I]     Total Nodes | Original:  1051, After Folding:   577 |   474 Nodes Folded
    [I] Folding Constants | Pass 2
    [I]     Total Nodes | Original:   577, After Folding:   577 |     0 Nodes Folded
    2026-05-21 21:27:45 [INFO]      ONNX model saved in .//ppocr-v5-rec.onnx.
    

Python ONNXRUNTIMEで推論!

主にGeminiに作ってもらいました。
辞書ファイルはgit cloneで落としてきた公式リポジトリの辞書を指定しています。

ppocr_rec_infer.py
import cv2
import numpy as np
import onnxruntime as ort
import os

# =====================================================================
# 1. 確定:GitHubリポジトリ内の公式英語辞書ファイルから文字マップを自動生成
# =====================================================================
# クローンした PaddleOCR のルートディレクトリにある公式辞書のパス
dict_path = "./ppocr/utils/dict/ppocrv5_en_dict.txt"

if not os.path.exists(dict_path):
    raise FileNotFoundError(
        f"公式辞書ファイルが見つかりません。現在の実行フォルダ(D:\\PaddleOCR)に "
        f"'{dict_path}' が存在するか確認してください。"
    )

# 0番目にCTCの「blank」を配置
character_list = ["blank"]

# 公式辞書ファイル(en_dict.txt)を読み込んでリストに追加
with open(dict_path, "r", encoding="utf-8") as f:
    for line in f:
        # 改行コードを取り除き、1文字ずつリストに格納
        char = line.strip("\r\n")
        character_list.append(char)

# 補足:PaddleOCR公式のPP-OCRv5/v3は、スペース文字( )をコード側で追加する仕様です
# リストにまだスペースが含まれていない場合は末尾に追加して全97クラスにします
if " " not in character_list:
    character_list.append(" ")

print(f"公式辞書の読み込みに成功しました。総文字数: {len(character_list)}")


# =====================================================================
# 2. 公式と100%一致させた前処理 (リサイズ + ゼロパディング)
# =====================================================================
def preprocess_image(img_path, target_shape=(3, 48, 320)):
    img = cv2.imread(img_path)
    if img is None:
        raise FileNotFoundError(f"画像が見つかりません: {img_path}")
    
    rec_c, rec_h, rec_w = target_shape
    img_h, img_w, _ = img.shape
    
    # アスペクト比を維持したリサイズ幅の計算
    ratio = img_w / float(img_h)
    resized_w = int(np.ceil(rec_h * ratio))
    resized_w = min(resized_w, rec_w) # 最大320
        
    resized_img = cv2.resize(img, (resized_w, rec_h), interpolation=cv2.INTER_LINEAR)
    
    # 浮動小数点変換と正規化 ((x / 255) - 0.5) / 0.5 -> x / 127.5 - 1.0
    norm_img = resized_img.astype(np.float32) / 127.5 - 1.0
    norm_img = norm_img.transpose((2, 0, 1)) # HWC から CHW
    
    # 【最重要】右側を確実に0で埋めるパディング処理
    padding_img = np.zeros((rec_c, rec_h, rec_w), dtype=np.float32)
    padding_img[:, :, :resized_w] = norm_img
    
    return np.expand_dims(padding_img, axis=0)

# =====================================================================
# 3. 修正:スペースを消さない CTC Greedy デコーダー
# =====================================================================
def ctc_decode(preds, character_list):
    if len(preds.shape) == 3:
        preds = preds[0] # バッチ次元を削除 -> (40, 97)
        
    preds_idx = np.argmax(preds, axis=-1)
    preds_prob = np.max(preds, axis=-1)
    
    char_res = []
    conf_res = []
    
    # 連続重複チェック用の初期値(存在しないインデックスにするため -1)
    last_idx = -1 
    
    for idx, prob in zip(preds_idx, preds_prob):
        # 1. 0番目(blankトークン)はCTCのルールに従ってスキップ
        if idx == 0:
            last_idx = idx
            continue
            
        # 2. 連続する同じインデックスはスキップ(CTCのルール)
        if idx == last_idx:
            continue
            
        # 3. 有効なインデックスであれば文字リストから復元
        if idx < len(character_list):
            char = character_list[idx]
            char_res.append(char)
            conf_res.append(prob)
            
        last_idx = idx
        
    text = "".join(char_res)
    score = np.mean(conf_res) if conf_res else 0.0
    return text, score

# =====================================================================
# 4. メイン処理 (CPU推論を強制)
# =====================================================================
if __name__ == "__main__":
    model_path = "./c_ppocr-v5-rec_sim.onnx"
    image_path = "./test_name_CalocenRieti.png"
    
    # 【重要】providersを「CPUExecutionProvider」のみに絞り、DirectMLの競合を回避します
    session = ort.InferenceSession(model_path, providers=['CPUExecutionProvider'])
    
    input_name = session.get_inputs()[0].name
    output_name = session.get_outputs()[0].name
    
    # 実行
    input_tensor = preprocess_image(image_path)
    outputs = session.run([output_name], {input_name: input_tensor})
    predictions = outputs[0]
    
    # 既存の推論とデコード
    recognized_text, confidence = ctc_decode(predictions, character_list)
    
    print("\n" + "="*40)
    print(f" 認識結果 : {recognized_text}")
    print(f" 信頼度スコア: {confidence:.4f}")
    print("="*40 + "\n")

test_name_CalocenRieti.png
test_name_CalocenRieti.png

(venv) PS D:\PaddleOCR> python ppocr_rec_infer.py
公式辞書の読み込みに成功しました。総文字数: 438

========================================
 認識結果 : Calocen Rieti
 信頼度スコア: 0.9947
========================================

参考にしたもの!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?