コマンドラインでPDFにOCRを掛ける on Linux(Ubuntu)

コマンドラインでPDFファイルにOCR掛けたくなったので、OCR用に[ocrize](#ocrize)というファイル名でbashスクリプトを書きました。

ocrizeを使われる方は事前に以下のコマンドを実行して、必要なパッケージをインストールして下さい。

```bash
$ sudo apt install tesseract-ocr-jpn imagemagick
# 縦書き日本語PDFにOCRを掛けるにはtesseract-ocr-jpn-vertが必要。
```

ocrizeの実行は次のように行います(事前にchmodで実行可能にして下さい)。

```bash
$ ./ocrize input.pdf
# or
$ ocrize input.pdf # 例えば、/usr/local/bin等、パスの通った場所にocrizeを置いた場合。
```

以下では、コマンドラインでPDFファイルinput.pdfにOCRを掛ける手順を解説します。

# Step.0 事前準備

input.pdfにOCRを掛ける前に、ファイル名を変数に格納して、一次保存用ディレクトリを作製しておきます。

```bash
$ stem="input" # 拡張子無しのファイル名。
$ dir="${file}.temp"
$ mkdir ${dir} # input.tempディレクトリが作られます。
```

# Step.1 PDFファイルを画像ファイルに分割

ImageMagickのconvertコマンドを使って、input.pdfを画像ファイルに分割しましょう。

```bash
$ convert -density 300 -geometry 1000 ${stem}.pdf ${dir}/${stem}.png
```

オプション`-density`の値を大きくするほど画質が良くなりますが、大きい値を指定する場合、ImageMagickのresourceを変更する必要があります。

上のコマンドを実行すると、以下のように、input.pdfをページ毎に分割してPNGに変換したファイルがinput.tempディレクトリ以下に出力されます。

```bash
$ ls ${dir}
input-0.png   input-1.png   input-2.png  input-3.png
```

`convert`コマンドで以下のエラーが出た場合は、ImageMagickのpolicy.xmlを修正しましょう(私の環境の場合、policy.xmlは/etc/ImageMagick-6直下にありました)。


## Error.1 convert: not authorized

PDFの読み込み書き込みが出来るように、domainがcoder、patternがPDFであるpolicyタグのrightを以下のように修正しましょう。

```xml
<policy domain="coder" rights="read|write" pattern="PDF" />
```

## Error.2 convert-im6.q16: cache resources exhausted

メモリーが不足しているので、domainがresourceのpolicyタグを修正しましょう。私の場合、nameがmemoryとdiskの箇所を修正しました。

```xml
<policy domain="resource" name="memory" value="1GiB"/>
<policy domain="resource" name="disk" value="2GiB"/>
```

resourceは次のコマンドで確認できます。

```bash
$  identify -list resource
# or
$  identify -list Resource
```

# Step.2 画像ファイルからOCRを掛けたPDFを作る

input.tempディレクトリ以下の画像ファイルそれぞれに対してOCRを施します。

```bash
$ N=$(ls ${dir} | grep -c '' | awk '{printf "%d", $1-1}')
$ for n in $(seq 0 ${N}); do tesseract -l jpn+eng ${dir}/${stem}-${n}.png ${dir}/${stem}-${n} pdf; done
```

オプション`-l`で認識言語を指定しています。第一優先に日本語、次に英語で認識したい場合、`jpn+eng`と指定します。

# Step.3 OCRが掛かったPDFファイルを1つにまとめる

複数のPDFファイルを1つにまとめるのに`pdfunite`コマンドを使います。

```bash
$ pdfunite $(for n in $(seq 0 ${N}); do echo ${dir}/${stem}-${n}.pdf; done) ocrized-${stem}.pdf
```

ページの順番が崩れないように`for`でPDFファイルをページ順に出力しています。実行後、input.pdfにOCRを掛けたocrized-input.pdfがカレントディレクトリ直下に作製されます。

最後に、input.tempディレクトリを削除して終わりです！

```bash
$ rm -r ${dir}
```

# ocrize

以下が、以上の内容をまとめたbashスクリプトです。ファイル名`ocrize`で保存して使って下さい。

```bash
#! /bin/bash

if [ $# -eq 1 ]; then
    file=$1
    ext=$(echo ${file} | rev | cut -d '.' -f 1 | rev)
    dir=${file}.temp
    if [ -d ${dir} ]; then
        echo "${dir} allready exists. Please remove this directory."
        exit 1
    fi
    if [ ${ext} = "pdf" -o ${ext} = "PDF" ]; then
        if [ ! -f ${file} ]; then
            echo "${file} dose not exist."
            exit 1
        fi
        stem=$(echo ${file} | rev | cut -c 5- | rev)
        mkdir ${dir}
        echo "1: Converting PDF to PNG."
        convert -density 300 -geometry 1000 ${file} ${dir}/${stem}.png
        echo "1: Finished."
       	N=$(ls ${dir} | grep -c '' | awk '{printf "%d", $1-1}')
        echo "2: OCRizing."
        for n in $(seq 0 ${N}); do
            p=$(echo "${n} ${N}" | awk '{printf "%5.1f", ($1+1)/($2+1)*100}')
            echo -ne "Progress: [${p} %]\r"
            tesseract -l jpn+eng ${dir}/${stem}-${n}.png ${dir}/${stem}-${n} pdf >& /dev/null
            rm ${dir}/${stem}-${n}.png
        done
        echo "2: Finished.        "
        echo "3: Merging PDF files."
        #pdftk $(for n in $(seq 0 ${N}); do echo ${dir}/${stem}-${n}.pdf; done) output ocrized-${pdffile}
        pdfunite $(for n in $(seq 0 ${N}); do echo ${dir}/${stem}-${n}.pdf; done) ocrized-${file}
        echo "3: Finished."
        rm -r ${dir}
    else
        echo "Extension must be pdf or PDF."
        exit 1
    fi
else
    echo "Usage: $ ocrize input.pdf"
    exit 1
fi
```

`ocrize`を実行すると、以下のようにスクリプトが走ります。

```bash
$ ./ocrize input.pdf
1: Converting PDF to PNG.
convert-im6.q16: profile 'icc': 'RGB ': RGB color space not permitted on grayscale PNG `input.pdf.temp/input.png' @ warning/png.c/MagickPNGWarningHandler/1654.
1: Finished.
2: OCRizing.
Progress: [ 84.4 %]
```

数秒後、

```bash
1: Converting PDF to PNG.
convert-im6.q16: profile 'icc': 'RGB ': RGB color space not permitted on grayscale PNG `input.pdf.temp/input.png' @ warning/png.c/MagickPNGWarningHandler/1654.
1: Finished.
2: OCRizing.
2: Finished.        
3: Merging PDF files.
3: Finished.
```

# 参考URL

* [How to use OCR from the command line in Linux? - Unix & Linux Stack Exchange](https://unix.stackexchange.com/questions/377359/how-to-use-ocr-from-the-command-line-in-linux)
* [Tesseract OCR loading a language - Japanese - Stack Overflow](https://stackoverflow.com/questions/45717363/tesseract-ocr-loading-a-language-japanese)
* [tesseractコマンドの使い方（Tesseract OCR 4.x） | テクノロジーで楽がしたい](https://blog.machine-powers.net/2018/08/02/learning-tesseract-command-utility/)
* [Warning. Invalid resolution 0 dpi. Using 70 instead. · Issue #1702 · tesseract-ocr/tesseract](https://github.com/tesseract-ocr/tesseract/issues/1702)
* [Tesseract Image Issue - Stack Overflow](https://stackoverflow.com/questions/33659458/tesseract-image-issue)
* [linux - Merge / convert multiple PDF files into one PDF - Stack Overflow](https://stackoverflow.com/questions/2507766/merge-convert-multiple-pdf-files-into-one-pdf)
* [convertに関して – kondolab](http://zairyo.susi.oita-u.ac.jp/wordpress/?p=7385)
* [Memory issues · Issue #396 · ImageMagick/ImageMagick](https://github.com/ImageMagick/ImageMagick/issues/396)