つれづれなる備忘録

日々の発見をあるがままに綴る

OpenCVの使い方5 ~ 幾何変換

 OpenCVの使用方法について、今回は画像の幾何変換について紹介したい。

1. 幾何変換

 画像の幾何変換とは、画像を点群としてとらえたときに、ある変換行列によって新しい点群へ変換されることを示す。例えば並進はX,Yにそれぞれtx,tyだけ動かす行列で

 {\bf{M}} = \begin{pmatrix} 1 & 0 & t_{x} \\ 0 & 1 & t_{y} \end{pmatrix}

と表される。

2. スケール、並進、回転変換

 OpenCVを使って画像に対して幾何変換を実行する方法について示す。まず、Google Colabでは処理する画像を以下のようにロードする。srcが処理を施す画像データになっている。

import cv2
import numpy as np
from matplotlib import pyplot as plt
from google.colab import files
uploaded_file = files.upload()
orig = cv2.imread(uploaded_file_name)
src = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)

画像のスケーリングは、単純にOpenCVのリサイズを実行するcv2.resize(src,(newWidth,newHeight),interpolation)を利用する。srcはリサイズ前の画像データ、newWidth,newHeightはリサイズ後の画像の幅、高さ、interpolationはリサイズ時の画素の補間方法で拡大には cv2.INTER_CUBIC (処理が遅い) や cv2.INTER_LINEARが用いられ、縮小時にはcv2.INTER_AREA が用いられる。

height, width = src.shape[:2]
res = cv2.resize(src,(2*width, 2*height), interpolation = cv2.INTER_CUBIC)
plt.subplot(1,2,1)
plt.imshow(src)
plt.subplot(1,2,2)
plt.imshow(res)

左が元画像srcで右が2倍にリサイズした画像resになっている。

"スケール変換"
スケール変換

次に画像を幾何変換する方法について説明する。まず並進変換では、上の行列Mをnumpyを使って定義し、cv2.warpAffine(src,M,(width,height))を用いてMsrcの作用させる。なお3番目の引数は出力する画像サイズとなる。

tx=100
ty=50
M = np.float32([[1,0,tx],[0,1,ty]])
dst = cv2.warpAffine(src,M,(width,height))

行列Mにより元の画像は横方向に100ピクセル、縦方向に50ピクセル移動したことが確認できる。

"並進変換"
並進変換

回転変換も並進変換と同様に具体的な行列を定義してcv2.warpAffineを適用するが、cv2.getRotationMatrix2D((cx,cy),rot,scale)を用いることでOpenVCVでは回転と拡大縮小を同時に実行する行列を生成することができる。なお回転と拡大縮小を行う行列は以下のようになっている。

 {\bf{R}} = \begin{pmatrix} \alpha & \beta & (1-\alpha)c_{x}-\beta c_{y} \\ -\beta & \alpha & \beta c_{x}+(1-\alpha)c_{y} \end{pmatrix}

 \alpha=a\cos\theta, \beta=a\sin\theta

画像を0.6に縮小し、画像中心を軸に90°回転する行列M2getRotationMatrix2Dにより取得し、warpAffineを用いてsrcに適用する。

M2 = cv2.getRotationMatrix2D((int(width/2),int(height/2)),90,0.6)
rot = cv2.warpAffine(src,M2,(width,height))
plt.imshow(rot)

元の画像が縮小回転された画像が以下に得られる。

"回転縮小変換"
回転縮小変換

3. 一般的なアフィン変換

 ここでは一般的なアフィン変換を画像に適用する方法について紹介する。上の並進や回転縮小変換はアフィン変換の特殊な例になっている。アフィン変換の効果がわかりやすいように別の画像(方眼紙)を読み込み、画像データをsrc2とする。

uploaded_file2 = files.upload()
uploaded_file_name2 = next(iter(uploaded_file2))
orig2 = cv2.imread(uploaded_file_name2)
src2 = cv2.cvtColor(orig2, cv2.COLOR_BGR2RGB)

入力画像pts1と出力画像pts2の対応点として3組用意して、getAffineTransform(pts1,pts2)でアフィン変換の行列式M3を算出し、warpAffineを用いてsrc2に適用する。

rows,cols,ch = src2.shape
pts1 = np.float32([[50,50],[200,50],[50,200]])
pts2 = np.float32([[10,100],[200,50],[100,250]])
M3 = cv2.getAffineTransform(pts1,pts2)
aff = cv2.warpAffine(src2,M3,(cols,rows))

以下アフィン変換前後の画像を表示すると、方眼紙の並行性を保った変換であることが確認できる。

plt.figure(figsize=(10,8))
plt.subplot(1,2,1)
plt.imshow(src2)
plt.title('Input')
plt.subplot(1,2,2)
plt.imshow(aff)
plt.title('Output')

"アフィン変換"
アフィン変換

4. まとめ

 今回はOpenCVを用いた幾何変換として画像をリサイズ、並進、回転、アフィン変換する方法について紹介した。