画像の回転 cv2.rotate, np.rot90, cv2.getRotationMatrix2D

pythonで画像を回転させる方法を紹介します。

特に今回は
  • affine変換による回転
  • cv2.rotate()による回転
  • np.rot90()による回転
を紹介します。

開発環境はjupyter notebook、言語はpythonです。

今回使用した画像・コード一式はgoogle driveにも保存しておりますので、必要に応じてお使いください。

作成したコード

#モジュールのインポート
import cv2
import glob
import numpy as np
import matplotlib.pyplot as plt
import os
%matplotlib inline

#affine変換用def
def rotate_affine(path,angle):
    img = cv2.imread(path)
    # 変換後の画像高さを定義
    height = img.shape[0]
    # 変換後の画像幅を定義
    width = img.shape[1]
    # 回転の軸を指定:今回は中心
    center = (int(width/2), int(height/2))
    # scaleを指定
    scale = 1
    trans = cv2.getRotationMatrix2D(center, angle,scale)
    img_rotate_affine = cv2.warpAffine(img, trans, (width,height),flags=cv2.INTER_CUBIC)
    cv2.imwrite(os.path.split(path)[1][:-4]+"_rotate_affine_"+str(angle)+".bmp",img_rotate_affine)
    return

#画像の読み込み
filename = glob.glob("*.jpg")

for n in range(len(filename)):
    img = cv2.imread(filename[n])
    angle_degree = 0
    
    #1. affineによる回転
    while angle_degree <= 360:
        #print(angle_degree)
        rotate_affine(filename[n],angle_degree)
        angle_degree += 20
        
    #2. cv2.rotateによる回転
    img_cv2rotate = cv2.rotate(img,cv2.ROTATE_90_CLOCKWISE)
    
    #3. np.rotによる回転:i*90度left
    i = 5
    img_nprot = np.rot90(img,i)
affine変換によって、20°刻みの左回転を実施するようにしています。

ちなみに、トップ画のgifアニメはimageJと呼ばれる無料の画像処理ソフトで作っています。
gifアニメはpythonでも作れるので今後紹介したいと思います。

処理の解説

1. affineによる回転

用いたコードはこちらです。
def rotate_affine(path,angle):
    img = cv2.imread(path)
    # 変換後の画像高さを定義
    height = img.shape[0]
    # 変換後の画像幅を定義
    width = img.shape[1]
    # 回転の軸を指定:今回は中心
    center = (int(width/2), int(height/2))
    # scaleを指定
    scale = 1
    
    trans = cv2.getRotationMatrix2D(center, angle,scale)
    img_rotate_affine = cv2.warpAffine(img, trans, (width,height),flags=cv2.INTER_CUBIC)
    cv2.imwrite(os.path.split(path)[1][:-4]+"_rotate_affine_"+str(angle)+".bmp",img_rotate_affine)
回転の際にはcv2.getRotationMatrix2D()というコマンドを使用します。
今回例に出した3つ回転方法の中で、唯一任意の角度で回転可能なため、非常に有用性の高い手法だと思います。

基本的には回転の中心軸、回転角度(左回りが正)、拡大縮小のスケールを指示するだけです。

【補間法について】
リサイズを紹介した際にも同じものをお見せしましたが、回転の際の補間法としては次の表のものが使用できます。

コマンド概要
cv2.INTER_NEAREST最近傍補間
cv2.INTER_LINEARバイリニア補間
cv2.INTER_AREA平均画素法
cv2.INTER_CUBICバイキュービック補間
cv2.INTER_LANCZOS4ランツォシュ

この画像の補間については下図を用いて説明をしたいと思います。


 

例えば元の画像の緑色の四角を45°回転させた場合を考えます。
回転後の枠は青色の四角のようになりますが、画像として再構成する場合、元の緑の四角の領域に切り出さなければなりません。
この時に、過不足が生じた黄色の領域と橙の領域を推定するのが補間法と呼ばれる手法です。

例えば、最近傍補間であると、はみ出た領域を無視し、枠内に大きく占める領域の画素値を再構成値として採用します(今回の例だと緑四角内の白い領域)。
つまり最近傍補間は空間の補間をほとんど行わない手法であり、画像も荒れてしまうため、補間法としてはあまりお勧めできません。

上記の表のうち、下に記載したものほど高精度に補間が可能ですが、処理にも時間がかかってしまいます。
実際バイリニア程度でも十分補間法としては悪くないというのが、個人的な実感です。

【回転軸について】
回転を実行する際には、この中心軸(本プログラムではcenterとしています)の設定が重要となります。
今回はそれを例示するため、画像の左上、原点を軸としてトップ画と同じgifアニメを作成しました。


画像中心が軸の場合
画像左上が軸の場合

目が回り、めちゃくちゃ気分が悪くなるgifですが、右側のgifを見ると、画像の回転が始まった直後に、ピクセルが画像の枠外へと見切れてしまっていることが分かります。

仮に画像中心で回転させたとしても、はみ出してしまう部分は必ず存在します。
一度枠外に出た箇所のデータは取り戻せない上に、回転によって失われたデータを補間する処理を行うため、回転を繰り返し実行するのはお勧めできません。
なるべく回転の処理を行う回数が1回で済むように、プログラムを組むことがデータ品質の保持につながります。

2. cv2.rotate()による回転

用いたコードはこちらです。
img_cv2rotate = cv2.rotate(img,cv2.ROTATE_90_CLOCKWISE)
cv2.rotate()は90°刻みの回転しか実施できません。
回転の角度については、cv2.ROTATE_90_CLOCKWISEの部分を、下記の表のいずれかに書き換えることで、変更ができます。

コマンド概要
cv2.ROTATE_90_CLOCKWISE時計回りに90°
cv2.ROTATE_90_COUNTERCLOCKWISE反時計回りに90°
cv2.ROTATE_180180°回転

具体的な数値ではなく、cv2.ROTATE_90_CLOCKWISE等の書き込みが必要であるため、cv2.rotate()は不便かなと思います。

使用するなら、下記のnp.rot()の方がおすすめです。

3. np.rot()による回転

用いたコードはこちらです。
#3. np.rotによる回転:i*90度left
    i = 5
    img_nprot = np.rot90(img,i)
こちらもcv2.rotate()と同様に90°刻みの回転しかできません。

コード中のiに代入する値によって回転する角度を変更できます。
i=1の場合は左に90°、i=2の場合は左に180°と、90°ごとに増加します。
iが負の値の場合は右回転となります。

今回はi=5ということで左に90°回転をしています。

今回使用した画像:わさび菜そば/そば茶屋ちのベルビア店

茅野駅の目の前のお蕎麦屋さんです。
長野にきてすぐに寄れる近さがうれしいです。

コメント