web-dev-qa-db-ja.com

OpenCVを使用した長方形の検出/追跡

必要なもの

私は現在、拡張現実感のちょっとしたゲームに取り組んでいます。ゲームで使用するコントローラー(ここでは物理的な入力デバイスについて説明しています)は、単色の長方形の紙です。カメラのキャプチャストリームで、その四角形の位置、回転、サイズを検出する必要があります。検出は、スケールでは不変で、X軸とY軸に沿った回転では不変でなければなりません。

ユーザーが用紙をカメラから遠ざけたりカメラに近づけたりする場合に、スケール不変性が必要です。スケール不変性がサイズ不変性に変換されるように、長方形の距離を知る必要はありません。

ユーザーがローカルのX軸またはY軸、あるいはその両方に沿って長方形を傾ける場合、回転不変性が必要です。このような回転により、用紙の形状が長方形から台形に変わります。この場合、オブジェクト指向の境界ボックスを使用して、用紙のサイズを測定できます。

私がやったこと

最初にキャリブレーションのステップがあります。ウィンドウにカメラフィードが表示され、ユーザーは四角形をクリックする必要があります。クリックすると、マウスが指しているピクセルの色が参照色として使用されます。フレームはHSVカラースペースに変換され、色の識別が改善されます。各チャンネルの上限と下限のしきい値を調整する6つのスライダーがあります。これらのしきい値は、画像を二値化するために使用されます(opencvのinRange関数を使用)。
その後、ノイズを除去し、nerbyチャンクを結合するためにバイナリイメージを侵食および拡張します(opencvのerodeおよびdilate関数を使用)。
次のステップは、バイナリイメージ内で輪郭を見つけることです(opencvのfindContours関数を使用)。これらの輪郭は、最小の向きの長方形を検出するために使用されます(opencvのminAreaRect関数を使用)。最終結果として、私は最大の面積を持つ長方形を使用しています。

手順の短い結論:

  1. フレームをつかむ
  2. そのフレームをHSVに変換します
  3. 2値化(ユーザーが選択した色とスライダーからのしきい値を使用)
  4. モーフ操作の適用(侵食および拡張)
  5. 輪郭を見つける
  6. 各コンターの最小の方向付けされたボウディングボックスを取得します
  7. 結果として、それらのバウンディングボックスのうち最大のものを取ります

お気づきかもしれませんが、この情報を適切に使用する方法がわからないという理由だけで、実際の用紙の形状に関する知識を活用することはありません。

また、opencvの追跡アルゴリズムの使用についても考えました。しかし、私がそれらを使用することを妨げた3つの理由がありました:

  1. スケール不変性:一部のアルゴリズムについて読んだ限り、一部のアルゴリズムはオブジェクトの異なるスケールをサポートしていません。
  2. 移動予測:一部のアルゴリズムはパフォーマンスを向上させるために移動予測を使用しますが、追跡しているオブジェクトは完全にランダムに移動するため、予測できません。
  3. シンプルさ:画像内に単色の四角形を探しているだけで、車や人の追跡のような派手なものはありません。

ここに-比較的-良いキャッチ(侵食および拡張後のバイナリイメージ) ok

そして、ここは悪いものです bad

質問

一般的に、特に照明の変化に対する耐性を高めるために、どのように検出を改善できますか?

更新

ここ はテスト用の生の画像です。

より厚い材料を使用することはできませんか?
はい、できます。すでにできます(残念ながら、現時点ではこれらの部分にアクセスできません)。ただし、問題はまだ残っています。段ボールのような素材を使用しても。紙ほど簡単に曲げることはできませんが、曲げることはできます。

どのようにして長方形のサイズ、回転、位置を取得しますか?
opencvのminAreaRect関数は、RotatedRectオブジェクトを返します。このオブジェクトには、必要なすべてのデータが含まれています。


長方形は単色なので、上下と左右を区別することはできません。これは、回転が常に範囲[0, 180]これは、私の目的にはまったく問題ありません。長方形の両側の比率は常にw:h > 2:1。長方形が正方形の場合、回転範囲は[0, 90]、しかしこれはここでは無関係とみなすことができます。

コメントで示唆されているように、輝度の問題を軽減し、ORB、SURF、およびSIFTを調べるために、ヒストグラムの均等化を試みます。

進行状況を更新します。

14
Timo

