HOWTO · Python

OpenCV で画像を回転

この記事では、OpenCV の rotate()および warpAffine()関数を使用して画像を回転させる方法について説明します。

このページの内容

Python では、OpenCV を使って画像を回転する方法が 2 つあります。直角回転には cv2.rotate() を使い、それ以外の角度で回転する場合や、変換の中心と倍率を指定する場合は cv2.getRotationMatrix2D() と cv2.warpAffine() を使います。どちらの関数も画像配列を処理します。各例では小さな配列をコード内で定義するため、外部の画像ファイルは不要です。OpenCV の例を実行するには、OpenCV と NumPy をインストールしてください。

OpenCV の rotate() で 90°、180°、270°回転する

cv2.rotate() には、画像配列と 3 種類の向き定数のいずれかを渡します。時計回りに 90°回転するには cv2.ROTATE_90_CLOCKWISE、反時計回りに 90°回転するには cv2.ROTATE_90_COUNTERCLOCKWISE を使います。後者は時計回りに 270°回転した場合と同じ向きです。180°回転には cv2.ROTATE_180 を使います。90°または 270°の回転では幅と高さが入れ替わり、180°回転では変わりません。この例では小さな画像配列を 3 通りに回転し、それぞれの結果の形状を表示します。OpenCV または NumPy がインストールされていない場合は、セットアップ方法を知らせるメッセージが表示されます。

このセクションのコードと表示されるラベルや出力は英語のままです。

try:
    import cv2
    import numpy as np
except ImportError:
    print("Install opencv-python and numpy to run this example.")
else:
    image = np.zeros((2, 3, 3), dtype=np.uint8)  # 2 rows, 3 columns, 3 color channels
    rotations = {
        "clockwise": cv2.ROTATE_90_CLOCKWISE,
        "counterclockwise": cv2.ROTATE_90_COUNTERCLOCKWISE,
        "half-turn": cv2.ROTATE_180,
    }
    for name, orientation in rotations.items():
        rotated = cv2.rotate(image, orientation)
        print(f"{name}: {rotated.shape[1]}x{rotated.shape[0]}")

次のリストの例は、座標の並べ替え方を別途示すものです。実行してみてください。

image = [["A", "B", "C"], ["D", "E", "F"]]
rotated = [list(row) for row in zip(*image[::-1])]
print(f"original: {len(image[0])}x{len(image)}")
print(f"rotated: {len(rotated[0])}x{len(rotated)}")
print("rows:", rotated)
original: 3x2
rotated: 2x3
rows: [['D', 'A'], ['E', 'B'], ['F', 'C']]

3 列の元画像と、時計回りに 90°回転して 2 列 3 行になった画像

この例のセルには英語のラベルが付いています。ラベルを目視すれば、表示ウィンドウを開かなくても回転方向と縦横の変化を確認できます。OpenCV の例では寸法を幅、次に高さの順で示します。一方、NumPy の画像配列の形状は高さ、幅、チャンネル数の順です。画像ファイルを使う場合は、cv2.imread() が返す配列を cv2.rotate() に渡し、読み込みに成功したことを先に確認してください。cv2.rotate() が対応するのはこのような直角回転だけで、任意の角度は指定できません。

OpenCV の warpAffine() で任意の角度に回転する

cv2.getRotationMatrix2D(center, angle, scale) は、2 行 3 列のアフィン変換行列を返します。center は (x, y) 座標、angle は度数で指定します(OpenCV の画像座標系では正の値で反時計回りに回転します)。scale は画像の大きさに掛ける倍率です。1.0 ならサイズを維持し、1.0 未満なら縮小、1.0 より大きければ拡大します。cv2.warpAffine(image, matrix, (width, height)) は、行列を適用して出力キャンバスに画像を描画します。サイズ引数は幅、高さの順です。この例では、120×80 の画像を中心の周りに 30°回転し、元のキャンバスサイズを維持するため、角が切り取られることがあります。必要なパッケージがない場合は、セットアップ方法を知らせるメッセージが表示されます。

このセクションのコードと表示されるラベルや出力は英語のままです。

try:
    import cv2
    import numpy as np
except ImportError:
    print("Install opencv-python and numpy to run this example.")
else:
    height, width = 80, 120
    image = np.zeros((height, width, 3), dtype=np.uint8)
    center = (width / 2, height / 2)
    angle = 30
    scale = 1.0
    matrix = cv2.getRotationMatrix2D(center, angle, scale)
    rotated = cv2.warpAffine(image, matrix, (width, height))
    print(f"input canvas: {width}x{height}")
    print(f"rotated canvas: {rotated.shape[1]}x{rotated.shape[0]}")

長方形の元画像と、同じキャンバス上で 30°回転して角が切り取られた画像

この例では出力先に元の (width, height) を指定しています。そのため出力サイズは変わらず、キャンバスからはみ出した回転後の角は切り取られます。画像全体を残すには、回転後の境界に合わせて出力サイズを大きくし、変換後の中心が新しいキャンバスの中心に来るように行列の平行移動量も調整します。出力サイズだけを大きくしても、変換の基準位置は元の座標のままです。

warpAffine() は、変換先に対応する元画像の画素がない部分を境界モードで埋めます。既定の cv2.BORDER_CONSTANT は一定の色で塗りつぶし、既定色は黒です。別の色にするには borderValue を指定します。cv2.BORDER_REPLICATE は、最も近い元画像の端の画素を延長します。cv2.BORDER_TRANSPARENT は対応する出力先の画素を変更しないため、このモードを使う場合は出力先をあらかじめ初期化してください。元画像の境界を自動的に再現するモードではありません。利用できるモードについてはOpenCV の境界モードに関するリファレンスを参照してください。