画像の幾何変換

目的

  • 並進,回転,アフィン変換といった幾何変換を学びます.
  • 以下の関数の使い方を学びます: cv2.getPerspectiveTransform

変換

OpenCVは2つの変換関数 cv2.warpAffinecv2.warpPerspective を提供しています. cv2.warpAffine は2x3の変換行列を入力するのに対して cv2.warpPerspective は3x3の変換行列を入力とします.

スケーリング(拡大・縮小)

スケーリングは画像のサイズ変更のみを行います.この変換のために cv2.resize() 関数が用意されています.変更後の画像サイズを指定する必要があります.補間方法は複数ありますが,縮小には cv2.INTER_AREA ,拡大には cv2.INTER_CUBIC (処理が遅い) や cv2.INTER_LINEAR が適しています.デフォルトの補間方法は cv2.INTER_LINEAR です.以下のどれかの方法を使ってスケーリングができます.:

import cv2
import numpy as np

img = cv2.imread('messi5.jpg')

res = cv2.resize(img,None,fx=2, fy=2, interpolation = cv2.INTER_CUBIC)

#OR

height, width = img.shape[:2]
res = cv2.resize(img,(2*width, 2*height), interpolation = cv2.INTER_CUBIC)

並進

並進は物体の位置を移動させる処理です.(x,y)方向への移動量が (t_x,t_y) だとすると,この並進を表す変換行列 \textbf{M} は以下のようになります:

M = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y  \end{bmatrix}

データ型が np.float32 のNumpyの配列に値を設定し, cv2.warpAffine() 関数に与えます.以下の例では移動量を (100,50) としています:

import cv2
import numpy as np

img = cv2.imread('messi5.jpg',0)
rows,cols = img.shape

M = np.float32([[1,0,100],[0,1,50]])
dst = cv2.warpAffine(img,M,(cols,rows))

cv2.imshow('img',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

Warning

cv2.warpAffine() 関数の第3引数には出力画像のサイズを (width, height) という形式で指定しなければいけません.widthは列の数,heightは行の数です.

結果は以下のようになります:

Translation

回転

画像を回転角 \theta 回転させるための変換行列は以下のようになります.

M = \begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta   \end{bmatrix}

OpenCVが提供する回転はスケーリングも同時に行い,回転の中心位置を変更できます.この変換を表す変換行列は以下のようになります.

\begin{bmatrix} \alpha &  \beta & (1- \alpha )  \cdot center.x -  \beta \cdot center.y \\ - \beta &  \alpha &  \beta \cdot center.x + (1- \alpha )  \cdot center.y \end{bmatrix}

ここで:

\begin{array}{l} \alpha =  scale \cdot \cos \theta , \\ \beta =  scale \cdot \sin \theta \end{array}

この変換行列を計算するための cv2.getRotationMatrix2D という関数があります.以下の例はスケーリングをせずに画像中心に対して90度回転する変換を試しています.

img = cv2.imread('messi5.jpg',0)
rows,cols = img.shape

M = cv2.getRotationMatrix2D((cols/2,rows/2),90,1)
dst = cv2.warpAffine(img,M,(cols,rows))

結果は以下のようになります:

Rotation of Image

アフィン変換

アフィン変換は変換前後で並行性を保つ返還です.変換行列を計算するためには入力画像と出力画像の対応点の座標が少なくとも3組必要です. cv2.getAffineTransform 関数を使い2x3の変換行列を作成し, cv2.warpAffine 関数で画像を変換します.

以下の例は3組の対応点(入力,出力画像上で緑色の円で表示)を指定したアフィン変換の例です:

img = cv2.imread('drawing.png')
rows,cols,ch = img.shape

pts1 = np.float32([[50,50],[200,50],[50,200]])
pts2 = np.float32([[10,100],[200,50],[100,250]])

M = cv2.getAffineTransform(pts1,pts2)

dst = cv2.warpAffine(img,M,(cols,rows))

plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()

結果配下のようになります.:

Affine Transformation

射影変換

射影変換は3x3の変換行列が必要です.変換の前後で直線性が保たれます.変換行列を計算するためには少なくとも4組の対応点の座標が必要になります.これら4点の内どの3点をとっても同一直線上に載らないような4点を選ぶ必要が有ります. cv2.getPerspectiveTransform 関数を使って変換行列を計算し, cv2.warpPerspective 関数を使って画像を変形します.

以下にサンプルコードを載せます.:

img = cv2.imread('sudokusmall.png')
rows,cols,ch = img.shape

pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])

M = cv2.getPerspectiveTransform(pts1,pts2)

dst = cv2.warpPerspective(img,M,(300,300))

plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()

結果:

Perspective Transformation

補足資料

  1. “Computer Vision: Algorithms and Applications”, Richard Szeliski

課題