estimateAffine2DとestimateAffinePartial2D

python

 OpenCVの関数です。便利に使っている割に意外と日本語のドキュメントや使用例が無いので、簡単にまとめます。estimateRigidTransformが非推奨の今、代わりにestimateAffine2Dを使うしかありません。環境次第ではestimateRigidTransformだとエラーになってしまいますし。

 アフィン行列そのものの簡単な説明は

 画像の回転、倍率変更等々の幾何変換を行うための行列です。

アフィン行列の推定関数

 アフィン行列は対応する3点があれば求められます。3点の変換元と先のペアがあればその間の変換は一意に決まります。

 その変換は

cv2.getAffineTransform(src, dst)

 で可能です。これは日本語のドキュメントがいっぱいあります。

 4点以上の対応点群がある場合には、それら変換は一意に決まらず通常「推定」が入ります。その推定には以前のOpenCV(v4.0以前。v2.3とか)では

cv2.estimateRigidTransform(src, dst, fullAffine)

 がよくつかわれていたようで、これに関しては日本語のドキュメントが多いです。ただOpenCV 4.0では非推奨扱いになっています。ちなみにRigidは「堅い」とかそんな意味のようです。なんとなくわかります。射影変換みたいに柔らかくなさそうです。

日本語(2.2)

構造解析と形状ディスクリプタ — opencv 2.2 documentation

英語(4.0)

OpenCV: Object Tracking

 ちょっと日本語の説明には少し「?」な点があり、fullAffine=falseの時の自由度が5とありますが、英語では4です。倍率を縦横同じに制限しているので4が正解のような気がします。

 この代わりに、estimateAffine2DとestimateAffinePartial2D()を使え。とあります。が、これに関しては日本語のドキュメントが少ないです。

OpenCV: Camera Calibration and 3D Reconstruction

estimateAffine2DとestimateAffinePartial2D

 2つの関数共に引数は同じです。簡単に引数を眺めると、従来のestimateRigidTransformには無い引数がいろいろあります。それらは基本的に点群の外れ値をどうやって外し、きれいな変換行列を求めるかをつかさどるパラメータになっているようです。

retval, inliers = cv.estimateAffine2D(from, to[, inliers[, method[, ransacReprojThreshold[, maxIters[, confidence[, refineIters]]]]]] )

from第一の2次元点群
to第二の2次元点群
inliersどの点が有効か外れかを指定した列(1:有効、0:外れ値) default=None
methodロバストにするための、外れ値を求める手段(RANSAC or LEMDS) default=RANSAC
ransacReprojThresholdRANSACにおけるreprojectionエラーの最大値(RANSCの時のみ有効)default=3
maxiters最大イテレーション数 default=2000
confidence確信レベル。0~1。0.95~0.99がお勧め。1にすると遅い。0.8~0.9にするとイマイチ。default=0.99
refineiterリファインする最大回数。0を設定するとリファインをOFFにし、ロバスト処理の結果が出力される。default=10
返り値
matrixアフィン行列
inlier有効点

 やっつけ気味の直訳です。Pythonでは返り値が2つで、2つ目に外れ値でない点群リストが返ってくる仕様のようです。

 基本的にはデフォルト引数(RANSAC)でよさそうですが、どうにもいかない場合にはこのパラメータを調整するとよさそうです。いじったことはないですが。

 両者の違いですが、制約の有無のようです。

 estimateAffinePartial2Dの方は回転、等方性倍率、X並進、Y並進の4つから行列を求めるようになっています。制約付きです。(等方性倍率とはX方向Y方向に同じ倍率)estimateRigidTransformの第三引数のfullAffine=falseに相当します。

 一方estimateAffine2Dは、その制約はなく、最適なアフィン行列を出してくれます。fullAffine=trueに相当します。

スポンサーリンク

サンプルコード

 使い方ですが、どちらかというと対応点群を取る方が手間です。今回はコードが長くなるので対応点群を取るコードは割愛します。

import cv2

img1 = cv2.imread("lena.jpg")
img2 = cv2.imread("lena2.jpg")
h, w = img1.shape[:2]

# ここに対応点群が入っている前提
pt1 = np.array(target_position)
pt2 = np.array(base_position)

mat, liers = cv2.estimateAffinePartial2D(pt1, pt2)
print(sum(liers), len(pt1))  # 有効点数の表示
out = cv2.warpAffine(img1, mat, (w, h))

cv2.imwrite("lena_out.jpg", out)

 こんな2枚の画像を入力し、対応点を取ります。右の画像は適当なパラメータでアフィン変換した結果なので、理想的にはびったり合うはずです。

 対応点はこんな感じ。ばっちり取れているように見えます。109点取れています。

 cv2.estimateAffinePartial2Dを使って、左の画像を右の形に変換するための行列を求め変換すると、

 合いきりません。当然です。スキュー(剪断)に相当する箇所を入れない制約付きなので。109個中56個の有効点で変換しているようです。

 cv2.estimateAffine2Dを使います。

 ばっちりです。109個中105個の値を使って変換しているようです。4個外れ値があったようです。

python画像処理
スポンサーリンク
キャンプ工学

コメント

タイトルとURLをコピーしました