LoginSignup
6
4

More than 3 years have passed since last update.

全文検索サーバを構築した話 その3(OCR環境構築編)

Last updated at Posted at 2019-06-10

なぜOCRが必要になったのか?

要求があり構築を進めた全文検索システムですが、環境を作る前にそもそも問題がありまして…
PDFは基本押印されたものをスキャンして保存しているという点。
つまり、PDFファイル内にテキストデータは保存されていない状態ということです。
画像ファイルと同じってことですね。

まぁ、全文検索できないですよね。
検索キーワードを紐づけるデータがそもそもファイルに書いてないわけですから。
しかもPDFにはパスワードも設定しているらしく、色々と難航しそうなので
半分諦めモードでしたが…

調べてみるとそれなりに解決方法があるようなので、機械的な文書起こしでも
ないよりはマシかと思い、やってみることにしました。

OCRするために使うツール

tesseract-ocr
画像データをOCRして抽出した文字情報を埋め込んだPDFファイルを生成するために使用します。
この作業で一番処理に時間のかかる工程になります。
インストールするパッケージは以下の2つ。
tesseract tesseract-langpack-jpn

poppler-utils
poppler-utilsはPDFのコマンドラインツールらしいです。今回は元のPDFをpngに変換する、
元のPDFから情報(パスワード設定されているか、印刷可能か)を参照するために使用します。
以下の2つのコマンドをこのパッケージから使用しています。
pdfinfo pdftoppm

pdftk
こちらもPDFのコマンドラインツールなんですが、メインはPDFファイルの結合に使用します。
PDFの結合自体はpoppler-utilsに含まれているpdfuniteコマンドでも実装できるのですが、
PDFファイルにパスワードを設定すること、印刷可否をコントロールすること
これができません。
一見pdftkのほうが高機能な感じがするのですが、PDFをpngファイルに変換する機能はありません。
なので、今回の作業では両方のパッケージが必要となりました。

必要なパッケージのインストール

tesseract-ocr

tesseractはCentOS標準リポジトリでは提供されていません。
なので、epelのリポジトリを追加してインストールします。

# epelをインストール
$ sudo yum install epel-release

インストール:
  epel-release.noarch 0:7-11

完了しました!

# tesseractをインストール
$ sudo yum install tesseract tesseract-langpack-jpn

インストール:
  tesseract.x86_64 0:3.04.00-3.el7                     tesseract-langpack-jpn.noarch 0:3.04.00-3.el7

依存性関連をインストールしました:
  cairo.x86_64 0:1.15.12-3.el7                               dejavu-fonts-common.noarch 0:2.33-6.el7
  dejavu-sans-fonts.noarch 0:2.33-6.el7                      fontconfig.x86_64 0:2.13.0-4.3.el7
  fontpackages-filesystem.noarch 0:1.44-8.el7                fribidi.x86_64 0:1.0.2-1.el7
  giflib.x86_64 0:4.1.6-9.el7                                graphite2.x86_64 0:1.3.10-1.el7_3
  harfbuzz.x86_64 0:1.7.5-2.el7                              jbigkit-libs.x86_64 0:2.0-11.el7
  leptonica.x86_64 0:1.72-2.el7                              libICE.x86_64 0:1.0.9-9.el7
  libSM.x86_64 0:1.2.2-2.el7                                 libX11.x86_64 0:1.6.5-2.el7
  libX11-common.noarch 0:1.6.5-2.el7                         libXau.x86_64 0:1.0.8-2.1.el7
  libXdamage.x86_64 0:1.1.4-4.1.el7                          libXext.x86_64 0:1.3.3-3.el7
  libXfixes.x86_64 0:5.0.3-1.el7                             libXft.x86_64 0:2.3.2-2.el7
  libXrender.x86_64 0:0.9.10-1.el7                           libXxf86vm.x86_64 0:1.1.4-1.el7
  libglvnd.x86_64 1:1.0.1-0.8.git5baa1e5.el7                 libglvnd-egl.x86_64 1:1.0.1-0.8.git5baa1e5.el7
  libglvnd-glx.x86_64 1:1.0.1-0.8.git5baa1e5.el7             libicu.x86_64 0:50.1.2-17.el7
  libjpeg-turbo.x86_64 0:1.2.90-6.el7                        libthai.x86_64 0:0.1.14-9.el7
  libtiff.x86_64 0:4.0.3-27.el7_3                            libwayland-client.x86_64 0:1.15.0-1.el7
  libwayland-server.x86_64 0:1.15.0-1.el7                    libwebp.x86_64 0:0.3.0-7.el7
  libxcb.x86_64 0:1.13-1.el7                                 libxshmfence.x86_64 0:1.2-1.el7
  mesa-libEGL.x86_64 0:18.0.5-4.el7_6                        mesa-libGL.x86_64 0:18.0.5-4.el7_6
  mesa-libgbm.x86_64 0:18.0.5-4.el7_6                        mesa-libglapi.x86_64 0:18.0.5-4.el7_6
  pango.x86_64 0:1.42.4-2.el7_6                              pixman.x86_64 0:0.34.0-1.el7

