粗大メモ置き場

個人用,たまーに来訪者を意識する雑記メモ

ホモグラフィー行列を用いた鳥瞰変換(Bird's-eye view)

鳥瞰変換とは

一言で言うと,ある視点からとったカメラの画像を別の視点で撮ったように見せかける手法のことです。

他サイト様の説明がわかりやすいのでこちらに載せます。 daily-tech.hatenablog.com

より一般的には「斜めから撮った画像を真上から撮った画像のように写す」という場面で使われます。

http://cdn-ak.f.st-hatena.com/images/fotolife/r/rkoichi2001/20160526/20160526035336.png

引用:Tanaka, S., Yamada, K., Ito, T. and Ohkawa, T.: Vehicle Detection Based on Perspective Transformation Using Rear-View Camera, International Journal of Vehicular Technology, Volume 2011, Article ID 279739 (2011).

鳥瞰変換とホモグラフィー変換

カメラの撮像の式

グローバル座標系にて座標(X,Y,Z)にある点をカメラの位置①から撮像して画像上の点 (x_1,y_1)に写っているとします。 今,仮想的なカメラの回転R_{12}を施した系②でこれが (x_2,y_2) に移るとします。(この系②が上から見下ろす系になっていればいいわけですね。)

それぞれの場合でのカメラの撮像の式は以下のようになります。(Texで打ったので画像で失礼)

f:id:ossyaritoori:20190310153102p:plain
(R,t)は系①でのカメラ姿勢,太字のKはカメラ行列

モグラフィー変換

一方,画像変換で用いられるホモグラフィー変換はホモグラフィー行列Gを用いて次のような形で表せます。

f:id:ossyaritoori:20190310153504p:plain
λはスケーリング定数。

カメラの回転とホモグラフィー変換の関係性

上のニ式を見比べてみると,回転行列R_{12}Gとの間には以下のような直接対応関係があることがわかります。 当然ですが,系①と②の間に純粋な回転以外の並進成分などがあった場合はこんな簡単な関係にはなりません。

f:id:ossyaritoori:20190310153948p:plain
カメラ回転 = ホモグラフィー行列に変換可能!

この関係を用いることでカメラを擬似的に真下向きに回したときの画像を生成することができるというわけです。

おまけ:手ぶれ補正

先程の式は裏を返せば,微小な並進成分を無視すれば画像内のホモグラフィー変換からカメラの回転を補正することができるということを表しています。 この手法を用いて手ぶれ補正をしたのが以下の記事だったりするわけです。

ossyaritoori.hatenablog.com

実践編

例のごとくOpenCVを使います。

下準備

先程の式を用いて鳥瞰変換を行う際には以下のパラメータが必要になります。

  • カメラ行列K
  • 系①におけるカメラ姿勢(R,t)(→ 実はRだけでいい)

これは一般にキャリブレーションと言われる作業です。

Ubuntu-ROS環境が整っている方にとっては造作もないと思いますが,Windowsなどでやる場合は下記のサイト等でやるのが良いかと思います。 ossyaritoori.hatenablog.com

また,カメラ姿勢(R,t)を測るのは結構大変で,ARマーカ等を用いるのが最も楽かなぁという気がします。 プログラムは適当に探してもらうとして,この辺りの記事なんかが説明が簡素でわかりやすいかも?

プログラムの流れ

カメラの姿勢Rの元となるrvecから座標系①における回転{}^gR_1を計算します。 ossyaritoori.hatenablog.com

また,変換したい座標系②での回転{}^gR_2cv2.Rodriguesから生成しておきます。

この時,先程のR_{12}={}^gR_2{}^gR_1^\topと計算できます。 その後,Kをかけて作ったホモグラフィー行列を元にcv2.warpPerspectiveに投げます。以上です。

略記プログラム

コードメモですがこのままでは動きませんよ~

# calculate bird view homography
import math
theta_below = np.array([0, math.pi, 0])
Rc_, _ =  cv2.Rodrigues(theta_below)#変換先の回転
Rc,_ = cv2.Rodrigues(vm.rvecs)#オリジナル関数有


Homo = np.linalg.multi_dot([K,Rc_.T,Rc.T,np.linalg.inv(K)])#ホモグラフィー行列

nHomo = Homo/Homo[2,2]# 正規化


minx = -20000
miny = 0

# 移動量を調整しないと画面外に出ていってしまう…
nHomo[0,2] -= minx 
nHomo[1,2] -= miny 

# 2000pix四方の画面を用意しないとどっか行っちゃうので…
out=cv2.warpPerspective(img,nHomo,(2000,2000))

f:id:ossyaritoori:20190310160628p:plain
元の画像

f:id:ossyaritoori:20190310160653p:plain
変換後の画像(Z軸も回すべきかも)