Computer Vision

Image Transformations

1 TRANSLATION

Dịch chuyển ảnh theo trục x và y. Vì ảnh trong OpenCV được biểu diễn ở dạng ma trận, nên ta cần dùng translation matrix M để biến đổi ma trận:

translation_matrix

Trong đó:

  • t_x – số pixel dịch theo trục x.
  • t_y – số pixel dịch theo trục y.

Để thực hiện phép dịch chuyển, ta có thể sử dụng function:

translatedImage = cv2.warpAffine(image, translationMatrix, (width, height))

Example 1: Dịch ảnh sang phải 50 pixel và xuống dưới 100 pixel.

import numpy as np
import cv2

image = cv2.imread("image.jpg")
# tạo translation matrix M
M = np.float32([[1, 0, 50], [0, 1, 100]])
# thực hiện dịch chuyển
translated = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
# display result
cv2.imshow("Original", image)
cv2.imshow("Translated", translated)
cv2.waitKey(0)

Output:

translate
Hình 1 – Kết quả phép dịch chuyển

2 SIMPLE ROTATION

Xoay ảnh một góc alpha (degree). Để thực hiện phép xoay, chúng ta cần chọn tâm xoay, đa số trường hợp tâm xoay sẽ là center of image, tất nhiên chúng ta có thể chọn tâm xoay là bất cứ điểm nào. Góc xoay dương sẽ ngược theo chiều kim đồng hồ.

Tương tự như translation, chúng ta cần một rotation matrix để biến đổi.

opencv_rotate_matrix

Trong đó:

  • \alpha=scale \times \cos(\theta);
  • \beta=scale \times \sin(\theta);
  • c_x, c_y – tọa độ tâm xoay.

Ta có thể sử dụng function cv2.getRotationMatrix2D để tạo một rotation matrix:

rotationMatrix = cv2.getRotationMatrix2D((x_center, y_center), angle, scale)

Trong đó:

  • (x_center, y_center) – tọa độ của tâm xoay;
  • angle – góc xoay ở degree, chiều xoay dương là ngược chiều kim đồng hồ;
  • scale – tỉ lệ của bức ảnh.

Sau đó dùng function cv2.warpAffine(image, rotationMatrix, (widht, height)) để thực hiện phép xoay.
Example 2 – Xoay ảnh một góc 30 độ.

import numpy as np
import cv2

image = cv2.imread("image.jpg")
(h, w) = image.shape[:2]
center = (w//2, h//2)

# tạo rotation matrix
M = cv2.getRotationMatrix2D(center, 30, 1.0)

# thực hiện phép xoay
rotated= cv2.warpAffine(image, M, (w, h))

# display result
cv2.imshow("Original", image)
cv2.imshow("Rotated", rotated)
cv2.waitKey(0)

Output:

rotation
Hình 2 – Kết quả phép xoay đơn giản

3 BETTER ROTATION

Ở phần Simple Rotation, bức ảnh sau khi xoay đã bị mất một số phần (xem hình 2) bởi vì phần bounding đã không được thay đổi cho phù hợp với bức ảnh mới. Để khắc phục điều này chúng ta sẽ cần thay đổi một chút ở Rotation Matrix.

Capture
Hình 3 – Quá trình xoay một bức ảnh

Trong hình 3, ở bên trái là bức ảnh ban đầu, bên phải là bức ảnh đã được xoay một góc theta. Có thể thấy rằng kích thước khung hình của bức ảnh đã thay đổi, từ width x height thành newwidth x newheight. Để không bị mất các phần hình ảnh sau khi xoay, chúng ta cần tính toán các giá trị newwidth, newheight và thực hiện biến đổi tịnh tiến trong Rotation Matrix.

Công thức tính rất đơn giản:

newwidth = width \times \cos(\theta) + height \times \sin(\theta)

newheight = width \times \sin(\theta) + height \times \cos(\theta)

Example 3: Better Rotation

import cv2
import numpy as np

image = cv2.imread("image.jpg")
(h, w) = image.shape[:2]
center = (w//2, h//2)

# tạo rotation matrix
M = cv2.getRotationMatrix2D(center, 30, 1.0)
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])

# tính toán giá trị newWidth, newHeight
newWidth = int(w * cos + h * sin)
newHeight = int(w * sin + h * cos)

# phép tịnh tiến trong Rotation Matrix
M[0, 2] += (newWidth / 2) - center[0]
M[1, 2] += (newHeight / 2) - center[1]

# thực hiện phép xoay
rotated = cv2.warpAffine(image, M, (newWidth, newHeight))

