画像を歪ませる(Warp)手法Thin Plate Spline(TPS)がOpencv3.0から追加されましたが、思った以上に不具合にハマったのでメモを残します。
TPSは画像上のポイントを任意の場所に動かし、その間を連続的に補完する手法です。画像を歪んだ薄板に見立てて、三次元的に補完するイメージです。
###環境
- OpenCV 3.1
- python 2.7
- numpy
###TPSのインスタンスを生成
tps = cv2.createThinPlateSplineShapeTransformer()
まずはインスタンス生成し、これにwarpの仕方を指定していきます。
###ソースとターゲットの座標指定
sshape = np.array([[100,100],[200,100],[240,200],[300,250]],np.float32)
tshape = np.array([[100,180],[200,130],[240,220],[300,280]],np.float32)
sshape = sshape.reshape(1,-1,2)
tshape = tshape.reshape(1,-1,2)
座標は何故か三次元で指定します。(cvMatの仕様?)
(1,ポイントの数,2)のように三次元目に座標(x,y)を指定します。
また、型はnp.float32にしてください。np.float64では何故か後の座標変換関数が動作しなくなります。
###ソースとターゲットの座標を対応
matches = list()
matches.append(cv2.DMatch(0,0,0))
matches.append(cv2.DMatch(1,1,0))
matches.append(cv2.DMatch(2,2,0))
matches.append(cv2.DMatch(3,3,0))
ソースとターゲットの各座標の対応関係を指定するマッチングリストを作成します。ポイント数分のリストとなり、上記では同じインデックス同士を対応させています。
DMatchクラスの3番目の引数は不使用のため0とします。
###TPS変換の推定
tps.estimateTransformation(sshape,tshape,matches)
ソースとターゲットの対応関係からTPS変換を推定します。ここでは一旦関数の形を推定して、後から画像や座標をいろいろ変換します。
引数にソース座標、ターゲット座標、マッチングリストを指定して推定すると、任意の座標を変換できるようになります。
###任意座標の変換
ret, tshape_ = tps.applyTransformation(sshape)
任意の座標も(1,ポイントの数,2)で指定して変換します。
上記ではTPS推定に使用したソース座標を変換し、ターゲット座標になっていることを確認します。
###画像のTPS変換
さて、推定したTPSで画像を変換しますが、tpsクラスのwarpImage関数はバグのせいかソースとターゲットが逆に変換されてしまいます。
なので画像変換した場合は別途、ソースとターゲットを反対にしてTPS推定した上で用いる必要があります。
tps.estimateTransformation(tshape,sshape,matches)
img = cv2.imread('sample.jpg', 1)
out_img = tps.warpImage(img)
座標と画像の両方にTPS変換を用いたい場合は、座標用と画像用のtpsクラスインスタンスを用意しなければいけないという有様です。
あとはwarpImageの補間方法等も指定できますので、別途OpenCVリファレンスを参照してください。