完了しました!

tesseractは依存関係が多いので結構追加パッケージが入りますね。
変換作業もCPUリソースを食うので、PDFコンバート用サーバが用意できるなら
別に用意するほうが良いかもしれません。

追加手順

yumでtesseractをインストールしてtesseractを実行すると、以下のようなエラーが出ます。

Tesseract Open Source OCR Engine v3.04.00 with Leptonica
Error opening data file /usr/share/tesseract/tessdata/osd.traineddata
Please make sure the TESSDATA_PREFIX environment variable is set to the parent directory of your "tessdata" directory.
Failed loading language 'osd'
Tesseract couldn't load any languages!
Warning: Auto orientation and script detection requested, but osd language failed to load

理由はよくわかりませんが、言語ファイルを読み込むための大元のファイル、"osd.traineddata"というファイルが
初期状態で欠如してしましっているようです。
なので、バージョンに合わせた"osd.traineddata"ファイルを取得して、/usr/share/tesseract/tessdata/に
保存します。

$ cd /usr/share/tesseract/tessdata/
$ sudo curl -OL https://github.com/tesseract-ocr/tessdata/raw/3.04.00/osd.traineddata

これで、tesseractが正常に動作して日本語を読み込める状態になります。

poppler-utils

こちらは標準レポジトリでインストールできます。

$ yum install poppler-utils

インストール:
  poppler-utils.x86_64 0:0.26.5-20.el7

依存性関連をインストールしました:
  lcms2.x86_64 0:2.6-3.el7                openjpeg-libs.x86_64 0:1.5.1-18.el7       poppler.x86_64 0:0.26.5-20.el7
  poppler-data.noarch 0:0.4.6-3.el7

完了しました!

pdftk

pdftkのインストールはまた一癖ありました。
nuxというリポジトリからrpmでリポジトリを追加してpdftkをインストールします。

$ sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm
http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm を取得中
警告: /var/tmp/rpm-tmp.5Nweqm: ヘッダー V4 RSA/SHA1 Signature、鍵 ID 85c6cd8a: NOKEY
準備しています...              ################################# [100%]
更新中 / インストール中...
   1:nux-dextop-release-0-5.el7.nux   ################################# [100%]

そのあと、yumでインストール。

$ sudo yum install pdftk

インストール:
  pdftk.i686 0:2.02-1.el7.nux

完了しました!

これでpdftkが使えるかと思うと…エラーが出ます。
どうやら64ビット用のpdftkはないようでして、32ビット用しかないようです。
(pdftk.i686ですからね…)
なので、追加で以下3点をインストールします。

$ sudo yum install ld-linux.so.2

インストール:
  glibc.i686 0:2.17-260.el7_6.5

依存性関連をインストールしました:
  nss-softokn-freebl.i686 0:3.36.0-5.el7_5

完了しました!

$ sudo yum install libstdc++.so.6

インストール:
  libstdc++.i686 0:4.8.5-36.el7_6.2

依存性関連をインストールしました:
  libgcc.i686 0:4.8.5-36.el7_6.2

完了しました!

$ sudo yum install libz.so.1

インストール:
  zlib.i686 0:1.2.7-18.el7

完了しました!

これで、無事pdftkが使える状態になります。

ここからは、変換作業になります。

上記ツールを使って画像PDFをテキスト埋め込みPDFに変換する

一つ一つのファイルを操作するのは流石に面倒なので、スクリプト化してしまいました。

pdfconvert.sh
#!/bin/bash

