textsnap: CPU上で画像、スクリーンショット、WebページからオフラインOCR
textsnapは、量子化されたONNXモデルを使用して画像、スクリーンショット、Webページからプレーンテキストを抽出する単一コマンドのPythonツールです。GPUやクラウドは不要です。
textsnapとは?
textsnapは、画像、スクリーンショット、Webページをプレーンテキストに変換する軽量なオフラインOCRツールです。すべてCPU上で動作し、クラウドに依存しません。量子化された0.9B PaddleOCR-VL-1.5視覚言語モデル(q4 ONNX)を使用して、標準的なラップトップでページ全体を解析します。CUDAもMシリーズ限定の機能も不要で、単純なコアだけで動作します。
なぜtextsnapなのか?
ほとんどのOCRツールはGPUが必要だったり、データをクラウドに送信したり、セットアップが面倒だったりします。textsnapはこれら3つの問題を解決します:
- CPUで動作 — モデルはq4 ONNXに量子化されており、最新のラップトップで効率的に動作します。
- 完全オフライン — 初回実行後(約890 MBのダウンロード)、すべてがローカルに保持されます。APIキー、クォータ、データの外部送信は不要です。
- 単一コマンド —
pip install textsnapで準備完了。ツールは単一のPythonモジュールです。 - 複数の入力タイプ — クリップボード、ローカル画像ファイル、直接画像URL、またはWebページURL。
- Markdownまたはプレーンテキスト出力 — デフォルトで表、見出し、構造を保持するか、
--plaintextでプレーンテキストにフラット化します。
クイックスタート
# インストール
pip install textsnap
# スナップ実行
textsnap screenshot.png
textsnap https://example.com/article --plaintext
textsnap photo.jpg -o ~/notes/receipt.txt
初回実行時にモデル(約890 MB)をダウンロードします。2回目以降はオフラインで動作します。
対応ソース
| ソース | 例 |
|---|---|
| クリップボード | textsnap(引数なし) |
| ローカル画像ファイル | textsnap path/to/img.png |
| 直接画像URL | textsnap https://example.com/x.png |
| WebページURL | textsnap https://example.com/article |
ローカルファイルはPillowがデコード可能な形式に対応:.png、.jpg、.jpeg、.webp、.bmp、.gif、.tiffなど。WebページURLの場合、textsnapはreadabilityを使用してメインコンテンツを抽出し、ページ内で最も目立つ画像を選択してOCRを実行します。
インストール
pip install textsnap
これにより、PATH上に2つの同等のコマンドがインストールされます:textsnap(正規名)とocr(エイリアス)。
ローカルソースからインストールする場合:
pip install .
依存関係のバージョンを固定して再現可能なインストールを行う場合:
pip install -r requirements-lock.txt
pip install .
クリップボードに関する注意: クリップボードからの画像読み取りはPillowのImageGrabに依存します。Linuxではxclipまたはwl-clipboardのインストールが必要な場合があります。macOSとWindowsはそのまま動作します。
使用方法
# クリップボード(引数なし)
textsnap
# ローカル画像ファイル
textsnap path/to/screenshot.png
# 直接画像URL
textsnap "https://example.com/diagram.png"
# Webページ — ページ内で最も目立つ画像をOCR
textsnap "https://example.com/article"
# モデルのMarkdownをプレーンテキストにフラット化
textsnap input.png --plaintext
# カスタム出力パス
textsnap input.png -o ./out/extracted.txt
# 非常に密度の高いページのトークン上限を引き上げ
textsnap dense-page.png --max-tokens 4096
# ダウンロードせずにローカルモデルディレクトリを使用
textsnap input.png --model-dir ~/models/paddleocr-vl
出力
出力はプレーンテキスト、UTF-8です。デフォルトの保存先はカレントワーキングディレクトリの./textsnaps/(存在しない場合は作成されます)。-oで上書き可能です。ファイル名は画像ファイル名の語幹(例:receipt_ocr.txt)から、またはURL入力の場合はWebページのスラッグから派生します。
textsnapはデフォルトで静かに動作し、Unixスタイルで標準出力に書き込んだファイルのパスのみを出力するため、クリーンに連携できます:
OUT=$(textsnap receipt.png) # パスをキャプチャ
textsnap receipt.png | xargs cat # 認識されたテキストを表示
-vを指定すると、進捗診断情報(入力タイプ、画像サイズ、デコード速度、トークン数)がstderrに送信されます。stdoutはどちらの場合もパスのみです。
デフォルトのファイル出力はモデルのネイティブMarkdownで、表、見出し、ドキュメント構造を保持します:
# 四半期レポート
| 地域 | 収益 |
| ------ | ------- |
| EMEA | $1.2M |
| APAC | $0.9M |
--plaintextを使用すると、Markdownがプレーンテキストにフラット化されます:
四半期レポート
地域 収益
EMEA $1.2M
APAC $0.9M
フラグ
| フラグ | 説明 |
|---|---|
-o, --output |
出力.txtパス。デフォルト:./textsnaps/<名前>_ocr.txt。 |
-v, --verbose |
進捗診断情報をstderrに出力。デフォルトはオフ。 |
--plaintext |
モデルのネイティブMarkdownをプレーンテキストにフラット化。 |
--model-dir |
ダウンロードせずにこのディレクトリのONNX/設定ファイルを使用。 |
--max-tokens |
生成トークンの上限。デフォルト2048。非常に密度の高いページでは引き上げてください。 |
--no-verify |
ダウンロードしたモデルファイルのSHA-256検証をスキップ(推奨しません)。 |
--generate-checksums |
固定されたモデルファイルをダウンロードし、新しいマニフェストを書き込んで終了。 |
セキュリティ
textsnapは初回実行時にHugging Face Hubから約890 MBのモデル重みを自動ダウンロードするため、これらのファイルを検証されるまで信頼しません:
- 固定モデルリビジョン。 ダウンロードは特定のリポジトリリビジョンに固定されているため、移動または再タグ付けされた
mainが静かに重みをすり替えることはできません。 - SHA-256検証。 ダウンロードされたすべてのファイルはハッシュ化され、既知の正しいダイジェストと照合されてから読み込まれます。不一致が発生した場合、検証されていない重みを実行する代わりに、明確なエラーで実行を中止します。ダイジェストは
model_checksums.sha256に保存され、スクリプト内にもフォールバックとして埋め込まれているため、ソースからインストールする場合もwheelからインストールする場合も検証が機能します。 - 固定依存関係。
requirements-lock.txtは再現可能なインストールのために依存関係のバージョンを固定します。このファイルには、完全なサプライチェーン固定のためにpip-compile --generate-hashesでホイールごとに--hashエントリを追加する方法が記載されています。
意図的にモデルリビジョンを変更した後、チェックサムマニフェストを再生成します:
textsnap --generate-checksums
検証をバイパスするには(変更したモデルでのローカル実験用)、--no-verifyを指定します。
動作の仕組み
- 読み込み。 クリップボード、ローカルファイル、直接画像URL、またはWebページURLの場合はページのメインコンテンツ内で最も目立つ画像(readability + 顕著性ヒューリスティック)から読み込みます。
- 前処理。 画像の最長辺を640pxに制限し、PaddleOCR-VLのQwen2-VLスタイルのスマートリサイズとパッチ化を実行し、視覚エンコーダが期待するピクセル値テンソルとグリッドを生成します。
- 認識。 3つのONNXコンポーネントがCPU上で実行:視覚エンコーダ(q4)、トークン埋め込みモデル(fp32)、KVキャッシュを備えた自己回帰デコーダ(q4)。貪欲デコードで、繰り返しを早期に停止する重複防止機能付き。
- フォーマット。 デフォルトでネイティブMarkdown。
--plaintextでプレーンテキストに削減。
画像はどこにも送信されません。実行間でキャッシュされたモデル以外の状態は保持されません。
モデルとキャッシュ
PaddleOCR-VL-1.5 ONNXコンポーネントは初回実行時に~/.cache/textsnap/にダウンロードされます:
onnx/vision_encoder_q4.onnx— 視覚エンコーダ + 空間マージプロジェクタonnx/decoder_q4.onnx— 自己回帰デコーダonnx/embedding.onnx— トークン埋め込み(fp32、q4バリアントは存在しません)tokenizer.json、config.json
合計約890 MB。独自のコピーを使用するには、--model-dirで同じonnx/ファイルとtokenizer.json、config.jsonを含むディレクトリを指定します。
注意点と制限
- 初回実行が最も遅い — 約890 MBをダウンロードします。その後はtextsnapは完全にオフラインです。
- CPUデコードは逐次的。 密度の高いページ全体のドキュメントは、短いスクリーンショットよりも時間がかかります。textsnapはスレッド数を物理コア数に固定し、ライブのトークン/秒の読み取り値を表示するため、遅い実行でもハングしているのではなく動作していることがわかります。
--max-tokensで出力を制限。 非常に密度の高いページはデフォルトの2048トークン上限に達して切り捨てられる可能性があります。ページの末尾が欠けている場合は引き上げてください。- Webページ入力は1つの画像をOCR — レンダリングされたページ全体ではなく、メインコンテンツ内で最も目立つ画像です。
- 貪欲デコードは繰り返しレイアウトでループすることがあります。組み込みのガードがこれを検出してトリミングします。
ライセンス
このプロジェクトはMITライセンスです。モデルはPaddleOCR-VL-1.5で、PaddlePaddleによりApache-2.0で配布されています。textsnapはonnx-community/PaddleOCR-VL-1.5-ONNXからONNXエクスポートを取得します。onnxruntimeとhuggingface_hubを利用しています。
ソース
kouhxp/textsnap: 画像、スクリーンショット、Webページをプレーンテキストに変換。GPU不要。クラウド不要。単一コマンド。