Swift Playgroundsで学ぶiOSプログラミング

バーコードやQRコードを認識・デコードするプログラム

文●柴田文彦 編集●吉田ヒロ

2018年05月30日 17時00分

タップしたバーコードの内容をアラートに表示する

 VisionによるQRコード/バーコード認識機能の本質的な部分は、上のプログラムによって確認できました。しかし、これだけでは実用的なプログラムとは言えません。そこでビューコントローラーを導入して、画面に表示された画像の中のQRコード/バーコード部分にタップすると、その内容を文字列としてアラートに表示するアプリ風のプログラムに仕上げてみましょう。

 この連載に最初からお付き合いいただいている読者なら、このような方針に対して、何をどうすればいいのか、すぐに思い浮かべることができるでしょう。まず認識すべき画像は、イメージビュー(UIImageView)として、ビューコントローラーのビューに設定します。タップの検出にはジェスチャーレコグナイザーを使います。アラート(UIAlertController)も、わりと最近登場したばかりです。上のプログラムに、そうしたユーザーインターフェース要素を組み込んでいきましょう。

 1つだけ注意すべきことは、認識する画像自体(UIImage)とイメージビュー(UIImageView)の座標系の違いです。画像はイメージビューとサイズがぴったり同じでない限り、縮小、または拡大されて表示することになります。そのため、タップされた位置の座標(イメージビューの座標系)が、コードが描かれた画像の座標(イメージの座標系)のどこに相当するのか、座標値を変換して対応させる必要があります。

 この変換は、認識させる画像の大きさはもちろん、iPadの画面の向き、あるいはiPad本体の種類によっても異なります。そこで、その都度イメージビューとイメージの大きさを考慮して計算する必要があります。今回は、イメージビューのコンテントモードとして、画像のアスペクト比を維持したまま、イメージビューにぴったりとはめ込む.scaleAspectFitを設定することを前提とします。その上で、座標を変換するパラメータを計算するadjustCoordsファンクションを作成しています。それによって計算する座標の原点のズレを表すoffsetと、拡大/縮小率を表すscaleを、このビューコントローラーのプロパティとして用意しています。

コードの画像の上をタップすると、その位置のコードの内容をアラートに表示するようなプログラムを書いてみましょう。そのためにはビューコントローラーを導入します。認識したコードの枠と内容は、それぞれcBoxesとcStrsという配列に入れることにします

 このプログラムでは、とりあえず認識したすべてのコードの枠を描くことはやめました。そのため、グラフィックコンテキストを利用する必要がなくなり、そのぶん簡略化されています。枠を描く代わりに、検出したコードの枠を表す長方形と、それに対応する内容の文字列を、それぞれcBoxesとcStrsという配列のプロパティに格納しています。

認識する画像は、ビューコントローラーのビューをイメージビューに変更してそのimageプロパティにセットすることで画面に表示します。ビューコントローラーのコンテントモードは、縦横比を保ったままスケーリングしてぴったり収める.scaleAspectFitを選びます

 タップを検出するジェスチャーレコグナイザーは、1本指で1回タップする動作を検出します。

ユーザーのタップ操作はUITapGestureRecognizerを使って認識します。1本指で1回だけタップするという最も単純なジェスチャーに反応するように設定しています

 タップジェスチャーを検出することによって呼び出されるtappedファンクションでは、上で述べたような座標変換を実行する準備として、まずadjustCoordsファンクションを呼び出します。

ジェスチャーを認識すると、タップした点の座標を画像の座標に変換してから認識したコードの枠と比べます。タップした点を包含するコードの枠が見つかれば、その内容をアラートに表示します

 これによって、その時点でのオフセットとスケールがセットされるので、それらを使ってイメージビュー上でタップされた座標を画像上の座標に変換します。あとは、その座標が、検出したコードの枠の中に入っているかどうかを検索します。タップされた点を含む枠が見つかれば、その枠に対応する文字列をアラートに表示します。

 座標のオフセットとスケールを計算するadjustCoordsファンクションでは、まずイメージビューとイメージ、両方のサイズを調べます。

