タップしたバーコードの内容をアラートに表示する
VisionによるQRコード/バーコード認識機能の本質的な部分は、上のプログラムによって確認できました。しかし、これだけでは実用的なプログラムとは言えません。そこでビューコントローラーを導入して、画面に表示された画像の中のQRコード/バーコード部分にタップすると、その内容を文字列としてアラートに表示するアプリ風のプログラムに仕上げてみましょう。
この連載に最初からお付き合いいただいている読者なら、このような方針に対して、何をどうすればいいのか、すぐに思い浮かべることができるでしょう。まず認識すべき画像は、イメージビュー(UIImageView)として、ビューコントローラーのビューに設定します。タップの検出にはジェスチャーレコグナイザーを使います。アラート(UIAlertController)も、わりと最近登場したばかりです。上のプログラムに、そうしたユーザーインターフェース要素を組み込んでいきましょう。
1つだけ注意すべきことは、認識する画像自体(UIImage)とイメージビュー(UIImageView)の座標系の違いです。画像はイメージビューとサイズがぴったり同じでない限り、縮小、または拡大されて表示することになります。そのため、タップされた位置の座標(イメージビューの座標系)が、コードが描かれた画像の座標(イメージの座標系)のどこに相当するのか、座標値を変換して対応させる必要があります。
この変換は、認識させる画像の大きさはもちろん、iPadの画面の向き、あるいはiPad本体の種類によっても異なります。そこで、その都度イメージビューとイメージの大きさを考慮して計算する必要があります。今回は、イメージビューのコンテントモードとして、画像のアスペクト比を維持したまま、イメージビューにぴったりとはめ込む.scaleAspectFitを設定することを前提とします。その上で、座標を変換するパラメータを計算するadjustCoordsファンクションを作成しています。それによって計算する座標の原点のズレを表すoffsetと、拡大/縮小率を表すscaleを、このビューコントローラーのプロパティとして用意しています。
このプログラムでは、とりあえず認識したすべてのコードの枠を描くことはやめました。そのため、グラフィックコンテキストを利用する必要がなくなり、そのぶん簡略化されています。枠を描く代わりに、検出したコードの枠を表す長方形と、それに対応する内容の文字列を、それぞれcBoxesとcStrsという配列のプロパティに格納しています。
タップを検出するジェスチャーレコグナイザーは、1本指で1回タップする動作を検出します。
タップジェスチャーを検出することによって呼び出されるtappedファンクションでは、上で述べたような座標変換を実行する準備として、まずadjustCoordsファンクションを呼び出します。
これによって、その時点でのオフセットとスケールがセットされるので、それらを使ってイメージビュー上でタップされた座標を画像上の座標に変換します。あとは、その座標が、検出したコードの枠の中に入っているかどうかを検索します。タップされた点を含む枠が見つかれば、その枠に対応する文字列をアラートに表示します。
座標のオフセットとスケールを計算するadjustCoordsファンクションでは、まずイメージビューとイメージ、両方のサイズを調べます。
この例ではコンテントモードが.scaleAspectFitであると想定しているので、縦と横の両方について計算したスケールのうち小さいほうが実際のスケールになります。また.scaleAspectFitでは、画像は必ずイメージビューの中央に表示されることになるので、オフセットは両者のサイズとスケールを利用して簡単に求めることができます。
このプログラムを動かすと、まず認識すべき画像がビューコントローラーにセットしたイメージビューに表示されます。
このうち、どれかのコードの上をタップすると、その内容がアラートに表示されます。
タップしたバーコードをアニメーションによってマークする
以上のプログラムで、機能的には最初に考えたものが実現できました。ただし、このままでは、どのコードの内容を表示しているのかわかりにくくなっています。特にコードの画像が隣接していると、どちらがタップされたのか、自分でもよくわからない場合もあるでしょう。そこで、認識したコードの枠を描くことで、それを明示することにします。ついでにアニメーションも付けて、タップした点からコードの枠に長方形が広がっていくような効果も実現しましょう。あたかも指先がバーコードセンサーになったかのような感覚で操作できるものを狙います。
画像にコードの枠を描くのは、アニメーション効果も考えて、これまでとは違う方法を採ることにします。枠と同じサイズのビュー(UIView)を重ねて配置してその外形線を描く方法です。そのためにUIViewクラスのオブジェクトcBorderを用意します。
この枠のビューはイメージビューのサブビューとして配置することになるので、イメージビューの座標系を使って位置と大きさを決めます。ただし画像上のコードにぴったり合わせる必要があるので、元の画像の座標系の枠をイメージビューの座標系に変換する必要があります。そこで、先ほどのタップ点を画像の座標系に変換するのとは逆の座標変換を実行しています。
枠のビューのフレームには、最初はタップした点を原点とする小さな正方形を設定しておきます。その後、UIViewのanimateメソッドを利用し、そのフレームを認識したコードのフレームに置き換えることで、アニメーションを実現しています。
このプログラムを実行して表示されたコードの上をタップすると、上で説明したようなアニメーションを伴って、コードの枠が描かれます。
コードの内容の表示については、元のままです。
画像をバーコードに置き換えても同様に動作します。
ただしバーコードの場合には、コード全体を囲む枠ではなく高さのほとんどない横一直線のような枠で認識するので、その上を探り当ててタップする必要があります。
次回の予定
今回は、Visionフレームワークを使って、QRコードやバーコードを認識させました。文字の認識とは異なり、コードの内容を「読む」ことができるのが、この機能の特徴でした。次回は、Visionフレームワークから、またCoreMLに戻って、もう少し高度な画像認識を試してみる予定です。