# display result
cv2.imshow("Original", image)
cv2.imshow("Rotated", rotated)
cv2.waitKey(0)

Output:

Capture2
Hình 4 – Kết quả phép xoay

4 RESIZING

Khi thực hiện resizing, chúng ta cần biết rõ tỉ lệ hình ảnh (the aspect ratio of the image). Tỉ lệ hình ảnh chính là tỉ lệ giữa width và height của image. Nếu không biết về aspect ratio, hình ảnh sau khi resizing sẽ không chính xác. Ví dụ: giả sử ban đầu image có kích thước 640×480 (aspect ratio là 4:3), nếu chúng ta muốn resize width xuống 400, thì height cũng phải giảm còn 300 để đảm bảo ratio là 4:3.

Chúng ta sử dụng function:

resizedImage = cv2.resize(image, (new width, new height), interpolation=cv2.INTER_AREA)

Example 4 – Resize image

import numpy as np
import cv2

image = cv2.imread("image.jpg")
cv2.imshow("Original", image)

r = 150.0 / image.shape[1]
dim = (150, int(image.shape[0]*r))

resized = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
cv2.imshow("Resized (Width)", resized)

r = 50.0 / image.shape[0]
dim = (int(image.shape[1]*r), 50)

resized = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
cv2.imshow("Resized (Height)", resized)
cv2.waitKey(0)

Output:

resize
Hình 5 – Kết quả phép scaling

5 FLIPPING

Chúng ta có thể flip image theo trục x hoặc y hoặc cả hai. Để flipping ta sử dụng function cv2.flip(image, flipcode). Trong đó, flipcode = 1 – lật quanh trục y; flipcode = 0 – lật quanh trục x, flipcode = -1 – lật quanh cả 2 trục x và y.

Example 5 – Flip image

image = cv2.imread("image.jpg")
cv2.imshow("Original", image)

flipped = cv2.flip(image, 1)
cv2.imshow("Flipped Horizontally", flipped)

flipped = cv2.flip(image, 0)
cv2.imshow("Flipped Vertically", flipped)

flipped = cv2.flip(image, -1)
cv2.imshow("Flipped Horizontally & Vertically", flipped)
cv2.waitKey(0)

Output:

flip
Hình 6 – Flipping

6 CROPPING

Khi thực hiện cropping, chúng ta sẽ giữa lại phần hình ảnh cần quan tâm, các phần hình ảnh khác sẽ bị loại bỏ. Vì bức ảnh được biểu diễn ở dạng Numpy array nên chúng ta có thể dễ dàng thực hiện cropping bằng slicing. Đọc thêm bài viết này để tránh các lỗi cơ bản.

Example 6 – Image cropping

image = cv2.imread("image.jpg")
cv2.imshow("Original", image)

cropped = image[50:150, 120:350]
cv2.imshow("T-Rex Face", cropped)
cv2.waitKey(0)

Output:

Capture
Hình 7 – Kết quả của cropping

7 PERSPECTIVE TRANSFORMATION / HOMOGRAPHY

Một số khái niệm cần chú ý:

  • Homography hoặc Perspective Transformation (Phép chuyển đổi phối cảnh): trong Computer Vision, là các thuật ngữ được dùng để chỉ sự ánh xạ giữa các điểm trên hai mặt phẳng ảnh mà tương ứng đến cùng vị trí trên một đối tượng phẳng trong thế giới thực.
  • Perspective Projection: là phép chiếu phối cảnh từ không gian 3-D về mặt phẳng ảnh 2-D.

OpenCV cung cấp cho chúng ta function cv2.getPerspectiveTransform(), cv2.findHomography() để tạo ma trận chuyển đổi và function cv2.warpPerspective() để thực hiện phép biến đổi ma trận.

Hai function cv2.getPerspectiveTransform() và  cv2.findHomography() đều có chức năng tính toán ma trận chuyển đổi. Tuy nhiên, cv2.getPerspectiveTransform() chỉ nhận đúng 4 cặp điểm nên phù hợp khi chúng ta chắn chắn rằng 4 cặp điểm này là tương ứng nhau (lựa chọn thủ công); trong khi đó cv2.findHomography() nhận nhiều cặp điểm (càng nhiều càng tốt) nên phù hợp khi các cặp điểm được lựa chọn tự động (bởi các thuật toán Feature Matching).

Function:

+ Tính toán ma trận chuyển đổi với 4 cặp điểm tương ứng:

transformMatrix = cv2.getPerspectiveTransform(srcPoints, destPoints)