質問をしてからしばらく経っています。私は最近このトピックを続け、問題を解決しました(ただし、四角形の検出は行いませんでした)。

変更点

  • 以下のように木材を使用してコントローラー(「長方形」)を強化します。
  • 各コントローラーに2 ArUco マーカーを配置。

Controller

使い方

  • フレームをグレースケールに変換し、
  • ダウンサンプリング(検出中のパフォーマンスを向上させる)、
  • cv::equalizeHistを使用してヒストグラムを均等化します。
  • cv::aruco::detectMarkersを使用してマーカーを検索し、
  • 相関マーカー(複数のコントローラーの場合)、
  • マーカーの分析(位置と回転)、
  • 結果を計算し、エラー修正を適用します。

マーカーの検出は、照明の変化やさまざまな視野角に対して非常に堅牢であるため、キャリブレーション手順をスキップできます。

検出の堅牢性をさらに高めるために、各コントローラーに2つのマーカーを配置しました。両方のマーカーを1回だけ検出する必要があります(それらの相関関係を測定するため)。その後、コントローラーごとに1つのマーカーのみを見つけるだけで十分です。もう1つのマーカーは、以前に計算された相関から推定できます。

明るい環境での検出結果は次のとおりです。

Detection in a bright environment

より暗い環境で:

Detection in a dark environment

マーカーの1つを非表示にする場合(青い点は、外挿されたマーカーの位置を示します):

Detection of missing markers

失敗

私が実装した最初の形状検出はうまく機能しませんでした。照明の変化に対して非常に脆弱でした。さらに、最初のキャリブレーション手順が必要でした。

形状検出アプローチの後、SIFTとORBをブルートフォースとknnマッチャーと組み合わせて試し、フレーム内の特徴を抽出して特定しました。モノ色のオブジェクトは、多くのキーポイントを提供しないことが判明しました(驚くべきことです)。とにかくSIFTのパフォーマンスはひどいものでした(540 pで約10 fps)。コントローラー上にいくつかの線やその他の形状を描いた結果、より多くのキーポイントが使用可能になりました。ただし、これでは大きな改善は得られませんでした。

3
Timo

HSV空間のHチャネルは色相であり、光の変化に敏感ではありません。約[150,180]の赤の範囲。

上記の情報に基づいて、次の作業を行います。

  1. HSV空間に変更し、Hチャネルを分割し、しきい値を設定して正規化します。
  2. モーフの適用(オープン)
  3. 輪郭を見つけ、いくつかのプロパティ(幅、高さ、面積、比率など)でフィルタリングします。

PS。ネットワークが原因で、Dropboxにアップロードした画像を取得できません。そこで、入力としてcrop 2番目の画像の右側 を使用します。

enter image description here

imgname = "src.png"
img = cv2.imread(imgname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

## Split the H channel in HSV, and get the red range
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)
h[h<150]=0
h[h>180]=0

## normalize, do the open-morp-op
normed = cv2.normalize(h, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8UC1)
kernel = cv2.getStructuringElement(shape=cv2.MORPH_ELLIPSE, ksize=(3,3))
opened = cv2.morphologyEx(normed, cv2.MORPH_OPEN, kernel)
res = np.hstack((h, normed, opened))
cv2.imwrite("tmp1.png", res)

さて、次のように結果を取得します(h、normed、opened):

enter image description here

次に、輪郭を見つけてフィルタリングします。

contours = cv2.findContours(opened, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
print(len(contours))[-2]

bboxes = []
rboxes = []
cnts = []
dst = img.copy()
for cnt in contours:
    ## Get the stright bounding rect
    bbox = cv2.boundingRect(cnt)
    x,y,w,h = bbox
    if w<30 or h < 30 or w*h < 2000 or w > 500:
        continue

    ## Draw rect
    cv2.rectangle(dst, (x,y), (x+w,y+h), (255,0,0), 1, 16)

    ## Get the rotated rect
    rbox = cv2.minAreaRect(cnt)
    (cx,cy), (w,h), rot_angle = rbox
    print("rot_angle:", rot_angle)  

    ## backup 
    bboxes.append(bbox)
    rboxes.append(rbox)
    cnts.append(cnt)

結果は次のようになります。

rot_angle: -2.4540319442749023
rot_angle: -1.8476102352142334

enter image description here

ソース画像の青い長方形タグのため、カードは2つの面に分割されます。しかし、きれいな画像は問題ありません。

2
Kinght 金