QRコードに飽きた?独自のフィジュアルマーカーを作りましょう

QRコードから解放されたい?独自のフィジュアルマーカーを作成しよう!

QRコードはどこでも見かけますが、もっとオリジナルなソリューションを作りたいですか?それでは、独自のフィジュアルマーカーを作成し、それを検出およびデコードする方法を学びましょう。

写真: Michael Dziedzic(https://www.voagi.com/2023-hpc-fellowship-recipients-announced.htmlから)Unsplash

この記事では、新しいフィジュアルマーカーを作成し、オブジェクト検出モデルのトレーニングによってそれを検出する方法、さらには画像処理技術を使用してマーカーをデコードする方法を学びます。

以下の3つのステップに分けてみましょう:

  • フィジュアルマーカーの作成
  • 画像内でのマーカーの検出
  • マーカーのデコード

フィジュアルマーカーの作成

コンピュータビジョン用のフィジュアルマーカーはすでにたくさん存在しています。最も有名なものはQRコードです。他のQRコードも、より多用されたり堅牢性のあるものなど、さまざまな種類のものが使えます。以下は非網羅的なコードの一覧です。

著名なフィジュアルマーカーとその作成日(出典: https://www.mdpi.com/1424-8220/21/16/5407, CC-BYライセンス)

上記の画像でわかるように、フィジュアルマーカーは非常に異なるものですが、すべて同じ目的を持っています:簡単にデコード可能な情報を含むことです。

良いフィジュアルマーカーとは何でしょうか?

理想的には、良いフィジュアルマーカーには以下の特性があります:

  • 検出が容易:マーカーをデコードする前に、イメージ内で正確に検出できる必要があります
  • デコードが容易:マーカーをデコードすることが容易であり、曖昧さがないこと(つまり、デコードされたマーカーは一意の値を持つこと)

これらの特性に基づいて、既存のものから独自のマーカーを作成しましょう。

フィジュアルマーカーの設計

私は個人的にRUNEタグが好きです(あくまで恣意的な理由で):

  • 円形の形状とドットは、四角いマーカーよりも柔らかさを持っています
  • 非常に識別可能であり、オブジェクト検出モデルにとっても検出しやすいでしょう
  • 簡単にカスタマイズ可能です:必要や予想される美的要件に合わせて、円の中のドット数や円の数を微調整することができます

ただし、元の状態では完璧ではありません:2つの回転したマーカーは、デコード結果が同じになる場合もあれば、異なる場合もあります。

これらの2つのタグの唯一の違いは90°の回転です:区別することはできません。画像:著者

この問題を緩和するために、マーカーに1つの条件を追加します:黒いドットのない1つの象限のみが存在するという条件です。

黒点がない1行だけのタグであり、回転問題を解決することができます。著者による画像。

このようなマーカーは簡単にデコードできます。各象限が0、1、または2の3つの可能な値を取ることを考えてみましょう:

  • 小さな黒点:0
  • 大きな黒点:1
  • 両方の点:2
いくつかの象限を表したもの:象限0は黒点がない唯一のものであり、他の象限は常に少なくとも1つの黒点を持っています。著者による画像。

一般的に言えば、C個の円形層を持つマーカーを考慮すると、象限は2ᶜ−1の値を取ることができます(黒点がない場合は象限0に予約されています)。

最終的に、d+1個の点を持つマーカーの場合、可能な組み合わせの数は(2ᶜ— 1)ᵈに等しいです。2つの円形層と円タグごとに20個の点を持つマーカーの場合、約11億6000万通りの可能な値があります。

フィジュアルマーカーの作成

ここでは、ランダムなフィジュアルマーカーの画像を生成するために使用されるコードの一部を説明します。

完全に動作するコードへのリンクは、記事の最後に利用可能かつ文書化されています。

ご覧のように、最初のステップはランダム値のリストを生成することです。Cが円形層の数で、d+1が円の点の数である場合、numpyを使用して0から2ᶜ−1の間のd個の値のリストを生成します。

このランダム値のリストに基づいて、点の値を計算します:白点の場合は0、黒点の場合は1です。最後に、ピクセルサイズを指定して最終的なタグを描画し、出力を画像として保存します。もちろん、完全に動作するコードリポジトリへのリンクも記事の最後に用意され、文書化されています。

マーカーデザインを選択し、そのようなマーカーを使用するためのソリューションが必要です。画像でそのようなマーカーを検出およびデコードできるソリューションが必要です。これは非常にシンプルな2ステップのパイプラインです:

  • オブジェクト検出でマーカーを検出する
  • 検出されたマーカーをデコードする

では、このパイプラインの最初のステップに進みましょう。

マーカーの検出

まず、与えられた画像でマーカーの存在と位置を検出することが最初のステップです。そのためには、多くのオブジェクト検出モデルがあります。ここでは、非常に簡単にトレーニングおよび本番環境で使用できるYOLOv8モデルを使用します。

ただし、実際にオブジェクト検出モデルをトレーニングできる前に、データが必要です。さまざまな背景や環境の画像を含むデータを収集およびラベリングするのは非常に時間がかかるため、ここでは合成データを生成して使用します。

データの生成

オブジェクト検出モデルをトレーニングするために合成データを生成するには、2つの要素だけが必要です:

  • 権利のあるさまざまなバックグラウンド画像は、たとえばUnsplashから入手できます。
  • マーカー画像は、必要に応じてランダムに生成します。

これらの2つの要素で、Albumentationsを使用していくつかの拡張を適用するだけで、関連付けられたラベルを持つユニークな合成画像を多数生成できます。

ここには、背景画像のパスや円のレイヤーの数、円ごとのドットなどのマーカーの機能を指定して画像を生成するためのコードの一部を提供しましょう。

合成データを生成するために使用されるコードの要点は以下の通りです。記事の最後に完全に動作するコードへのリンクを参照してください。

これはかなり長いコードですので、詳細を調べるかどうかは自由ですが、要点をいくつか述べると以下のことを行います:

  • ランダムなタグを生成し、画像の境界ボックスのラベルが境界ボックス枠内に表示されます
  • 変形(アフィン、透視、スケール変換など)を適用するためにAlbumentationsを使用します
  • ランダムに選択した背景画像にこのタグをランダムに挿入します
  • 必要な回数だけ繰り返します

この方法で、数百または数千の画像を含む十分な大きさのデータセットを簡単に生成することができます。以下は、赤い境界ボックスとしてラベル付けされた生成された画像のいくつかの例です。

赤い境界ボックスとしてラベル付けされた生成された画像のいくつかの例。画像の作者。

生成された画像は、背景やぼかし、透視などの追加された変換があるため、かなり多様です。

もちろん、トレーニングセットと検証セットで同じ背景画像を使用しないようにして、モデルの評価ができるだけ偏らないようにしています。

YOLOモデルトレーニングのために、イメージと関連するラベルを正しいフォルダに生成するためのPythonスクリプトがGitHubリポジトリで利用可能です。

モデルのトレーニングと評価

以前に作成したデータセットを使用して、オブジェクト検出モデルをトレーニングすることができます。YOLOv8ライブラリにより、わずかなコード行で新しいモデルをトレーニングすることができます。

100エポックを超えるYOLOv8 smallモデルトレーニングのためのコード概要です。記事の最後に完全に動作するコードへのリンクを参照してください。

以下のように、モデルをインスタンス化し、データにトレーニングするだけです。

100エポック後(トレーニング中に早期終了条件が満たされた場合、ここでは約80エポック後に早期終了しました)、生成された結果からmAP@50は約0.5です。

YOLOv8ライブラリによって生成された結果。mAP@50の結果は約0.5です。画像の作者。

結果は完璧とは言えませんが、合成データのみでトレーニングされたデータセットには十分です。さらに良い結果を得るために、データの拡張を少し調整することや、実際のラベル付けされたデータが非常に役立つでしょう。

次に、ウェブカムフィードを使用してこのモデルを実際の条件でテストしてみましょう。

それには、以下のコードを使用できます:

ウェブカムフィードでYOLOv8モデル推論を行うためのコード概要です。記事の最後に完全に動作するコードへのリンクを参照してください。

このコードは非常に簡単です:

  • モデルをロードし、ウェブカムフィードを取得する
  • 新しい画像ごとにモデルの推論を行い、検出された境界ボックスを表示する
  • エスケープキーが押されたらフィードを停止する

私は自分の携帯電話のマーカーの画像でこのコードを実行しましたが、以下の画像でうまくいきました。

合成データのみでトレーニングされたYOLOモデルの検出結果。画像の作者。

すべての構成で完璧にマーカーを検出するわけではありませんが、合成データのみでトレーニングされたモデルには十分に機能します。より良い結果を得るために、データの拡張を少し調整することや、もちろん実際のラベル付けされたデータが非常に役立つでしょう。

パイプラインの最初の部分が完成したので、2番目のステップであるタグのデコードに移りましょう。

マーカーのデコード

新しい特徴マーカーを生成して検出するために完全に動作するコードがあります。

画像でタグを検出することができるようになったら、次のステップはもちろんデコードです。以前にトレーニングしたモデルによって検出されたマーカーの切り抜き画像から始めましょう。

私たちの物体検出モデルからの切り抜き画像。このマーカーをデコードしましょう。

以下の手順で構成されるデコードアルゴリズムを開発しました。

  • ドットの検出
  • 外側の円の検出と楕円の適合
  • ホモグラフィの計算に使用するドットの選択
  • ホモグラフィ行列の計算と画像の非歪み
  • 最後に、マーカーのデコード

主なアイデアは次のとおりです。検出されたマーカーを参照マーカーと一致させることができれば(円の層の数と円ごとのドット数を知っている)、画像が白か黒かをチェックすることで比較的簡単にデコードできます。しかし、そのためにはまず画像を非歪みにする必要があります。

これらの手順を一緒に進めましょう。

ドットの検出

最初のタスクは、YOLOモデルによって検出された画像でドットを検出することです。

入力の切り抜き画像から、次のOpenCVを使用した画像処理のリストを適用します:

以下のコードがその役割を果たします:

切り抜き画像からドットを検出するためのコード。記事の最後に完全に動作するコードへのリンクがあります。

上記のように、ブロブ検出器のために多くのパラメータが設定されています。最小および最大面積、また最小円形度などが設定されています。これらのパラメータを微調整するのにかなりの時間がかかりましたが、お好きにいじってみてください。

このコードを入力の切り抜き画像に適用すると、次のようなブロブ検出結果が得られます。

入力の切り抜き画像でのブロブ検出の結果:ドットがうまく検出されています。著者による画像。

ドットがうまく検出されています。次のステップは外側の円を検出することです。

外側の円の検出

今度は外側の円の層を検出する必要があります(タグ内の円の数に関係なく、この解決策は一般化可能です)。これにより、外側の円上のドットを見つけることができ、最終的に画像を非歪みにすることができます。

楕円を計算するには、より大きなドット(OpenCVではkeypointsと呼ばれる)を保持し、それらの点から楕円の方程式を適合させるだけです。以下のコードがその役割を果たします:

検出されたドットから楕円の方程式を計算するためのコード。このコードは常に中心推定を行い、後のステップで役立ちます。

このコードを適用し、検出されたポイントを散布図として表示し、フィットした楕円を表示すると、次の結果が得られます:

検出されたブロブと外側の円のフィットした楕円の散布図。著者による画像。

見るとわかるように、フィットした楕円はよく定義され、点の位置と一致しています。透視のために検出されたマーカーがどれだけ変形していても、楕円をフィットさせるので動作することになります。

今度は、実際にこの楕円上にある点を見つける必要があります。これは非常に簡単です。計算した楕円の方程式を満たす点の位置を見つけるだけです(指定された閾値で)。次のコードで行います。

コードの要点は、楕円上のキーポイントを返します。記事の最後に、完全に動作するコードへのリンクがあります。

今、どこに点があるか、そして最も外側の円の上にある点は何点あるかを知っているので、それらを使用してホモグラフィ行列を計算し、画像を解像します。

ホモグラフィ計算のための点の選択

目標は、参照画像と一致するマッチングポイントをいくつか見つけ出し、ホモグラフィ行列を計算することです。

Image of a reference tag, with all dots filled. Image by author.

上記の参照イメージを基に、検出されたブロブを適切なホモグラフィ行列で解除する必要があります。

ホモグラフィ行列を計算するために、単純にOpenCVの関数findHomographyを使用することができます。この関数には、参照画像と入力画像で少なくとも4つのマッチングポイントが必要です。これにより、ホモグラフィ行列を見つけることができます。このホモグラフィ行列によって、検出されたイメージを解像し、参照と一致させることができます。

最も外側の円上の検出されたブロブからは、オリジナルの参照イメージ上の点がどこにあるかを正確にはわかりません。そのため、最も長い連鎖を持つ最も近い隣接点のブロブを選択し、それらを参照と一致させることにします。これを行うために、2つのステップがあります。

  • 隣接行列の計算:各点に対して、隣接する点がどれかを知るために
  • 隣接行列から、最も長い連鎖の隣接点を計算する

最初のステップでは、次のコードを使用できます。

隣接行列の計算のコードです。記事の最後に、完全に動作するコードへのリンクがあります。

このコードは、隣接行列を計算し、Pythonの辞書として返します。外側の円上の既存の点のインデックスをキーとし、見つかった隣接点のインデックスのリストを値とします。

この隣接行列から、隣接するノードの最も長い連鎖を見つけることは今では非常に簡単です。このために、次のコードを使用しました。

隣接する点の最長連鎖のためのコードの要点です。記事の最後に、完全に動作するコードへのリンクがあります。

このコードは、効率的に隣接する点の最も長い連鎖を見つけ、そのインデックスのリストを返します。

この出力に少なくとも4つの点がある場合、理論的にはホモグラフィ行列を計算することができます。ただし、多くの場合、点がほぼ同じライン上にあるため、ホモグラフィ行列を正確に計算することはできません。この問題を解決するために、もう1つの点を追加します。中心に対して対称的に配置された点です。これにより、より正確なホモグラフィ計算が可能になります。

中心を考慮に入れて、与えられた点の対称点を見つけるために、以下のコードを使用できます。

入力された最長の連鎖と楕円上のすべてのキーポイントに基づいて、対称的な点を見つけるためのコード。記事の最後に、完全に動作するコードへのリンクがあります。

ただし、楕円上にいるため、中心の推定を使用して与えられた点の対称点を見つける方法は完全に信頼できるものではありません。認識の計算を行う際には、これを心に留めておく必要があります。

最終的に、以下の画像に結果が表示されます。青い円は最も長い連鎖の点、赤い円は検出された対称的な点です(最も長い連鎖の一部の点も含まれています)。

Result of the dots selection. In blue circle are the longest chain dots (except the left most). In red circle are the detected symmetrical dots. The central red dot is the estimated ellipse center. Image by author.

私たちが見るように、確かに7つの隣接した点の連鎖を選択しました。そして、その中で最も左の点の対称点を選択しました。

画像のアンワーピング

では、入力画像でいくつかの点を選択したので、参照画像で対応する点を取得し、ホモグラフィ行列を計算しましょう。そのためには、以下の入力が必要です:

  • クロップされた画像内の選択した点の位置:これはすでに計算したものです
  • これらの点の参照画像内での対応位置:参照マーカーを知っていれば、これを計算する必要があります

これらの点の位置を計算するために、以下のコードを使用します。

ホモグラフィ計算のための参照点の位置を生成するために使用されるコードの要点。記事の最後に完全動作するコードへのリンクを参照してください。

ただし、対称点の計算に可能なエラーがある場合に備えて、対称指標オフセットというパラメータを1つ追加したことに注意してください。これにより、対称点の位置にオフセットを追加することで、可能なエラーを処理することができます。

クロップされた画像と参照画像の正しい点の位置を持っているので、ホモグラフィ行列を計算し、画像をアンワープすることができます。対称点でミスをしないようにするために、次のコードスニペットでオフセットの範囲[-2、2]で、ステップ1で行います。

ホモグラフィ計算と画像のアンワーピングのためのコードの要点。記事の最後に完全動作するコードへのリンクを参照してください。

ここで行っていることは、OpenCVのfindHomography関数を使用してホモグラフィ行列を計算し、次にwarpPerspectiveを使用して画像をアンワープするだけです。そして、これを-2から2までのオフセット値で行いますので、5つのアンワープされた画像が得られます。

以下は、その結果の画像です:

Resulting unwarped images. Only the one with a -1 offset is correctly unwarped. Image by author.

オフセットによって、アンワーピングの結果はかなり異なります。視覚的な検査でオフセット-1が正しいことがわかりますが、このチェックを自動化したいと考えています。次のステップでこれを処理します:実際のマーカーのデコード。

マーカーのデコード

与えられたアンワープされた画像から、最後のステップはマーカーをデコードすることです。私たちは本当に近づいており、このステップはおそらく最も簡単です。

行うことは、期待される各点の位置ごとに、アンワープされた画像の色をチェックするだけです。画像がOtsuの二値化を経ているため、これは非常に簡単です。期待される点の周囲の3×3ピクセルの領域に黒いピクセルがあるかどうかをチェックします。ある場合は点があり、ない場合は点がありません。

アンワープされた画像からコードリストを計算するために使用されるコードの要点。記事の最後に完全動作するコードへのリンクを参照してください。

これは基本的に上記のコードが行うことです。次に、位置に応じて値を割り当てます。したがって、この関数の出力は値のリストだけです。最後に、-1の値(黒点のない予想される四分円をチェックするために、セクション「fiducial markerの設計」に関するリマインダーを参照してください)を探し、配列を最後のインデックス位置に配置し直します。

たとえば、次に、各アンワープされた画像に対して計算されたコードを示します:

  • オフセット-2:[0、2、0、-1、1、-1、0、0、0、2、2、1、2、2、0、2、2、2、0、-1]
  • オフセット-1:[2、2、2、0、2、0、1、1、1、2、2、1、2、2、0、2、2、2、0、-1]
  • オフセット0:[0、-1、2、2、0、0、-1、0、0、-1、0、1、2、2、0、2、2、2、0、-1]
  • オフセット1:[-1、2、2、2、2、0、-1、-1、0、0、0、0、0、2、0、2、2、2、0、-1]
  • オフセット2:[-1、2、1、2、1、0、1、-1、-1、-1、-1、0、0、0、2、2、0、0、2、-1]

私たちが見るように、最後のインデックスの場所には、-1の値を持つ画像が1枚だけあります:オフセットが-1の場合の非歪んだ画像です。これは私たちが目視で確認したように、マーカーを実際にデコードすることができるよく非歪んだ画像です。

このコードリストは、各可能なマーカーにとってユニークですので、ここで終了するか、ユニークな整数値を計算することができます。ユニークな値は、次のコードスニペットで非常に簡単に計算することができます:

コードリストからデコードされた最終値を計算するために使用されるコードの要点。記事の最後に完全に動作するコードへのリンクをご覧ください。

私たちの場合、誤って非歪んだ画像すべてには-1が返され、実際のマーカーには377667386の値が返されます。

以上です、入力画像から実際のユニークなコードまでの全プロセスを辿りました!さあ、今回の取り組みの制限を再確認し、反省しましょう。

完全なパイプラインの作成

全ての要素を揃えたので、あとはそれらを組み合わせて素敵でカスタムな位置指定マーカーデコーダーを作成すればいいです。それがQRコードを置き換えることができます!

再確認として、完全に動作するパイプラインのステップは以下の通りです:

  • 入力画像から、オブジェクト検出でマーカーを検出します
  • 検出された各オブジェクトに対して、画像を切り取り、次のステップを実行します
  • Otsu二値化とブロブ検出で点を検出します
  • 楕円フィッティングで最外ドットを見つけます
  • 最も近い隣接ドットの最長チェーンと対称ドットを使用して、ホモグラフィ行列を計算します
  • ホモグラフィ行列を使用して画像を非歪化します
  • 参照画像を使用してタグをデコードします

以上です!全てのコードを一から書かせるつもりはありません、すべてはGitHubリポジトリで入手できます。また、事前訓練されたオブジェクト検出モデルもあります。

このリポジトリには、サブステップを実行するためのPythonスクリプト(シンセティックな画像の生成、オブジェクト検出モデルのトレーニングなど)や、ウェブカメラを入力としてフルパイプラインを実行するためのPythonスクリプトも含まれています。使ってみてテストしてください!

最後に

この記事がお楽しみいただけて、何か学ぶことができたら嬉しいです!個人的には、このプロジェクトが機械学習と画像処理を組み合わせたところが好きです。

ただし、私が開発したアルゴリズムにはいくつかの制限があり、克服したいと考えています。実際には、すべてのマーカーをデコードすることはできません:

  • 最外円上の隣接ドットが2つ以下の場合、マーカーは正しくデコードされません
  • 最長チェーンからの対称ドットのないマーカーも同様に、不正確なホモグラフィ行列のため信頼性の低い結果を与えます

別の制限として、ホモグラフィが画像を反転させることで、リストコードが逆になり、最終的なデコードされた整数値が異なる点があります。

これらの制限を克服するためのアイデアがあれば、メッセージを送っていただくか、プルリクエストを提案していただけると嬉しいです!

もう一つのトピックとして、ここでのデコードは整数値のみを返します。これを関連するもの(リンク、アイテム、画像など)に一致させて、アプリケーションで本当に役立つものにするのはあなた次第です。直接ASCII文字のリストとしてこのようなマーカーをデコードすることも可能だと思いますが、私自身は試していません。再度、どんな貢献でも歓迎します。

参考文献

オリジナルのRUNE-Tagの論文:

F. Bergamasco, A. Albarelli, E. Rodolà and A. Torsello, “RUNE-Tag: A high accuracy fiducial marker with strong occlusion resilience,” CVPR 2011, Colorado Springs, CO, USA, 2011, pp. 113-120, doi: 10.1109/CVPR.2011.5995544.

オリジナルのRUNE-Tagリポジトリ:https://github.com/artursg/RUNEtag

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more