Phở code xử lý ảnh

Như đã nói ở trên, một pixel mang 3 giá trị màu, vì trên thực tế mỗi pixel được cấu thành bao gồm 3 sub-pixels. Và pixel cũng không có kích thước vật lý cụ thể, mà nó phụ thuộc vào pixel density. Mỗi pixel chứa được 256 (0-255) giá trị, cho nên về mặt lý thuyết 3 sub-pixels hiện thị được 256^3 = 16777216 (~16 triệu màu). Nhưng tất nhiên là mắt người chưa hẳn đã phân biệt được 16 triệu màu này.

Xử lý hình ảnh bằng Pillow và OpenCV

Trong toàn bài này mình sẽ sử dụng ảnh này làm ảnh gốc để chỉnh sửa. Phần code có hơi mì ăn liền cho nên bạn nào khó tính vui lòng bỏ qua.

Phở code xử lý ảnh

1. Chuyển ảnh màu sang grayscale

Về cơ bản thì grayscale (hình trắng đen) là loại ảnh mà tất cả pixels chỉ mang thông tin về độ sáng, hay nói cách khác chỉ thể hiện các sắc thái của màu xám (Luminance mode). Trong không gian màu RGB, thì màu xám là màu mà các sắc tố Red, Green, Blue có giá trị bằng nhau. Ví dụ màu đỏ là

from PIL import Image
img = Image.open("rick-morty.png")

# If you want a greyscale image, simply convert it to the L (Luminance) mode:
new_img = img.convert('L')
new_img.show()

# Or save it to a file
# new_img.save('rick-morty-L.png')
0 thì nếu ta muốn chuyển nó sang xám thì lấy giá trị trung bình của 3 sub-pixels và tạo thành 1 pixel mới
from PIL import Image
img = Image.open("rick-morty.png")

# If you want a greyscale image, simply convert it to the L (Luminance) mode:
new_img = img.convert('L')
new_img.show()

# Or save it to a file
# new_img.save('rick-morty-L.png')
1

1.1 Code thủ công

Đầu tiên chúng ta áp dụng lý thuyết ở trên để sửa từng pixel cho ảnh gốc để tạo ra ảnh mới:

from PIL import Image
img = Image.open('rick-morty.png')
pixels = img.load()

new_img = Image.new(img.mode, img.size)
pixels_new = new_img.load()
for i in range(new_img.size[0]):
    for j in range(new_img.size[1]):
        r, b, g = pixels[i,j]
        avg = int(round((r + b + g) / 3))
        pixels_new[i,j] = (avg, avg, avg, 0)
new_img.show()

Chúng ta có ảnh gốc được chuyển thành grayscale như sau:

Phở code xử lý ảnh

1.2 Sử dụng Pillow

Pillow là một bản fork của PIL (Python Image Library) một thư viện xử lý hình ảnh của Python. Do PIL được phát hành từ 2009 và không có cập nhật thường xuyên, cho nên PIL đã bị thay thế bở Pillow trên các bản phân phối của Debian và Ubuntu luôn.

from PIL import Image
img = Image.open("rick-morty.png")

# If you want a greyscale image, simply convert it to the L (Luminance) mode:
new_img = img.convert('L')
new_img.show()

# Or save it to a file
# new_img.save('rick-morty-L.png')

Kết quả hơi khác so code thủ công ở trên một xíu, ví dụ cái màu áo của Morty ở trên là

from PIL import Image
img = Image.open("rick-morty.png")

# If you want a greyscale image, simply convert it to the L (Luminance) mode:
new_img = img.convert('L')
new_img.show()

# Or save it to a file
# new_img.save('rick-morty-L.png')
2 nhưng ở dưới là
from PIL import Image
img = Image.open("rick-morty.png")

# If you want a greyscale image, simply convert it to the L (Luminance) mode:
new_img = img.convert('L')
new_img.show()

# Or save it to a file
# new_img.save('rick-morty-L.png')
3

Phở code xử lý ảnh

2. Thay đổi độ tương phản (contrast)

Để thay đổi độ tương phản của pixel bằng

from PIL import Image
img = Image.open("rick-morty.png")

# If you want a greyscale image, simply convert it to the L (Luminance) mode:
new_img = img.convert('L')
new_img.show()

# Or save it to a file
# new_img.save('rick-morty-L.png')
4 khá đơn giản

from PIL import Image, ImageEnhance
# PIL accesses images in Cartesian co-ordinates, so it is Image[columns, rows]
img = Image.open("rick-morty.png")

# Enhance constrast
enhancer = ImageEnhance.Contrast(img)
for i in range(1, 8):
    factor = i / 4.0
    new_img = enhancer.enhance(factor)
    new_img.show()
    # Or save it to file
    # new_img.save('rick-morty-%s.png' % i)

Trong đó thì factor là một giá trị để kiểm soát độ tương phản, 1.0 là tương đương hình gốc, còn thấp hơn 1.0 là giảm độ tương phản vân vân và mây mây.

