観光客向けに日本のプロ野球を中心としたコンシェルジュチケットサービスを運営しています。どんどん、チームは QRコードのチケットをますます採用しており、多言語対応のカスタムチケットを作成する可能性が開かれています。
例えば、次のチケットを考えてみましょう。
上は、英語翻訳を追加する前の典型的な阪神タイガースの電子チケットの外観です。
実現したいのは、顧客向けにチケット情報をデータベースに入力し、顧客向けには英語でモバイル端末に表示して、球場の案内係向けには日本語で表示できるようにしたいということです。現在、バックアップとして、次のようなよりわかりやすい方法でチケットを印刷しています。
手動で作成しているため、英語を追加したのオリジナルと非常によく似ています。それぞれチームは独自のデザインをしているので、同一形式なスタイルQRコードチケットを印刷できればいいなと思います。
そのためのに、QRコードの内容値を読み取れて、再現出力できる可能な方法を調べてみましょう。Livebookを利用して、それがどのように機能するかを理解してみましょうと試してみましょう。
QRコード遊び場
上記の QRコードをスキャンする場合は、その内容が 202302088808H19425
であることがわかります。よっし。しかし、画像を保存せずに、QRコードの読み取りと作成を自動化できるようにしたいと考えています。そのために、新しい Livebook を開いて、:evision
と :kino
をインストールして、実験してみましょう。
Mix.install([
{:evision, "~> 0.1.33"},
{:kino, "0.11.0"}
])
次に、上記の最初のタイガース チケットの画像をLivebook の Files
セクションの + Add File
領域にドラッグ アンド ドロップします。
これにより、現在の Livebookセッションに添付ファイルとして追加されます。では、添付されたファイルを読み込んでみましょう。
alias Evision, as: Cv
Kino.FS.file_path("2023-05-04-han-breeze-35-97.png")
|> Cv.imread()
Evision
は Cv
("Computer Vision" のこと?) というエイリアスとしてよく使われるそうので、使用します。
添付された PNGファイルのパスを取得し、Evision
の imread/1
関数にパイプするだけです。次のような結果が得られます。
次に、Cv.QRCodeDetector
にパイプして、QRコードの内容を取得しましょう。
|> then(&Cv.QRCodeDetector.detectAndDecode(
Cv.QRCodeDetector.qrCodeDetector(), &1))
QRコードのデコードされた文字列がこの結果に表示されています。後で使用するために抽出しましょう。
alias Evision, as: Cv
{decoded_string, points, straight_qrcode} =
Kino.FS.file_path("2023-05-04-han-breeze-35-97.png")
|> Cv.imread()
|> then(&Cv.QRCodeDetector.detectAndDecode(
Cv.QRCodeDetector.qrCodeDetector(), &1))
decoded_string
QRコードを出力する
これで、デコードされた QRコードの内容が得られ、ゲート、通路、列、座席番号とともにデータベースに保存できるようになりました。QRコード内容にその情報が含まれていると思いますが、それを解読することに興味はありません。QRコードを任意のサイズで再現して様々な方法で出力したいだけです。
そのためには、Evision
モジュールを使用すると非常に簡単になります。
Cv.QRCodeEncoder.encode(Cv.QRCodeEncoder.create(), decoded_string)
これで QRコードを出力しましたがが、かなり見にくいです。小さすぎて、上の圧縮画像ではスキャンするはできないと思います。もう少し大きくで出力しましょう。
|> Cv.resize({300, 300})
うまく大きくなりまして、スキャンするとは可能です。でも、なんだかぼやけてますね。このために特別に設計された画像補間アルゴリズムを使用して、もっとはっきり出力しましょう。
|> Cv.resize({300, 300}, interpolation: CV.Constant.cv_INTER_AREA())
はるかに優れており、シャープです。実際、上記の翻訳されたチケットに使用した QRコードはこの作成されたより少しぼやけていることがわかります。このワークフローにより、任意のサイズで鮮明に出力できるようになります。
複雑な QRコード
タイガーズの QRコードはどれもシンプルでベーシックな QRコードです。他のほとんどすべての球団は、り複雑で大きな QRコードを使用しています。以下がスワローズの代表的なチケットです。
以上の画像を Livebookに添付して、試してみましょう。
{decoded_string, points, straight_qrcode} =
Kino.FS.file_path("2023-10-04-yak-ss1-22-18.png")
|> Cv.imread()
|> then(&Cv.QRCodeDetector.detectAndDecode(
Cv.QRCodeDetector.qrCodeDetector(), &1))
decoded_string
よっし! ここまでは順調ですね。それでは、QRコードを再生成してみましょう。
Cv.QRCodeEncoder.encode(Cv.QRCodeEncoder.create(), decoded_string)
|> Cv.resize({300, 300}, 補間: Cv.Constant.cv_INTER_AREA())
うーん。QRコードを再作成しました。しかし、オリジナルの QRコードとは見えません。新しい QRコードをスキャンすると、同じデコードされた文字列になります。
"d34a78f948c92e681f66b34b7c34806eeaee98709e3ae76a69d620103d24fb37"
ちょっと Base 16でエンコードされた文字列のようです。UTF-8 またはシフト JIS でしょうか? いいえ、d34a
は UTF-8で「퍊」の韓国語の文字であって、シフトJISの最初のバイトが 0xd3
であるものはありません。
じゃ、Base 16でエンコードされた文字列をバイトの配列に変換し、QRコードを生成して見てみましょう。
{decoded_string, points, straight_qrcode} =
Kino.FS.file_path("2023-10-04-yak-ss1-22-18.png")
|> Cv.imread()
|> then(&Cv.QRCodeDetector.detectAndDecode(Cv.QRCodeDetector.qrCodeDetector(), &1))
decoded_string
|> Base.decode16!(case: :lower)
|> IO.inspect()
|> then(&Cv.QRCodeEncoder.encode(Cv.QRCodeEncoder.create(), &1))
|> Cv.resize({300, 300}, interpolation: Cv.Constant.cv_INTER_AREA())
いやー。最初の QRコードから再生された QRコードにデータがすこじまばされたそうでしたが、もっとデータが減られたそうに見えます。
より大きな QR コードを機能させるには、さらに研究する必要があるようです。ここで何が起こっているのか知っている人がいたら、ぜひご意見をいただきたいです。