}ページHome2015-09-06ZXing(.Net)のバーコード認識率を上げる方法TweetZXing.Netは、1次元/2次元バーコードを認識するためのライブラリで、よく見るJAN-13(EAN-13)コードや、QRコードを認識させることができる。使い方は一見簡単で、NuGetでぽいっと入れて、数行書けば動かすことができる。バーコードが含まれる静止画を認識させようとするとデフォルトだと認識率がだいぶ低い。カメラを動かしながら認識させていくために速度重視なのであろう。この記事では、そんなZXing.Netの認識率を上げる方法について書く。コードは流用できずとも、JavaのZXingも含めた他の言語実装でも同じようなテクニックが利用可能と思う。ZXing.Netの単純な使い方BarcodeReaderを作って、Decode/DecodeMultipleメソッドにbitmapを流し込むだけ。コードをざっと見、プラットフォームごと、例えばXamarin用のコードだと受け取る画像オブジェクトが違うように見えるので、使う環境によって見てみるといいと思う。System.Drawingなんてヤダヤダ、BitmapSourceがいいという場合は、zxing.presentationアセンブリにそれを受け取るやつが居るっぽいが、この記事を書いているときに気づいたので試してない。x).ToArray();}気をつけなきゃいけないのはDecodeMultiple()はnullを返すことがあるということ。空配列にしなかったのか。...などというのはこの後説明する内容に比べれば些細なのでしれっとnullチェックする。TryHarder他オプションを設定するデフォルトだと回転すらしてくれないので、ほぼバーコード自体のみの画像でない限り認識した結果を見るほうが珍しいという事態になると思う。回転なんかはさすがにこのTryHarderオプションが面倒を見てくれるので、オプションを設定する。x).ToArray();}今回のケースではJAN-13だということがはっきりしているので、TryHarderのほかにPossibleFormatsを指示する。これでデフォルトからするとだいぶ改善する。事前に2値化するだがまだ認識してくれないものがだいぶある。輝度の処理がイマイチという話をどこかで読んだので、大津の2値化をしてから投げるだけで改善した。後述の理由により、認識させるものによってはこれでいいケースがかなりあると思う。例によってOpenCVSharpを使ったコード。しておくとMatはToBitmap()でSystem.Drawing.Bitmapにできるのがポイント。AdaptiveThresholdでもいいかなと思ったが自分の見てるソースだといつもうまくいくパラメータを持ってくるのが逆にしんどかった。カメラで撮っている画像だと頑張ってAdaptiveThresholdのパラメータ合わせた方がいいことがあるかもしれない。バーコードが黒だとはっきりしているのなら、色ありの状態で黒部分を抜き出して処理するという戦略もあると思う。そこまでじゃないかなと思ったのでやっていない。事前に領域分割するそれでも認識してくれない画像があるのだが、その画像もサイズを半分にしたら認識されたりする。見では追いかけられなかったのでコードは追っていないのだが、画像全体のサイズから一定の比率以下のサイズだと認識されない感じがする。そのため、事前に分割してしまうと、予想通りうまくいった。具体的には、Erodeで収縮させバーコード領域のあたりを潰し、そこをFindContoursで輪郭検出させる。詳しい処理はZXing.NETが面倒を見てくれるので、ざっくり分かれればよしとしている。処理イメージとしてはこんな感じ。↓コードはこんな感じ。変わったこととしては、サイズ下限でのフィルタ。頑張ってもバーコードが入らない小さい領域を食べさせてもしょうがないので。縦も数字の「8」が最低5px必要なので×2はあるだろうということで10pxとし、回転も考えて長いほう/短いほうでフィルタしてみた。あと、領域ぎりぎりで切り取ると認識しないことがあったので、白枠を付与することにした。領域分割を行っているのだから、バーコードは分割されると割り切って、画像にバーコードが複数含まれる場合でもDecodeMultiple()でなくDecode()を呼び出すと、速度が向上する。背景色の関係で領域がくっつくことがまれにあるので、Decode()を狙うならもう少し領域分割をまじめにやったほうがいいかもしれない。3.を実行してみて認識した数がこちら(グラフには個数と書いてしまったが枚数である)。MultipleはDecodeMultiple()を呼んだ版、MultipleなしはDecode()を呼んだ版。デフォルト状態での認識数はまさかのゼロ。貼ってあったQRコードを認識してて笑った。この「事前領域分割(Multiple)」(3.)のほうの残り3枚は、2つ存在するバーコードの他に一つ追加で何かを誤認識してしまったもの。(Multipleなし)」のほうも197枚なのだが、「事前領域分割(Multiple)」と同じなわけではなく、誤認識が一つ減っている代わりに、一つの画像で2つのバーコードの領域が合体し、認識が一つ漏れてしまっていた。理由は前述の背景色の関係なのだが、1/200が他にもあってもおかしくないので、自分的にはDecodeMultipleを呼んで使おうと思う。速度の違い処理方式の違いによる速度の違いは以下の通り。認識率を最も重視しているので、方法が複雑になるほど処理速度は下がっている。領域分割に関しては、ばらつきが大きいが、これはどのように分割されるかによってトータルでの領域サイズが変わるからであろう。DecodeMultipleを使わない(Decodeを使う)版だと領域分割したとしても単にTryHarderにしたDecodeMultipleより速いので、速度重視であれば自前で分割してでもDecodeMultiple()は使わないほうがよさそうである。ここはOpenCVのほうが速いからという話はありそうだが。自分で使う分には満足行くレベルになったので、もうおしまいにするが、以下のような点を行うと、より改善されるように思う。必要であればお試しされたし。自前回転PureFormatsオプションの利用ここまでやってしまったら、JAN-13程度であれば自前で認識してしまうというのも視野に入ってきてしまうと思うので、要求に応じてという感じで。