はじめに
仕事でGUI操作系のタスクを触る機会がありました。画面のスクリーンショットからボタンやアイコン、テキストを検出する必要があり、その流れで PaddleOCR や ローカルVLM(Ollama経由) を使うことになりました。
どちらも問題なく動きましたし、精度も悪くない。
GUI操作系のいくつか最新論文の手法はすでにライブラリとして提供されている、もしくは検出系のモデルとOCR系のライブラリを組み合わせればそれほど労力もかからず再現できます。
OCRの仕組みをよく理解していなくても問題ありませんでした。
paddleocr.PaddleOCR() を呼んで ocr() するだけ。Ollamaで ollama run するだけ。それで「画像から文字が出てくる箱」として使っていました。動いてしまうので深追いせずに済んでいたんですね。
とは言ってもやっぱりOCRがどの様な技術、プロセスもって動いているのか気になるので、簡単に調べてまとめることにしました。
OCRと言ってもいろいろありますが、この記事では私がよく使っているPaddleOCRとVLM系モデルの基本的な解説になります。
まぁ、VLMに関しては「OCRに関する記事」で解説するのはおかしい気もしますが(プロンプトでOCRタスクを指示するだけですし)。
あと、ざっくりとした説明なので、詳しいモデルの技術解説記事とかではないです。
OCRの基礎用語
まず、用語を整理します。
「OCR」と一口に言っても、内部では複数のタスクが組み合わさっています。
| 用語 | やること |
|---|---|
| テキスト検出 (Text Detection) | 画像のどこに文字があるかを矩形(バウンディングボックス)で囲う |
| テキスト認識 (Text Recognition) | 切り出された矩形領域の中の文字列を読み取る |
| レイアウト解析 (Layout Analysis) | 段落・表・見出しなど、文書構造を把握する |
| ドキュメント理解 (Document Understanding) | 「請求書の合計金額はどこか」のように意味を理解する |
従来型OCRは 検出 → 認識 までを担当することが多く、レイアウト解析やドキュメント理解は別モジュールや別ツールで対応していました。
VLMベースOCRは、これらをひとつのモデルで一気にやろうとします。
PaddleOCRの中身
PaddleOCRは中国のBaidu発のオープンソースOCRツールキットです。pip install paddleocr で導入できます。
中で何をやっているのか、分解してみます。
パイプライン構造
PaddleOCR(特にPP-OCRシリーズ)は、典型的な 3段階パイプライン で動いています。
入力画像
↓
[① テキスト検出] ← どこに文字があるか
↓
[② 方向分類] ← 文字が縦か横か、上下どっち向きか
↓
[③ テキスト認識] ← 文字を読み取る
↓
出力(文字列 + 座標 + 信頼度)
各ステップで 別々のモデル が動いています。
テキスト検出:DBNet
検出には DBNet (Differentiable Binarization Network) が使われています。
ざっくり言うと、画像を「文字がある領域」と「ない領域」にセグメンテーションするモデルです。各ピクセルについて「これは文字の一部か?」を予測し、得られた確率マップから矩形を生成します。
通常のセグメンテーションだとしきい値処理が微分不可能で学習しづらいのですが、DBNetは 二値化処理を微分可能な形で組み込むことでEnd-to-End学習を可能にしたのが特徴です。
バックボーンには軽量CNN(MobileNetV3など)が使われていて、これがPaddleOCRの「軽さ」「速さ」に貢献しています。
方向分類
検出された矩形領域を、文字の向きが正しくなるように回転させるステップです。地味ですが、これがあることで「逆さまの文字」「縦書きの文字」も正しく認識できます。
方向分類では軽量なCNNが用いられています。
検出されたテキスト行(画像)
↓
小型CNN(分類器)
↓
0° / 180°(場合によっては 90°, 270°)
↓
回転補正
↓
認識モデルへ
認識モデル(CRNNやSVTR)は「左→右に読む前提」で学習されています。
なので、テキストの方向によって精度が低下するのを防ぐためにこのプロセスがあります。
テキスト認識:CRNN → SVTR
認識モデルは時代とともに変化しています。
- PP-OCR / PP-OCRv2:CRNN(CNN + Bi-LSTM + CTC)
- PP-OCRv3 以降:SVTR(Scene Text Recognition with a Single Visual Model)
CRNNは「画像を縦長の特徴列に変換 → LSTMで時系列処理 → CTCで文字列に変換」という古典的な構成です。
SVTRはRNNを捨ててTransformerベースに置き換えたもので、長距離の文脈をより効率的に捉えられます。PP-OCRv3では完全な置き換えではなく、SVTR_LCNet (SVTRの前半を軽量CNNのPP-LCNetに置き換えたハイブリッド版)が採用されました。
なぜ軽くて速いのか
PaddleOCRの強みは「軽い・速い」です。これは以下の設計から来ています。
- バックボーンに軽量CNN(MobileNet, PP-LCNet)を採用
- 各モジュールが小さく、CPU推論でも実用速度
- 検出と認識が独立しているので、それぞれを最適化しやすい
強みと弱み
実際に使ってみての所感も含めて整理します。
強み
- 軽い・速い・CPUでも動く
- 文字単位の精度が高い
- 多言語対応(80+ 言語)
- ライセンスが緩く、商用利用しやすい
弱み
- レイアウトが複雑な文書(表、入り組んだUI)は苦手
- 「読んだ文字列の意味」は理解していない
- 画像が荒い・回転がきつい・装飾的フォントなどに弱い場合がある
GUI操作の文脈で言うと、ボタンに書かれた文字を拾うという用途には十分でしたが、「このボタンは何のボタンか」までは分からないわけです。意味づけはVLMが適しています。
VLMベースOCR:ローカルVLMの中身
次に、基本的なVLMの構造を見ていきます。
VLMの基本構造
VLM(Vision Language Model)は、ざっくり3つのパーツでできています。
画像 → [Vision Encoder] → [Projector] → [LLM] → テキスト
↑
テキストプロンプトもここに入る
| パーツ | 役割 |
|---|---|
| Vision Encoder | 画像を「特徴ベクトル」に変換する。CLIP系のViTがよく使われる |
| Projector | Vision Encoderの出力をLLMが理解できる形(≒トークン)に変換する |
| LLM | 受け取った画像トークンとテキストプロンプトをもとに、テキストを生成する |
Projectorは単純なMLP(2層のLinear+活性化)であることが多く、これが画像とテキストの「翻訳機」の役割を担っています。
「画像から文字を出す」が成立する理由
VLMでOCRが成立するのは、ざっくりこういう仕組みです。
- Vision Encoderが画像をパッチに分割し、各パッチを特徴ベクトルに変換
- Projectorがそれをトークン列に変換し、LLMに「画像トークン」として渡す
- LLMはプロンプト(例:「画像の文字を全て出力して」)と画像トークンを受け取り、通常のテキスト生成タスクとして 文字列を出力する
つまり OCRも「画像という入力に対して文字列を生成する」というLLMタスクとして解いているイメージです。
OCR特化VLM vs 汎用VLM
VLMベースOCRには大きく2系統あります。
汎用VLM(GPT-4o、Gemini、Claude、Qwen2.5-VL、MiniCPM-Vなど)
- VQA、画像説明、推論など多目的に学習されている
- OCR能力は副産物的に高い
- レイアウトや意味理解にも強い
OCR特化VLM(GLM-OCR、olmOCR、DeepSeek-OCRなど)
- OCRタスクに特化した学習データで訓練されている
- 文字認識精度が高い、構造化出力(Markdown等)が得意
- 汎用タスクでは基本的に劣る
Ollamaで個人開発レベルで触る場合、Qwen2.5-VLやMiniCPM-Vのような汎用VLMが現実的な選択肢になりやすいです(私はQwenを使用していました)。
強みと弱み
強み
- レイアウトを理解した出力ができる(Markdownで表や構造を保存)
- 「画像中の数字を合計して」みたいな 読解+推論 が同時にできる
- 装飾的なフォント、回転、ノイズに比較的強い
- プロンプトで挙動を変えられる
弱み
- ハルシネーション(幻覚)が起きる。画像にない文字を「読んで」しまう
- 重い。GPUがほぼ必須で、推論も遅い
- 出力が安定しない。同じ画像でも結果が微妙に違うことがある
- 文字単位の正確性は、ピーキーな従来型に劣ることもある
特に ハルシネーション は厄介で、「ぱっと見もっともらしいけど元画像にない文字列」を返してくることがあります。これは「OCRをLLMタスクとして解いている」副作用と言えます。
2つを並べて整理
ここまでを表にまとめます。
| 観点 | PaddleOCR(従来型) | ローカルVLM |
|---|---|---|
| アーキテクチャ | パイプライン分業 | End-to-End |
| 解き方 | 検出 → 認識の独立タスク | テキスト生成タスクとして一括処理 |
| 速度 | 速い(CPU可) | 遅い(GPU推奨) |
| モデルサイズ | 数MB〜数十MB | 数GB〜数十GB |
| 文字単位の精度 | 高い | モデル次第 |
| レイアウト・構造 | 別モジュールが必要 | 標準で出力可能 |
| 意味理解 | なし | あり |
| ハルシネーション | しない(読めない=空) | する(ありそうな文字を作る) |
| 出力形式 | 座標+文字列 | 自由(Markdown、JSONなども可) |
| プロンプトで制御 | 不可 | 可 |
当たり前ですが解き方の思想が根本的に違うのが分かります。
- 従来型は「画像から文字を抜き出す」ことに特化し
- VLMは「画像を見て答える」ことができる、汎用的な道具
使い分けの指針
PaddleOCRが向いている場面
- 大量の画像を処理したい(速度・コスト重視)
- 文字単位の精度が重要(請求書の金額、IDなど)
- ハルシネーションが許されない(金融、医療系)
- CPUしか使えない環境
- レイアウトが定型的
ローカルVLMが向いている場面
- レイアウトが複雑(表、入り組んだUI、手書きメモ)
- 「読んだ後に何かを判断したい」(意味理解が必要)
- 構造化された出力が欲しい(Markdown、JSON)
- 多少のハルシネーションが許容できる
- GPUが使える
両方使うという選択肢もあります。実際、Microsoft OmniParser のようなGUI解析ツールは PaddleOCRでテキストを拾い、VLMで意味づけ という構成を取っています。それぞれの得意分野を組み合わせるアプローチですね。
私自身も検出モデルでコンポーネントやテキストを検出して、個別にVLMで判定するという方法をとることもあったので、単体で使うというよりかはどう組み合わせるかを考えるべきかもしれません。この場合であればVLMも軽量のものですみますし。
まとめ
もともと、複数のモデルを組み合わせなくてはいけなかったのが、今ではライブラリ任せで簡単に動いてしまうのがOCRのすごいところです。
でも、中身を知ると以下のような 判断の解像度が上がる気がします。
- モデル選定:用途に応じてどっちを選ぶべきか分かる
- デバッグ:精度が出ないとき、どこの問題か当たりがつく
- アーキテクチャ設計:両方を組み合わせる発想ができる
- 限界の把握:何ができて何ができないかが分かる
Nougat、Donut系統や、より最近のGUI特化VLMなども出てきているみたいなので、触ってみたいと思っています。
地味に今までOCRで検出に使われるのってBbox検出系のモデルだと思い込んでいました。
そりゃセマセグ系も強いよなぁ...