PDFファイル上のQRコードを認識させる際のひと工夫

このご時世になってもキーボード入力は苦手という方が多く、多くの人に使ってもらうシステムを作る際は、この辺がすごく障害になります。ということでそのような方もサポートすべく、QRコードが記載されているPDFファイルを認識させてサーバに取り込むような機能を作ろうか、と思い立ち色々実験していたら以外と嵌まったので、ここで共有しておこうと思います。

QRコードの生成方法は色々ありますのでここでは触れません。

なお、QRコードは(株)デンソーウェーブの登録商標です。使い方などの注意事項は
http://www.qrcode.com/faq.html
を参考にしてください。

# QRコードの認識自体はZBarを使えばすごく簡単

すべてCentOS7上で実行しております。まずは、zbarという先人が作ってくれた素晴らしいツールをインストールすることから始まります。EPELリポジトリにありますので、EPELが使えない人はまずはこの作業を行います。（必要に応じてrootになるなり、sudo付けるなりしてください）

```bash
$ yum install epel-release
```

EPELが使える状態になれば、以下のコマンドで一発でZBarが入ります。

```bash
$ yum install zbar ImageMagick ImageMagick-devel
```

imagemagickは必須ではないですが、後の記事中に出てくるのでインストールしてあります。

サンプルQRコード
![Qiita_QR.png](https://qiita-image-store.s3.amazonaws.com/0/104477/b4bc2f47-0317-4ce2-0d6d-8434143d080f.png)

使い方は簡単でzbarimgというコマンドに画像ファイルを指定するだけです。

```bash
$ zbarimg Qiita_QR.png
[ここに認識した内容が表示される]
scanned 1 barcode symbols from 1 images in 0 seconds
```

画像ファイルに複数のQRコードが入っていれば、複数分出力してくれます。認識順はイマイチよく分からなかったので、その辺も対応するならあらかじめimagemagickでcropするなどが必要かもしれませんが、今回の私の作業の要件では不要だったので確認してません。

# スキャナから取り込んだPDFファイルでQRを認識させようとすると認識率がガッツリ下がる

いやー、QRの認識なんて簡単ｗ 楽勝w（自分で作ったツールでもないのにｗ）なんて思いつつ、PDFファイルを認識させる実装を進めていったところ、途中で上手く認識しないことが出てくることに気づきました。

```
$ zbarimg scan.pdf
scanned 0 barcode symbols from 3 images in 0.25 seconds


WARNING: barcode data was not detected in some image(s)
  things to check:
    - is the barcode type supported?  currently supported symbologies are:
      EAN/UPC (EAN-13, EAN-8, EAN-2, EAN-5, UPC-A, UPC-E,
      ISBN-10, ISBN-13), Code 128, Code 93, Code 39, Codabar,
      DataBar, DataBar Expanded, and Interleaved 2 of 5
    - is the barcode large enough in the image?
    - is the barcode mostly in focus?
    - is there sufficient contrast/illumination?
```

コンピュータ上で直接出力したQRコード入りのPDFは認識できるのですが、スキャナで取り込み解像度300dpiで取り込んだQRコード入りのPDFだと認識できなくなるのです。QRコードを大きくしたり、imagemagickで無理やり画像を補正すると認識できるようにはなるのですが、それでも異様に認識率が悪い。そもそも取り込んだPDFファイルも若干コントラストは低いが、見た感じ解像度も十分足りているし。。。Zbarimgコマンド自体に調整するオプションもないようですし、困った…

なんとなくPDF画像自体が悪いのか？と思ってimagemagickについてるidentifyという画像を調べるコマンドに-verboseオプションを付けて見てみると

```
$ identify -verbose QiitaQR.pdf
Image: QiitaQR.pdf
  Format: PDF (Portable Document Format)
  Class: DirectClass
  Geometry: 842x595+0+0
  Resolution: 72x72
  Print size: 11.6944x8.26389
  Units: Undefined
  Type: TrueColorAlpha
  Endianess: Undefined
  Colorspace: sRGB
  Depth: 16/8-bit
```

あれ？　画像のPixcel数が842x595になってる。PDF内に埋め込まれてる画像はAdobeのPhotoshop上で見ると3506×2480なんですが、なにゆえ？？？　Unitsの項目がUndefinedってのも？？？　たしかにimagemagickでconvertしただけで極端に画像がぼやける。ZBarも内部でimagemagickと同じ画像解析エンジンを使ってるようですので、どうもPDF内の解像度？[^1]を正しく認識できず、粗い画像で解析してしまうためQRコードを認識できないようです。AdobeのAcrobatなどで正しく設定することは可能かもしれませんが、すべてのPDFファイルにこれを期待するのも無理な話です。もし、この辺で正しくPDFファイルを認識できる方法をご存じの方は教えてください（以下の手順が面倒なのでｗ）

# 違うアプローチでトライ 〜画像を抜き出す〜

しかたないので、違う方向からゴールを目指すことにしました。PDFファイル内の画像を抜き出して、それをZBarにかければどうか？と。PDFから画像を抜き出すのはpdfimageというコマンドを使えば良いようで、xpdfをインストールすれば使えます。

```bash
$ yum install xpdf
```

これでPDFファイルから画像を抜き出します。今回はスキャナから取り込んだ3ページ分連結しているPDFファイルを使います。

```bash
$ pdfimages scan.pdf out -png

$ ls -al
-rw-r--r-- 1 root root 26110061  3月 20 14:43 scan.pdf
-rw-r--r-- 1 root root  5664271  3月 20 14:49 out-000.png
-rw-r--r-- 1 root root  5663376  3月 20 14:49 out-001.png
-rw-r--r-- 1 root root  5665273  3月 20 14:49 out-002.png
```

とやると、out-000.png、out-001.png、out-002.pngと3ページ分のpngファイルができあがります。ちなにみ -png オプションを付けないと.ppmというビットマップフォーマットの画像になります。処理時間は圧倒的に.ppmの方が早いですが、ファイルサイズは数倍になります。この辺はお好みで。

できあがったpngもしくはppmファイルをZbarにかけると、問題なくQRコードが認識されます。複数ページのファイルはちょっと手間ですが、なにかしらのスクリプトをかませばなんとかなりそうです。

ああ、3連休がこんな作業でつぶれてしまった。。。

[^1]: そもそも「PDFファイルそのもの」に「解像度」という概念がないようです　http://blog.antenna.co.jp/PDFTool/archives/2007/01/25/