
画像のアフィン変換と透視変換について学んだのでメモ
1.そもそもなぜこれを学ぶのか
OpenCVは、画像分類や異常検知においての画像の前処理にとても有用だからです。

例えば医療現場において、肺がん患者の予測をしたいとします。
そこであたらえれた画像データが肺も写っているが、肩や首の部分はあまり画像解析では関係なさそうですよね。
では次のように画像の前処理ができたらどうでしょうか

周りの画像が切り取られて肺のみにフォーカスできています。
これで画像解析すれば、肺の異常においては、よりよい分類や検知ができそうです。
OpenCVでは、画像の特徴抽出が可能です。そのため、モデル作成前にこのような処理をするのはとても大切なことなのです。
今回は、二つの変換方法について学習していきます。
1.アフィン変換とは
アフィン変換とは、画像自体を平行移動させたり、回転などを作用させる変換のことを指します。
下の行列で画像そのものを変換させることができます。
a〜dについては、画像を回転させる際に入力するもので
x,yについてはx方向にどれくらい移動させるか、y方向にどれくらい移動させるかを指定します。
実際に上の行列式と下の行列式は計算してみると結果は同じになります。
ただ行列積でまとめたって感じですね。

1.1 実際にOpenCVでアフィン変換を行なってみる
まずは必要なライブラリをインストールしていきます。
import cv2
import os # 画像のアウトプット先のディレクトリを作るためにインポートします。
from PIL import Image
import numpy as np
自分の環境はDockerのコンテナ上で操作しているため、
cv2.imshowが使えない状況です。そのため、Imageモジュールで画像を表示させます。
#読み込み
img = cv2.imread("/workspace/img_folder/dog/akita_dog.jpg")
#imreadにするとBGRになる
#PILで画像を表示させるにはRGBにしないと色彩が変わってしまう
img=cv2.cvtColor(img_afn, cv2.COLOR_BGR2RGB)
ここで軽い注意ですが、cv2.imreadで画像を読み込んでしまうと
画像がRGBではなく、BGRモードになってしまいます。
そのため、Imageで画像を表示させる際に、正しい色彩で表したければ
cv2.cvtColorで変換を行なってください。
では早速アフィン変換を行なっていきます
h , w = img.shape[:2] # 画像の高さと幅を取得
dx , dy = 30 , 30 # どれだけ並行移動させたいか
afn_mat = np.float32([[1,0,dx] , [0,1,dy]]) # 上の画像の行列部分を作成
img_afn = cv2.warpAffine(img , afn_mat , (w,h)) # 上の行列式を作成してくれる
#画像が縦30,横30に移動していることがわかる
Image.fromarray(img_afn)
実際に実行した画像がこちら

縦横で移動されていることがわかりますね。
では次は画像の回転です
一応、説明はコメントアウトの通りですが、
cv2.getRotationMatrix2Dでa~dの値を決めてくれます。
引数にはどれくらい回転させたいのか、圧縮はするのか否かを指定してあげます。
それを平行移動同様に
cv2.warpAffineに代入させます。
#画像の回転
#第一引数:どこを中心に回転させるかその軸を第一引数で決めてあげている
#第二引数:何度回転させるのか?
#第三引数:圧縮の単位(今回は圧縮を行わないので、1となる)rot_matを指定することによって、アフィン行列のa~dが自然と決まるという便利な機能
#rot_mat = cv2.getRotationMatrix2D((w/2 , h/2) , 40 , 1)
#img_afn2 = cv2.warpAffine(img , rot_mat , (w ,h))

上手く画像の中心で回転させることができました
2.透視変換について
言葉で説明すると、3次元の情報を2次元に射影してくれる性質を持っています。

例えば、上の図のように実験対象の範囲を固定させたいときに
このようにマーカーをしたとします。
ドローンなどで実験対象の位置を撮影したとすると、本来は正方形の範囲なのに平行四辺形みたいになってしまいますよね。
これを対処するために、透視変換が行われます。

このようにすると、ドローンが実験対象の真ん中から撮影したような写真になりますよね。
これに画像解析などを行えば、画像分類や異常検知などの精度が上がりそうです。
実際に自分の環境でも、透視変換を行なってみました。
par1 = np.float32([[100,500] , [300,500] ,[300,100],[100,100]])# 元の座標位置
par2 = np.float32([[100,500],[300,500],[200,200],[150,200]]) # 変換後の座標位置
psp_matrix = cv2.getPerspectiveTransform(par1, par2)
img_psp = cv2.warpPerspective(img , psp_matrix , (w ,h))

この犬の画像でははまったく実務では役に立ちませんが、上の海岸の写真だと実務で役に立つのが分かりますね。
3. まとめ
OpenCVは画像の前処理でよく用いられています。
フォーカスさせたい場所のみの画像を取ってきたり、
画像解析でとても便利なのがOpenCVです。
みなさんもぜひ使って遊んでみてください。
それでは、また