macOS 12 Monterey では,OSビルトインでのOCR機能が搭載されました。Preview.app で,画像やスキャンPDF(中身がスキャン画像のPDF)に対して,ただマウスでドラッグするだけで,中身の文字を認識して選択し,コピーできるようになっています。さらに,macOS 13 Ventura では,それが日本語にも対応しました。
たとえば,(今や入手困難となってしまった)The TeXbook のアスキーによる日本語版をスキャンしたものを Preview.app で開くと,何もしなくても,文字選択できます。
これをコピーして他のエディタにペーストすると,
TEXの名称で気をつけなければならないことがほかにもある。Eの文字が不揃いになっていることだ。Eの文字を少し下げてあるのは、TeXが組版のためのシステムであることを印象づけるためであり、またほかのシステムの名称と区別するためでもある。
と,驚くほど完璧に日本語の文字を認識できていることが分かります。
すると,このOCRエンジンを Preview.app の外側からも使えないか? という欲が湧いてきます。特に,コマンドラインから使えれば,リダイレクト・パイプなどを用いて他のUNIXコマンドと連携したテキスト処理も可能となります。
それに対する一つのアイデアは次のツイートにありました。
macOSのビルトインOCRは日本語だとAdobe Acrobatより精度が高い(白井の体感)のでコマンドから使えないかと探したら “Shortcuts\.app” から呼び出せることがわかった。起動して画像みたいなのを作って
— Nobu C. Shirai (@nobucshirai) 2023年6月28日
$ shortcuts run Extract_text_from_files
とすると呼び出せる。https://t.co/NHPOL4Bu2h pic.twitter.com/mODstZvrb4
ただし,この方法の場合,「事前にショートカットアプリでショートカットを用意しておかなければならない」という準備作業が必要となります。それだと,macOSのデフォルト状態(Macを購入した直後の状態)では動かないので,広く一般に配布するにはやや不向きです。
そこでここでは,次の条件を満たすシェルスクリプトを作ることを目指します。
要件
- macOS のデフォルト状態で動作する。ユーザに対して,事前に何らかのツールをインストールしておくなどの準備を要求しない。
- コマンドラインから使えるシェルスクリプトとする。
- 入力として,PDF および JPEG/PNG などの画像ファイルの両方を受け入れる。
- OCR認識結果は標準出力にプレーンテキストとして出力する。
- OCR認識時に,英語として認識するか,日本語を優先して認識するかを,オプションで切り替えられる。
実装のアイデア
macOS のOCRエンジンは,Vision フレームワークというAPI群で提供されています。これを自作プログラムから呼び出せばよいのですが,Swift や Objective-C のコードとして書くと,どうしても一度 clang でコンパイルする必要があり,シェルスクリプトとしては使えません*1。そこで,AppleScriptObjC のコードとして書き,それを /usr/bin/osascript
の引数として与えるのです。
いきなり完成形のシェルスクリプトを書くのは大変なので,まずは最も書きやすい Swift で Vision フレームワークを呼び出すコードを書いておいて,それを
と次々に書き換えてゆきます。
この手法は,以前PDFの結合やQRコードの生成にも用いました。
完成形のシェルスクリプト
使い方
$ ./ocr.sh input1.pdf input2.jpg input3.png
のように引数に1つまたは複数のPDFや画像ファイルを指定すると,それらに対してOCR処理を実施し,認識結果を標準出力に出力します。
オプション
--lang ja
/--lang en
:日本語優先認識モード,英語のみ認識モードを切り替えます。対象画像に含まれる文字の言語に応じて切り替えてください。(デフォルト:ja
)--dpi <VALUE>
:PDFに対してOCR処理を実施するときは,事前にビットマップ画像化処理をかけてからOCRエンジンに投げます。そのときのDPI値を指定します。数値が大きいほど画像が精細になり認識精度が上がりますが,その分速度が落ちます。(デフォルト:200
)
変換元コード
何かの参考になるかもしれないので,最終的にシェルスクリプトに変換するまでに書いた,
- Swift
- Objective-C
- AppleScriptObjC
のコードも公開しておきます。
追記
この記事を公開したところ反響があり,有り難いことに,拙作の ocr.sh
のフォークバージョンを作ってくださった方がいらっしゃいました。
Javascript (JXA) で書くと,AppleScriptObjC よりは Swift に近くて,読みやすいですね。
*1:shebang に #!/usr/bin/swift を付けて Swift コードをシェルスクリプト化する方法もありますが,実行環境に Xcode Command Line Tools がインストールされている必要があり,「デフォルト状態で動作する」という要件を満たしません。