はじめに
おはようございます。当ブログにアクセス頂き、ありがとうございます。スクワットのフォームが悪くて、膝を少し痛めてしまった、たなけんです。
本エントリではClojureを用いてシェルを操作し、画像データを加工し、必要となる文字列を抽出する方法を記載します。
事の発端
大学院の授業でシュミレーションゲームを行っており、そのデータをClojureを用いて分析していることを、以前の記事で紹介しました。その後、あるチームメイトから「このpdfファイルのデータも使えない?」と聞かれ、『pdfなら文字情報を抽出するのも簡単だし、問題ない』と考え、二つ返事でデータの抽出を引き受けたのが、今回の事の発端です。
以前、利用したデータはcvs形式だったので、データ読み取りに何の工夫も必要ありませんでした。今回も、xpdfなど既存のソフトウェアを使えば、文字情報が簡単に取り出せると考えていたのですが、意外な落とし穴がありました。実は
ファイル拡張子はpdfであるものの、実際のデータは画像
だったのです。(xpdfでテキスト抽出処理をした結果、一文字も抽出されませんでした。不審に思い、その後手動で文字をコピーしようと文字部分をマウスでドラッグしてみて、データが画像であることに気が付きました。。。)
以前より、画像から文字を抽出(OCR)することには関心があったので(※1)、ImageMagickとTesseractを利用して、画像データを抽出することにしました。
実装方針
翌日には結果を渡さないといけないという、時間的制約から、手作業で作業内容を確立し、プログラムでそれを自動化するという方針としました。- それぞれの処理(画像加工、OCR)はシェルからコマンドを実行する(作業内容)
- シェルの起動や、コマンド発行をClojureから行う(自動化)
ImageMagickとTesseractのインストール
どちらもMacPortsからインストールしました。Tesseractは本体だけではなく、英語バージョンの学習ファイル(辞書)もインストールしました。
特に問題なく、スムーズにインストールすることができました。
(パラメータなどは全てデフォルト値)
作業内容
作業内容および手順は、以下の通り。- 複数ページを含むpdfファイルを、個別の画像ファイルに分割する(同時に解像度も上げる)
- 各画像ファイルから抽出対象の文字が含まれる部分を切り取る
- 切り取ったファイルの文字情報を読み取る
- 読み取った文字情報を整形し、レポートを出力する(本エントリでは記載を省略)
ソースコード
形式変換&ページ分割
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn make-pdf2png-args | |
"" | |
[file-name] | |
(list | |
"-density" "300x300" | |
"-units" "PixelsPerInch" | |
(str org-dir "cor/" file-name ".pdf") | |
(str prcs-dir "cor/" file-name ".png"))) | |
(defn pdf2png | |
"" | |
[y k-ap] | |
(apply shell/sh "convert" (make-pdf2png-args (make-cor-file-name-str y k-ap)))) |
ImageMagickのconvertコマンドで複数ページを含むpdfファイルを個別のpngファイルに変換します。(-formatオプションを付けなくてもフォーマット変換出来ました。。。)
解像度指定しなかった場合、画像が荒すぎてOCR精度が落ちるため、-densityオプションで解像度を設定、-unitsオプションで解像度の単位を指定しています。
Clojureで実装した際の注意点としては、clojure.java.shell/sh関数を利用する際、通常(clojure.java.shell/sh コマンド オプション1 オプション2 ...)のように、コマンドのオプションを(リストではなく)第2引数、第3引数...と指定する点があげられます。
今回の実装では、オプションを別関数で生成しているため、apply関数を用い、(apply clojure.java.shell/sh コマンド オプションのリスト)のように実行しました。
またオプションは一続きの文字列ではなく、それぞれのオプション(およびその値)が分割されている必要がある点も注意が必要でした。例えば、"-density 300x300 -units PixelsPerInch"を第2引数に渡すとエラーとなり、正しくは"-density" "300x300" "-units" "PixelsPerInch"をそれぞれ、第2引数、第3引数、第4引数、第5引数として渡さなくてはいけません。
上記の点さえ気をつければ、特に問題なくclojure.java.shell/sh関数を使うことが出来ます。(シェルコマンドをClojureで自動実行できれば、いろいろと便利ですね)
画像切り取り
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(def geo-prd-cost-el "1540x60+780+770") | |
(def geo-prd-cost-mf "1540x60+780+2015") | |
(def geo-lbr-cost "1540x60+780+2760") | |
(def geo-lbr-prod "1540x60+780+2810") | |
(def geo-inv-el "1540x135+780+960") | |
(def geo-inv-mf "1540x135+780+2310") | |
(defn make-crop-args | |
"" | |
[file-name geo in-ext out-ext] | |
(list | |
"-crop" | |
geo | |
(str prcs-dir "cor/" file-name in-ext) | |
(str prcs-dir "cor/crop/" file-name out-ext))) | |
(defn crop | |
[args] | |
(apply shell/sh "convert" args)) | |
(defn crop-prd | |
"" | |
[y k-ap] | |
(do | |
(crop (make-crop-args (make-cor-file-name-str y k-ap) geo-prd-cost-el "-0.png" "_prd_el.png")) | |
(crop (make-crop-args (make-cor-file-name-str y k-ap) geo-prd-cost-mf "-0.png" "_prd_mf.png")))) | |
(defn crop-lbr | |
"" | |
[y k-ap] | |
(do | |
(crop (make-crop-args (make-cor-file-name-str y k-ap) geo-lbr-cost "-0.png" "_lbr_cost.png")) | |
(crop (make-crop-args (make-cor-file-name-str y k-ap) geo-lbr-prod "-0.png" "_lbr_prod.png")))) | |
(defn crop-inv | |
"" | |
[y k-ap] | |
(doseq [[pg rg] pg-rg] | |
(crop (make-crop-args (make-cor-file-name-str y k-ap) geo-inv-el (str "-" pg ".png") (str "_" rg "_el.png"))) | |
(crop (make-crop-args (make-cor-file-name-str y k-ap) geo-inv-mf (str "-" pg ".png") (str "_" rg "_mf.png"))))) |
形式変換&ページ分割と同様、切り取るサイズと位置を指定してconvertコマンドを実行します。ページ毎に切り取り部分が異なるため、ファイル名に応じた切り取りサイズ&位置を渡しています。
文字情報の読み取り
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn extract-text | |
"" | |
[file-name] | |
(shell/sh | |
"tesseract" | |
(str prcs-dir "cor/crop/" file-name) | |
(str prcs-dir "cor/extract/" file-name) | |
"-psm" | |
"6")) |
切り取り済みの画像ファイルを順に読み込み、テキストファイルを生成します。
オプションの-psmは画像の中でさらに読み取る部分を指定するオプションです。
画像全体を読み込み対象とするため、-psmに6を指定しています。
捕捉: psm (pagesegmode)一覧
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pagesegmode values are: | |
0 = Orientation and script detection (OSD) only. | |
1 = Automatic page segmentation with OSD. | |
2 = Automatic page segmentation, but no OSD, or OCR | |
3 = Fully automatic page segmentation, but no OSD. (Default) | |
4 = Assume a single column of text of variable sizes. | |
5 = Assume a single uniform block of vertically aligned text. | |
6 = Assume a single uniform block of text. | |
7 = Treat the image as a single text line. | |
8 = Treat the image as a single word. | |
9 = Treat the image as a single word in a circle. | |
10 = Treat the image as a single character. |
上記のように、一行とみなしたり、円で囲った部分だけ読むなどのオプションがあります。
調査中に見つけたライブラリ
今回は、時間的制約から、プログラムの再利用性(汎用性)よりも、実装の手早さを重視しました。そのため、あまり調査に時間を掛けることはできませんでした。しかし、少しだけ行った調査の過程で、面白そうなライブラリを発見したので、この場を借りて紹介したいと思います。im4java
ImageMagickのコマンドを網羅したJavaライブラリ。<im4javaのwebサイト>複雑な処理を自動化するのであれば、使ってみたいといった印象。
学習コスト削減のため、今回は利用しませんでした。
Tess4J
TesseractのJNIラッパー。<Tess4Jのwebサイト>まだMacでの動作に問題があるようだったので利用を回避。
2012年8月8日にversion 1.0 beta 5がリリースされたばかりの新しいライブラリの様なので、今後の成熟に期待。
今回の作業は以上。最後までお読み頂き、ありがとうございました。
たなけん(作業時間2時間(テキスト抽出後の処理は除く))
※1: OCRを組み込んだ、アプリケーション、サービスについての構想は別エントリにて紹介したいと思います。
0 件のコメント:
コメントを投稿