イメージビューとイメージの座標系の間で変換する処理を補佐するために、両者の間のスケールとオフセットを計算するadjustCoordsファンクションを用意しました。座標変換が必要になる都度呼び出して最新の値に更新しています

 この例ではコンテントモードが.scaleAspectFitであると想定しているので、縦と横の両方について計算したスケールのうち小さいほうが実際のスケールになります。また.scaleAspectFitでは、画像は必ずイメージビューの中央に表示されることになるので、オフセットは両者のサイズとスケールを利用して簡単に求めることができます。

 このプログラムを動かすと、まず認識すべき画像がビューコントローラーにセットしたイメージビューに表示されます。

プログラムを起動するとプレイグラウウンドのライブビューにイメージビューが表示され、そのイメージとしてQRコードの画像が表示されました。どれかのコードをタップしてみましょう

 このうち、どれかのコードの上をタップすると、その内容がアラートに表示されます。

タップしたQRコードの内容を表す文字列がアラートに表示されました。このままではどのコードを認識したのかわからない表示になっています

タップしたバーコードをアニメーションによってマークする

 以上のプログラムで、機能的には最初に考えたものが実現できました。ただし、このままでは、どのコードの内容を表示しているのかわかりにくくなっています。特にコードの画像が隣接していると、どちらがタップされたのか、自分でもよくわからない場合もあるでしょう。そこで、認識したコードの枠を描くことで、それを明示することにします。ついでにアニメーションも付けて、タップした点からコードの枠に長方形が広がっていくような効果も実現しましょう。あたかも指先がバーコードセンサーになったかのような感覚で操作できるものを狙います。

 画像にコードの枠を描くのは、アニメーション効果も考えて、これまでとは違う方法を採ることにします。枠と同じサイズのビュー(UIView)を重ねて配置してその外形線を描く方法です。そのためにUIViewクラスのオブジェクトcBorderを用意します。

認識したコードに外枠を表示するために、タップを認識した後に呼ばれるtappedファンクションに手を加えます。cBorderというUIViewのオブジェクトを用意して、その外形線を描くことでコードの枠を表示することにします

 この枠のビューはイメージビューのサブビューとして配置することになるので、イメージビューの座標系を使って位置と大きさを決めます。ただし画像上のコードにぴったり合わせる必要があるので、元の画像の座標系の枠をイメージビューの座標系に変換する必要があります。そこで、先ほどのタップ点を画像の座標系に変換するのとは逆の座標変換を実行しています。

 枠のビューのフレームには、最初はタップした点を原点とする小さな正方形を設定しておきます。その後、UIViewのanimateメソッドを利用し、そのフレームを認識したコードのフレームに置き換えることで、アニメーションを実現しています。

cBorderのフレームはタップした点からコードの枠まで拡大するように、UIViewのアニメーション機能を使ってユーザーにわかりやすく表示します

 このプログラムを実行して表示されたコードの上をタップすると、上で説明したようなアニメーションを伴って、コードの枠が描かれます。

プログラムを動かして表示されたコードのどれかをタップすると、そのコードの枠が描かれると同時にその内容がアラートに表示されます

 コードの内容の表示については、元のままです。

 画像をバーコードに置き換えても同様に動作します。

QRコードだけでなく、バーコードも同じプログラムで認識して枠を表示できます。ただし、バーコードの「枠」はほとんど高さのない横一直線なので、タップできる位置を探り当てる必要があります

 ただしバーコードの場合には、コード全体を囲む枠ではなく高さのほとんどない横一直線のような枠で認識するので、その上を探り当ててタップする必要があります。

次回の予定

 今回は、Visionフレームワークを使って、QRコードやバーコードを認識させました。文字の認識とは異なり、コードの内容を「読む」ことができるのが、この機能の特徴でした。次回は、Visionフレームワークから、またCoreMLに戻って、もう少し高度な画像認識を試してみる予定です。

mobileASCII.jp TOPページへ