# 引数のチェック
if [ $# -eq 0 ]; then
   echo "usage : $ pdfconvert.sh [対象ディレクトリ] [pdfパスワード]"
   echo "   [対象ディレクトリ]は必須"
   echo "   [pdfパスワード]を指定しない場合、作成したPDFにパスワードは設定されません."
   echo "終了します."
   exit 1
fi

# 引数に指定したディレクトリが存在しない場合は処理を中止する
if [ ! -d $1 ]; then
   echo "指定したディレクトリが存在しません."
   echo "処理を終了します."
   exit 1
fi

# PDFパスワードを読み込む
if [ ! x"$2" = "x" ];then
   PWDTXT=$2
else
   PWDTXT=0
fi

# PDFファイルのリスト化
LIST=`find "$1" -type f -name "*.pdf"`

### リストをループに読み込み ###
for TARGET in ${LIST}; do

# 拡張子を除いたファイル名を抽出
FILENAME=`basename ${TARGET} .pdf`

# ディレクトリ名を抽出
DIRNAME=`dirname ${TARGET}`

# パスワード付きPDFかをチェック
PDFPWD=`pdfinfo ${TARGET} | grep "^Encrypted" | awk '{ print $2;}'`

# 画像保存ディレクトリの作成
TMPDIR=${TARGET}.d
mkdir -p ${TMPDIR}

# 画像変換処理実行
pdftoppm -png -r 200 ${TARGET} ${TMPDIR}/page

# 文字抽出したPDFを作成
find "${TMPDIR}/" -type f -name "*.png" | sed 's/\.png$//' | xargs -P8 -n1 -I% tesseract %.png % -l eng+jpn pdf

# 作成したPDFを1ファイルにまとめる
# PDFパスワード有無により処理を分岐
if [ ${PDFPWD} = "yes" ] && [ ${PWDTXT} != "0" ]; then
   # ファイルの印刷可否を抽出
   PRINTABLE=`pdfinfo ${TARGET} | grep "^Encrypted" | awk '{ print $3;}' | cut -d':' -f2`
   # 印刷可否によって処理を分岐
   if [ ${PRINTABLE} = "yes" ]; then
      # パスワード付きで印刷可の場合
      pdftk ${TMPDIR}/*.pdf output ${TARGET} owner_pw ${PWDTXT} allow printing
   else
      # パスワード付きで印刷不可の場合
      pdftk ${TMPDIR}/*.pdf output ${TARGET} owner_pw ${PWDTXT}
   fi
else
   # パスワード無の場合
   pdftk ${TMPDIR}/*.pdf output ${TARGET}
fi

# 作成したディレクトリを削除する
rm -rf ${TMPDIR}

### リストループ終了 ###
done

ざっと仕様を記載します。

  • 第一引数は、変換するファイルが格納されたディレクトリを指定します。
  • 第二引数は、変換後のPDFファイルに設定するパスワードを指定します。
  • 第一引数に指定されたディレクトリ内のPDFファイルはテキスト埋め込みPDFに置換されます。オリジナルを取っておく必要がある場合は、必ずバックアップしてから実行してください。
  • 元のPDFファイル名.dフォルダを一時ディレクトリとして作成し、その中にpngファイルを作成します。作成したpngファイルをtesseractでOCRしてpngファイルと同名のテキスト埋め込みPDFファイルを作成し、最後にpdftkで結合と同時にパスワード設定、印刷設定をしています。
  • ログ出力機能は実装していません。必要は方は"/bin/bash -x pdfconvert.sh [対象ディレクトリ] [pdfパスワード] > /tmp/pdfconvert.log 2>&1 &"的にコマンドを工夫してログを出力させてください。
  • 細かいところはスクリプトをご覧ください。イケてないところ、間違いがございましたらご指摘頂けると幸いです。

最後に

上記スクリプトで1.2GB弱、1500程度のファイルを変換するのに約2日かかりました。
CPUの性能にもよるとは思いますが、Xeron X3430でxargs8パラレルで実行してこれだけ時間がかかりました。
参考程度ですが、検討する際に数値をご検討にご利用いただければと思います。
変換後のファイルサイズですが、1.2GB弱→約5GBに増加しました。
この辺もストレージとの相談になると思いますので、数値をご参考までにご利用いただければと思います。

次は、変換したPDFファイルをFessでクロールしてインデックス作成する手順を気が向いたら書きます…

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4