factor – A floating point value controlling the enhancement. Factor 1.0 always returns a copy of the original image, lower factors mean less color (brightness, contrast, etc), and higher values more. There are no restrictions on this value.

Phở code xử lý ảnh

Mình cũng không rõ Pillow thay đổi độ tương phản bằng cách nào, nhưng về lý thuyết chung thì làm tăng/giảm contrast là làm tối (darkening) nhưng pixel có giá trị dưới một mức nào đó, và làm sáng (lightening) những pixels có giá trị trên một mức nào đó. Độ chênh lệch này sẽ làm tăng hay giảm sự tương phản.

Các bạn có thể xem thêm công thức thay đổi độ tương phản của pixel ở đây, nhưng kết quả từ công thức này không ổn lắm nên mình không post kết quả ở đây.

3. Thay đổi độ sáng (brightness)

Để thay đổi độ sáng của ảnh bằng Pillow cũng rất đơn giản, ta làm như sau:

from PIL import Image, ImageEnhance
img = Image.open("rick-morty.png")
enhancer = ImageEnhance.Brightness(img)
# Lighter
new_img = enhancer.enhance(1.8)
# Darker
# new_img = enhancer.enhance(0.8)
new_img.show()

Để thay đổi độ sáng của pixels bằng cách thủ công cũng khá đơn giản, ta chỉ cần tăng hay giảm giá trị của từng pixel là được, chỉ cần lưu ý

from PIL import Image
img = Image.open("rick-morty.png")

# If you want a greyscale image, simply convert it to the L (Luminance) mode:
new_img = img.convert('L')
new_img.show()

# Or save it to a file
# new_img.save('rick-morty-L.png')
5 giá trị đó sao cho nó nằm trong khoảng 0-255 là được.

from PIL import Image

def truncate(value):
    if (value < 0):
        return 0
    if (value > 255):
        return 255
    return value

if __name__ == "__main__":
    img = Image.open('rick-morty.png')
    pixels = img.load()

    img_new = Image.new(img.mode, img.size)
    pixels_new = img_new.load()
    brightness = 20
    for i in range(img_new.size[0]):
        for j in range(img_new.size[1]):
            r, b, g = pixels[i,j]
            _r = truncate(r + brightness)
            _b = truncate(b + brightness)
            _g = truncate(g + brightness)
            pixels_new[i,j] = (_r, _b, _g, 255)
    img_new.show()

Phở code xử lý ảnh

4. Làm mờ (Gaussian blur)

Guassian blur Phương pháp làm mờ hình ảnh sử dụng Gussian function để giảm nhiễu và chi tiết trên bức ảnh.

Giải thích ngắn gọn thì cái ma trận 3x3 trong hình được gọi là

from PIL import Image
img = Image.open("rick-morty.png")

# If you want a greyscale image, simply convert it to the L (Luminance) mode:
new_img = img.convert('L')
new_img.show()

# Or save it to a file
# new_img.save('rick-morty-L.png')
6. Chúng ta áp dụng cái filter như hình cho từng pixel thì cuối cùng chúng ta sẽ có một hình đã được làm mờ. Để sẽ giải thích chi tiết ở các bài sau.

Phở code xử lý ảnh
*Nguồn Digital Image processing

# Gaussian blur
new_img = img.filter(ImageFilter.GaussianBlur(radius=50))
new_img = img.filter(ImageFilter.GaussianBlur)
new_img.show()

Phở code xử lý ảnh

5. Làm tối 4 góc (vignette) sử dụng OpenCV và numpy

import cv2
import numpy as np

img = cv2.imread('rick-morty.png')
rows, cols = img.shape[:2]

# generating vignette mask using Gaussian kernels
kernel_x = cv2.getGaussianKernel(cols, 200)
kernel_y = cv2.getGaussianKernel(rows, 200)
kernel = kernel_y * kernel_x.T
mask = 255 * kernel / np.linalg.norm(kernel)
output = np.copy(img)

# applying the mask to each channel in the input image
for i in range(3):
    output[:, :, i] = output[:, :, i] * mask

cv2.imshow('image',output)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Or write to a file
# cv2.imwrite('rick-morty-vig.png', output)

Phở code xử lý ảnh

Cơm thêm

Phần này không liên quan tới xử lý hình ảnh ở trên

Tìm các cạnh bằng phương pháp Canny (Canny edge detection)

import cv2

img = cv2.imread('rick-morty.png', 0)
edges = cv2.Canny(img, 100, 200)
cv2.imshow('image', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()

Phở code xử lý ảnh

Vẽ countour của ảnh

Contour trong image proccesing là những đường nối những điểm liền nhau tạo thành hình dáng của vật trong ảnh (không phải contour trong trang điểm). Nó là một công cụ rất hữu ích trong nhận dạng và định danh các vật thể trong hình (object detection and recognition).