今日も微速転進

ここではないどこかへ

陸自イラク日報のPDFにテキストを付加して検索できるPDFを作る(試行錯誤 その1)


スポンサーリンク

意外なことに簡単なようですんなりとはいかない。とりあえずやってみた的な記事。

過去記事の続き。

JSONデータはDBにぶち込むほうが使い勝手がいいだろうとは思いますが、どっちにしろ出遅れているので違う方向で。

前提条件など

OCR処理自体は過去の記事で実施済みのJSONデータを使用。

a244.hateblo.jp

現状のGoogle Cloud Vision APIOCR機能は認識結果データとして2種類のデータを返してくる。

文書構造の情報なし(textAnnotations)とあり(fullTextAnnotation)の2パターン。

文字の認識位置さえ検出できればいいので文書構造の情報なしの方を使ってみる。

PDFの背景に元の画像をセットして、その上に透明なテキストを配置する。

問題はOCR結果に含まれる位置情報の座標系は左上原点系だけど、PDF内部では左下原点系であること。

f:id:atuyosi:20180427220805p:plain:w480

もう一点は単位の違い。画像はピクセル単位だけどPDF内部ではポイント(pt、72dpi相当なので1ポイント=1/72inch)またはcmなどの用紙サイズ由来の単位になる。

文字サイズについても考慮してやらないと位置がずれてしまう。PDF側は72dpi、OCR結果は画像に変換するためのpdf2imageというライブラリのデフォルトが200dpiなのでそのまま200dpi。

文字サイズの換算は 、OCR結果のJSONデータから確認式領域の左上と左下の頂点座標の差を取って、200dpi / 72dpiの比率を掛ければそこそこ良い値になっている。

イラク日報のPDFについて

ページサイズとしてはほぼA4。どうやらA4画像を200dpiでスキャンした画像(1640x2339 pixel)を含んでいる。

適当なファイルを選んでmutoolで情報を取り出してみると、以下のようになる。

$ mutool info pdf/イラク後送業務隊/060731.pdf
pdf/イラク後送業務隊/060731.pdf:

PDF-1.4
Info object (1 0 R):
<<>>
Pages: 6

Retrieving info from pages 1-6...
Mediaboxes (2):
    1   (4 0 R):    [ 0 0 590.39999 842.04 ]
    5   (8 0 R):    [ 0 0 593.27999 842.04 ]

Images (6):
    1   (4 0 R):    [ CCITTFax ] 1640x2339 1bpc DevGray (10 0 R)
    2   (5 0 R):    [ CCITTFax ] 1640x2339 1bpc DevGray (14 0 R)
    3   (6 0 R):    [ CCITTFax ] 1640x2339 1bpc DevGray (18 0 R)
    4   (7 0 R):    [ CCITTFax ] 1640x2339 1bpc DevGray (22 0 R)
    5   (8 0 R):    [ CCITTFax ] 1648x2339 1bpc DevGray (26 0 R)
    6   (9 0 R):    [ CCITTFax ] 1640x2339 1bpc DevGray (30 0 R)

PDFのサイズ情報としては593.3x842.04という中途半端な値(単位はポイント)。標準的なPDFは595x842というケースが多いんだけど……。

スキャンする時に微妙に用紙がはみ出したのか。MediaBoxの値が複数あるのも気持ち悪いけど。

多少の誤差は許容範囲は気にせずA4の用紙サイズとみなすことにする。

問題は特定のページだけ横向きだったりする点。地味にめんどい。

PDFを画像化したもの(200dpi, 1640x2339)をGoogle Cloud Vision APIOCR処理している。OCR結果のJSONデータは文字の認識位置を1640x2339の画像上で左上が原点の座標データとなっている(過去記事参照)。

座標系の変換処理としてy座標の原点が上下逆なので変換した上で比率をあわせる必要がある。

ライブラリの選定

既存のPDFの分割やつなぎ合わせであればPyPDF2でもできるみたいだけど、文字を追加したり画像を追加するにはReportLabが一番確実。

www.reportlab.com

PDF形式のユーザーガイドとあとはWeb上の記事を参考にする。

Python 2.xのページが結構引っかかるが、概ねメソッドの名称などは同じなのであまり気にしなくてもなんとかなる。

ReportLabの画像埋め込みメソッドが画像データではなく画像ファイルのパスを要求する点がネック。

初めからPDFを画像化してしまえばよかったと後悔している。まあ仕方ない。

プログラム

環境など

ソース

文字の位置がずれる、表の一部で文字サイズが狂う、などまだまだ問題あり。

デバッグ用に文字色を赤色にしているので実際に使うなら透明度の値(alpha=)を0に。

newPdfPage.setFillColor(red, alpha=0.0)

ファイル名決め打ちなのでそこも修正。

暫定版陸自イラク日報テキスト付PDF生成スクリプト

プログラムの解説は流石に力尽きましたってことで。元データのPDFをpdfディレクトリに、JSOSNデータをjsonディレクトリにそれぞれ対応するサブディレクトリ、ファイル名で格納しておく必要があります。

残念ながら汎用性は低いです。

生成物のサンプル

暫定版なので画像で。透明にはせずに文字色は強調用に赤。

元データは、イラク後送業務隊の060720.pdf。

そこそこ上手くいったページをピックアップ。

f:id:atuyosi:20180427215158j:plain:w480]

f:id:atuyosi:20180427214715j:plain:w480

生成したPDF: Dropbox

感想など

簡単に見えて地味にめんどい。文字認識の取りこぼしのチェック手段としてはやはり画像に文字を重ねてやるのが一番。

元の画質が良くない点を差し引いても取りこぼし箇所、認識ミスがそこそこある。

やっかいなのは文字と文字の間の空白が除去されている箇所と、逆に文字列としては認識されずに分離している箇所。分離されて認識しているケースの場合、そういう箇所は検索してもヒットしない。また、まとめてテキストを選択できるんだけど間にスペースが入ってしまう。

某社のOCR製品のAmazonのレビュー欄にもテキスト間のスペースの問題はしてきされていたのでGoogleAPIだけの問題ではないんだけど。
文字を検出した領域同士の距離によってはグループ化する処理をいれてもいいかも。その場合、用紙の傾きの補正とかちゃんとやる必要がある。

文字と文字の間の空白が除去されるパターンは、N-gramデータの辞書か何かにある単語の場合だけスペースを除去するロジックが入ってるんだろうと思う。


OCRのミスのある状態でPDFにテキストを埋め込んでもしょうがないのでひとまず休止予定。

ReportLabに関しては他にもいろいろと活用できそうです。

参考にしたURL

PDF構造解説

PDF構造解説

広告