画像処理 - team-continue/CoRE2025_Wiki GitHub Wiki
画像処理
敵のダメージパネルの検出とダメージパネルの位置を機体座標系で算出を行う.この画像処理で使用したライブラリはOpenCVのライブラリのみ.
使用したカメラ
画像処理で使用したカメラは,上の画像のやつ.解像度とフレームレートは640x480 @ 100fps、1280x720 @ 60fps、1920x1080 @ 30fpsという感じで広角170度の魚眼レンズUsbカメラ.デプスの機能はない. 画角は広ければ広いほど良いということで、このカメラを採用.
検出方法
ダメージパネルの検出方法を書く前に,まず検出するダメージパネルについて書く.
ダメージパネル
検出するダメージパネルの特徴として、前面が真ん中のパネル本体の上下にLEDバンパーが挟んだ構造をしている.そして、このLEDバンパーが赤色もしくは青色に発光し,撃破状態(HPが0の状態)になると黄色に発光,無敵状態になると緑色に発光する. そのため,敵のダメージパネルを認識する時は,LEDバンパーが赤色もしくは青色に発光しているダメージパネルを認識すればよし.
検出
手順は下記の通り.
- LEDバンパーの色(赤色or青色)とダメージパネルの本体の色をそれぞれ閾値決めて二値化 もちろん、BGR画像はHSV画像に変換してH(色相)S(彩度)V(明度)の閾値を設定.RGBで決めるとかムリゲー...
- 良い感じにカーネルのサイズ決めて、画像の膨張縮小でノイズ処理
- 二値化した2つの行列(LEDバンパー&ダメージパネル本体)をそれぞれラベリング処理
- ダメージパネルと認識したラベルの重心座標の上下に、LEDバンパーと認識したラベルの重心座標がダメージパネルのレティクル(幅)内に存在すれば、そのラベルをダメージパネルとして認識.その座標を保持する.
設けたパラメータ
- LEDバンパーが赤のHSV値の閾値(上限値&下限値)
- LEDバンパーが青のHSV値の閾値(上限値&下限値)
- ダメージパネル本体の色の閾値(上限値&下限値)
- 画像の膨張縮小に使用するカーネルのサイズ
この検出方法で実装した結果、計7つ(実際19つ)のパラメータを設定する羽目になった.多すぎ... もっといい方法あったかもね.
ダメージパネルの座標算出
座標系
まず,ダメージパネルの座標算出する上で出てきた座標系について説明する.計算過程で出てきた座標は下の画像の3つ.最終的にはカメラ座標系のダメージパネルの座標を求める.
座標系 | 説明 |
---|---|
イメージ座標系 | OpenCVで画像読み込んだ時のデフォルトの座標系 $(X'_i, Y'_i)$ |
正規化したイメージ座標系 | イメージ座標系の原点を真ん中に移した座標 $(X_i, Y_i)$ |
カメラ座標系 | カメラ視点の座標系 $(X_c, Y_c, Z_c)$ |
ちなみに,ロボットのurdfはこんな感じ.カメラ2つ付いてるけど,画像処理で使用するのは一番高いところに付いてるcamera1_link.
算出
まず,イメージ座標系 $(X'_i, Y'_i)$ で求めたダメージパネルの重心座標を正規化したイメージ座標系 $(X_i, Y_i)$に反映させる.カメラ行列 $K$を使って算出します.本来ならレンズ歪みの補正とかもするんでしょうけど,面倒くさいのでやってません.レンズ歪みの補正なしで試してダメなら実装する予定でした.カメラ行列 $K$はOpenCVライブラリのサンプルにキャリブレーションのプログラムがあるので,それを使います. $f_x$と $f_y$はカメラの焦点距離, $c_x$と $c_y$は画像の中心座標.
$$ \begin{eqnarray*} K &=& \begin{bmatrix} f_x & 0 & c_x \ 0 & f_y & c_y \ 0 & 0 & 1 \end{bmatrix} \ \begin{bmatrix} X_i \ Y_i \ Z_i \end{bmatrix} &=& K^{-1} \begin{bmatrix} X'_i \ Y'_i \ 1 \end{bmatrix} \end{eqnarray*} $$
次に,正規化したイメージ座標系 $(X_i, Y_i)$からカメラ座標系 $(X_c, Y_c, Z_c)$に変換する.でも,正規化したイメージ座標から求められるのは,上記のカメラから見たダメージパネルの方向ベクトル $(X_{vec}, Y_{vec}, Z_{vec})$のみ.その上,画像処理に使用したカメラにはデプスはないので,カメラ座標系のX軸は求められません.無理やり計算します.はい... Core2025のルールにはダメージパネルの取付に規則があります.
床から下部LEDバンパーの最下端までの距離が200mm-300mm以内に収まるように取り付け、競技中に⾼さが⼤きく変化しないようにすること。なお、フライングディスクや段差等への乗り上げによる⼀時的な⾼さの変化は許容する。
ということで,地上からダメージパネルの高さは勘で決めます.これで良い感じにカメラ座標系の座標に変換します. では,算出します.イメージ図と数式はこんな感じ.
$$ \begin{eqnarray*} X_{vec} &=& Z_i\ Y_{vec} &=& -X_i \ Z_{vec} &=& -Y_i \ \theta_{(pitch)} &=& \arctan\frac{Z_{vec}}{X_{vec}} \ \theta_{(yaw)} &=& \arctan\frac{Y_{vec}}{X_{vec}} \ Z_{c} &=& Height_{(damagepanel)} - Height_{(camera)} \ X_{c} &=& \frac{Z_c}{\tan\theta_{(pitch)}} \ Y_{c} &=& X_c * \tan\theta_{(yaw)} \ \end{eqnarray*} $$
これで完成.tf2でcamera1_linkを親にしてダメージパネルの座標 $(X_c, Y_c, Z_c)$をbroadcastするだけ.
お試し
https://github.com/user-attachments/assets/b0f539f5-e601-49cf-9f4c-b9208b0a2a46
カメラのログとか取れなかったので、遠くから撮った試合の映像で試してみた.
後半とか検出できてないけど、実際ロボットに取り付けて検出する想定だから!割と至近距離だから!
この距離で検出できるなら、まあ割と使えるんじゃないですかね...。(パラメータの多さに目を背けながら)