HOWTO · Python

OpenCV 旋轉影象

本文教你使用 OpenCV 的 rotate() 和 warpAffine() 函式旋轉影象。

本頁內容

在 Python 中,OpenCV 提供兩種旋轉影像的方法。若要旋轉直角,請使用 cv2.rotate();若要旋轉其他角度,或需要控制轉換中心與縮放比例,請搭配 cv2.getRotationMatrix2D() 使用 cv2.warpAffine()。這兩個函式都會處理影像陣列。範例會直接在程式碼中建立小型陣列,因此不需要外部影像檔。若要執行 OpenCV 範例,請安裝 OpenCV 和 NumPy。

使用 OpenCV rotate() 旋轉 90°、180° 和 270°

cv2.rotate() 接受影像陣列和三種方向常數之一。順時針旋轉四分之一圈時,請使用 cv2.ROTATE_90_CLOCKWISE;逆時針旋轉四分之一圈時,請使用 cv2.ROTATE_90_COUNTERCLOCKWISE(方向等同於順時針旋轉 270°);旋轉半圈則使用 cv2.ROTATE_180。旋轉四分之一圈會交換寬度與高度;旋轉半圈則維持原尺寸。此範例會將三種方式套用至小型影像陣列,並列印各結果的形狀。若尚未安裝 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']]

三欄原始影像與順時針旋轉四分之一圈後的影像並列;旋轉後為兩欄三列

標示的儲存格讓人不必開啟顯示視窗,也能看出旋轉方向和尺寸變化;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 邊界模式參考文件,了解可用的模式。範例中的可見標示文字維持英文;程式碼和輸出區塊依原樣保留。