Xử Lý Hình ảnh Bằng Python - Koodibar

  • Một chút căn bản về ảnh kỹ thuật số (digital image)
  • Xử lý hình ảnh bằng Pillow và OpenCV
    • 1. Chuyển ảnh màu sang grayscale
      • 1.1 Code thủ công
      • 1.2 Sử dụng Pillow
    • 2. Thay đổi độ tương phản (contrast)
    • 3. Thay đổi độ sáng (brightness)
    • 4. Làm mờ (Gaussian blur)
    • 5. Làm tối 4 góc (vignette) sử dụng OpenCV và numpy
  • Cơm thêm
    • Tìm các cạnh bằng phương pháp Canny (Canny edge detection)
    • Vẽ countour của ảnh
    • Nhận diện gương mặt bằng OpenCV
Một chút căn bản về ảnh kỹ thuật số (digital image)

Một hình ảnh (kỹ thuật số) được tạo thành bởi nhiều phần tử gọi là pixel (hay điểm ảnh). Thường thì một hình được biểu diễn bằng một mảng hai chiều gồm nhiều pixels. Mỗi pixel (cụ thể là hình RGB) mang trong mình 3 thông số màu: Red, Green, Blue.

"RGB Color Space"

Ngoài ra, đối với không gian RGBA thì nó có một kênh thứ 4 là Alpha, trong đó Alpha là một thông số chỉ độ trong suốt của pixel đó.

"Pixel"

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.

"Rick and Morty"

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à rgb(255, 0, 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 rgb(85, 85, 85)

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:

"Grayscale"

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à rgb(227, 227, 227) nhưng ở dưới là rgb(184, 184, 184)

"Grayscale Pillow"

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

Để thay đổi độ tương phản của pixel bằng Pillow 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.

"RGB Color Space"

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 ý truncate 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()

"Brightness"

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à kernel. 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.

"Gaussian blur" *Nguồn Digital Image processing

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

"Gaussian blur"

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)

"Gaussian blur"

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()

"Canny edges"

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).

import numpy as np import cv2 img = cv2.imread('rick-morty.png') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY) contours = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2] for contour in contours: cv2.drawContours(img, contour, -1, (0, 255, 0), 3) cv2.imshow('image', img) cv2.waitKey(0) cv2.destroyAllWindows()

"Contour"

Nhận diện gương mặt bằng OpenCV

Sử dụng Haar Cascade classifier của OpenCV. Tải về file haarcascade_frontalface_default.xml ở đây

import cv2 img = cv2.imread('queen.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faceCascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml') faces = faceCascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE ) for (x, y, w, h) in faces: cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.imwrite('queen-fd.jpg', img)

"Contour"

Từ khóa » Thư Viện Xử Lý ảnh Trong Python