.. _Geometric_Transformations: 画像の幾何変換 ************************************* 目的 ======== * 並進,回転,アフィン変換といった幾何変換を学びます. * 以下の関数の使い方を学びます: **cv2.getPerspectiveTransform** 変換 ================= OpenCVは2つの変換関数 **cv2.warpAffine** と **cv2.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)方向への移動量が :math:`(t_x,t_y)` だとすると,この並進を表す変換行列 :math:`\textbf{M}` は以下のようになります: .. math:: 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は行の数です. 結果は以下のようになります: .. image:: images/translation.jpg :alt: Translation :align: center 回転 ---------- 画像を回転角 :math:`\theta` 回転させるための変換行列は以下のようになります. .. math:: M = \begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta \end{bmatrix} OpenCVが提供する回転はスケーリングも同時に行い,回転の中心位置を変更できます.この変換を表す変換行列は以下のようになります. .. math:: \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} ここで: .. math:: \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)) 結果は以下のようになります: .. image:: images/rotation.jpg :alt: Rotation of Image :align: center アフィン変換 ------------ アフィン変換は変換前後で並行性を保つ返還です.変換行列を計算するためには入力画像と出力画像の対応点の座標が少なくとも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() 結果配下のようになります.: .. image:: images/affine.jpg :alt: Affine Transformation :align: center 射影変換 --------- 射影変換は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() 結果: .. image:: images/perspective.jpg :alt: Perspective Transformation :align: center 補足資料 ===================== #. "Computer Vision: Algorithms and Applications", Richard Szeliski 課題 ===========