Trong đó:

  • srcPoints – một float NumPy array, chứa tọa độ 4 điểm trong bức ảnh gốc;
  • dstPoints – một float NumPy array, chứa tọa độ 4 điểm tương ứng khi đã qua chuyển đổi;
  • transformMatrix – ma trận chuyển đổi nhận được từ function.

+ Tính toán ma trận chuyển đổi với N cặp điểm tương ứng:

transformMatrix, status = cv2.findHomography(srcPoints, destPoints)

Trong đó:

  • srcPoints – một float ndarray, chứa tọa độ N điểm trong bức ảnh gốc;
  • dstPoints – một float ndarray, chứa tọa độ N điểm tương ứng khi đã qua chuyển đổi;
  • transformMatrix – ma trận chuyển đổi nhận được từ function;
  • status – một ndarray N x 1 chứa các giá trị 0 và 1, trong đó: N – số lượng điểm có trong srcPoints.

+ Thực hiện chuyển đổi bức ảnh input với transform matrix:

transformedImage = cv2.warpPerspective(inputImage, transformMatrix, (outputWidth, outputHeight))

Trong đó:

  • inputImage – bức ảnh cần được chuyển đổi;
  • transformMatrix – ma trận chuyển đổi nhận được từ function cv2.getPerspectiveTransform();
  • (outputWidth, outputHeight) – một list chứa kích thước của output image;
  • transformedImage – output image, bức ảnh đã qua chuyển đổi.

Để hiểu rõ hơn về các function này, hãy xem ví dụ dưới đây.

Example 7 – Perspective projection

import numpy as np
import cv2
import argparse
 
def four_point_transform(image, pts):
    (tl, tr, br, bl) = pts
 
    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
    maxWidth = max(int(widthA), int(widthB))
 
    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA), int(heightB))
 
    dst = np.array([[0,0],
                    [maxWidth-1,0],
                    [maxWidth-1,maxHeight-1],
                    [0,maxHeight-1]], dtype="float32")
 
    M = cv2.getPerspectiveTransform(pts, dst)
    # or use this function
    #M = cv2.findHomography(pts, dst)[0]
 
    print("angle of rotation: {}".format(np.arctan2(-M[1,0],M[0,0])*180/np.pi))
 
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
 
    return warped
 
def selectROI(event, x, y, flags, param):
    global imagetmp, roiPts
 
    if event == cv2.EVENT_LBUTTONDOWN and len(roiPts) < 4:
        roiPts.append((x,y))
        print(x,y)
        cv2.circle(imagetmp, (x,y), 2, (0,255,0), -1)
        if len(roiPts) > 1:
            cv2.line(imagetmp, roiPts[-2], (x,y), (0,255,0), 2)
        if len(roiPts) == 4:
            cv2.line(imagetmp, roiPts[0], (x,y), (0,255,0), 2)
 
        cv2.imshow("image", imagetmp)
        print("select ROI")
 
def main():
    global imagetmp, roiPts
    roiPts = []
    
    ap = argparse.ArgumentParser()
    ap.add_argument("-i", "--image")
    args = vars(ap.parse_args())
    image = cv2.imread(args["image"])
    imagetmp = image.copy()
    cv2.namedWindow("image")
    cv2.setMouseCallback("image", selectROI)
 
    while len(roiPts) < 4:
        cv2.imshow("image", imagetmp)
        cv2.waitKey(500)
 
    roiPts = np.array(roiPts, dtype=np.float32)

    warped = four_point_transform(image, roiPts)
 
    cv2.imshow("Warped", warped)
    cv2.waitKey(0)
 
if __name__ == '__main__':
    
    main()

Execution:
$ python thisfile.py -i yourImage

Khi bức ảnh gốc đã được hiển thị, lần lượt click vào 4 điểm top-left, top-right, bottom-right và bottom-left để tạo thành một hình chữ nhật bao quanh vùng ảnh cần chuyển đổi trên bức ảnh gốc và xem kết quả.

Output:

Capture
Hình 8 – Kết quả phép Perspective Projection

Bức ảnh bên trái chính là ảnh gốc sau khi được đánh dấu vùng cần chuyển đổi. Bức ảnh nhỏ hơn bên phải là bức ảnh output đã qua chuyển đổi.


SUMMARY

Như vậy chúng ta đã tìm hiểu qua một số phép biến đổi hình ảnh đơn giản. Cảm ơn các bạn đã theo dõi bài viết.

Thân ái và quyết thắng.

Referecnce:
[1] Geometric Image Transformations.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s