Posted on

使用OpenCV將圖形轉正

旋轉圖片的方法

若是單純只是要把圖片做角度的旋轉,可以直接使用OpenCV 的 cv2.rotate() 函数。可按指定的方向旋轉圖像。如下:

import cv2

# 讀取圖像
image = cv2.imread('your_image.jpg')

# 將圖像旋轉90度
rotated_image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)

# 顯示旋轉後的圖像
cv2.imshow('Rotated Image', rotated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

翻轉圖片的方法

cv2.flip() 是 OpenCV 中用於圖像翻轉的函數。它可以在水平、垂直或兩個方向上翻轉圖像。該函數接受三個參數:輸入圖像、翻轉的模式和輸出圖像的可選參數。

dst = cv2.flip(src, flipCode[, dst])

flipCode:翻轉的模式。可以是以下值之一:

  • 0:水平翻轉(沿著垂直軸翻轉)。
  • 1:垂直翻轉(沿著水平軸翻轉)。
  • -1:同時在水平和垂直方向上翻轉。

cv2.flip() 函數和 cv2.rotate() 函數都可以用於實現圖像的旋轉和翻轉,但它們的效果是不同的。

cv2.flip() 函數可以在水平和垂直方向上翻轉圖像,包括水平翻轉、垂直翻轉和同時在水平和垂直方向上翻轉。例如,使用 cv2.flip(image, -1) 可以同時在水平和垂直方向上翻轉圖像。

cv2.rotate() 函數用於對圖像進行旋轉。通過指定旋轉的角度和旋轉中心點,可以實現不同角度的旋轉。例如,使用 cv2.rotate(image, cv2.ROTATE_180_CLOCKWISE) 可以將圖像順時針旋轉180度。

雖然cv2.flip(image, -1)cv2.rotate(image, cv2.ROTATE_180_CLOCKWISE) 可以實現類似的效果,將圖像翻轉或旋轉180度,但它們的內部操作是不同的。 cv2.flip() 是基於軸對稱翻轉實現的,而 cv2.rotate() 是基於旋轉變換實現的。

針對形狀做角度校正

在許多圖像偵測的狀況,我們仍然會需要針對物件去做旋轉,首先我們一定是先用cv2.findContours取得輪廓,然後取得該物件輪廓的角度。這邊很重要的,就是要取得物件輪廓的角度,要取得角度,首先就要先去做輪廓擬合(請參考: OpenCV裡面形狀擬合的幾種方法)。

這邊我大推使用橢圓去做輪廓擬合並且取得軸心的角度,為什麼呢? 雖然cv2.minAreaRect() 可計算最小擬合矩形,但是這個矩形會非常容易受到輪廓的些微影響而改變擬合的方式,例如以下圖為例,就有可能有黑框、紅色框兩種的最小擬合矩形(會視當下輪廓取得的細微變化而改變)。也因此所取得的角度會非常多變,後續的辨識也會更困難

但是使用最小擬合橢圓,對於像上面這種左右、上下為對稱,但是長寬不同的形狀來說,非常適合使用最小擬合橢圓cv2.fitEllipse(),使用範例如下

import cv2

image = cv2.imread('./333_2023-06-08_19-57-30.jpg')
canny = cv2.Canny(image , 50, 250)
cnts, hier = cv2.findContours(canny , cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 執行最小橢圓擬合
ellipse = cv2.fitEllipse(cnts[0])
(center, axes, angle) = ellipse
cv2.ellipse(image, ellipse, (0, 255, 0), 2)
# 顯示結果
cv2.imshow('image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

上面可以看到(center, axes, angle) = ellipse這邊的angle就是所偵測到的輪廓的角度,接著可以用cv2.warpAffine方法將圖像轉正

def rotatedDice(image, cnt):
    # 取得最小擬合橢圓並對圖像做翻轉
    ellipse = cv2.fitEllipse(cnt)
    (center, axes, angle) = ellipse
    angle = angle + 90
    rotation_matrix = cv2.getRotationMatrix2D(tuple(center), angle, 1)
    image = cv2.warpAffine(image, rotation_matrix,(image.shape[1], image.shape[0]))
    # 計算裁切位置
    mark = np.zeros_like(image)
    cv2.drawContours(mark, [cnt], 0, (255, 255, 255), -1)
    mark = cv2.warpAffine(mark, rotation_matrix,(mark.shape[1], mark.shape[0]))
    mark = cv2.cvtColor(mark, cv2.COLOR_RGB2GRAY)
    cnts, hier = cv2.findContours(mark, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    x, y, w, h = cv2.boundingRect(cnts[0])
    matting_result = image[y:y+h,x:x+w,:]
    return matting_result

從上面,我們使用cv2.warpAffine來做圖片角度的校正,warpAffine裡面有要輸入一個旋轉的矩陣的參數,在上面的範例,我們使用cv2.getRotationMatrix2D,這個參數是單純做形狀旋轉,但是在真實的世界當中,大部分3D的角度轉換也會帶有著深度的轉換,如下圖

這時候就會需要使用cv2.getAffineTransform來取得這個旋轉矩陣

import cv2
import numpy as np

# 定義三個點的坐標
point1 = (106, 92)
point2 = (28, 91)
point3 = (154, 33)

# 定義旋轉角度
rotation_angle = -45

# 創建一個空白圖像
image = np.zeros((500, 500), dtype=np.uint8)

# 在圖像上繪製三角形
cv2.drawContours(image, [np.array([point1, point2, point3])], 0, (255), thickness=2)

# 計算旋轉中心
center = np.mean([point1, point2, point3], axis=0)

# 構建旋轉矩陣
rotation_matrix = cv2.getRotationMatrix2D(tuple(center), rotation_angle, 1)

# 對整個圖像進行旋轉
rotated_image = cv2.warpAffine(image, rotation_matrix, (image.shape[1], image.shape[0]))

# 顯示旋轉後的圖像
cv2.imshow("Rotated Image", rotated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()