Series bài viết

Mục tiêu bài viết

Phần đầu tiên của bài viết sẽ giúp các bạn hiểu được khái niệm Lọc ảnh (Image Filtering), nguyên lý hoạt động của việc Lọc ảnh. Ở mục kế tiếp, các bạn sẽ hình dung rõ ràng hơn với ví dụ về cách làm mờ ảnh (Blurring), và cách thực hiện trên OpenCV.

1. Image Filtering

1.1 Image Filtering là gì ?

   Lọc ảnh (Filtering) là một kĩ thuật chỉnh sửa (Modifying) hoặc làm rõ (Enhancing) ảnh [1]. Việc loại bỏ những điểm lốm đốm trên một bức ảnh cũ, hay làm nét một bức ảnh bị nhòe, đấy là một số ví dụ về ứng dụng của việc lọc ảnh. Làm mịn/mờ ảnh (Smoothing/blurring), làm nét ảnh (Sharpening) hay làm sắc nét cạnh của ảnh (Edge enhancement) là một số phép xử lý ảnh phổ biến của Lọc ảnh.
(Bổ sung hình minh họa)

1.2 Nguyên lý hoạt động của Image Filtering

Filtering là phép toán thực hiện biến đổi một điểm ảnh dựa trên thông tin của các điểm ảnh (pixel) xung quanh điểm ảnh đó (neighborhood operation). Nói một cách đơn giản, khi bạn đưa một điểm ảnh vào, Filtering sẽ lấy thông tin của điểm ảnh lân cận (những con số về cường độ sáng), áp dụng công thức toán để tính ra giá trị đầu ra của điểm ảnh đấy.
Thông thường, trong xử lý ảnh, các công thức dùng để biến để giá trị thường ở dạng tuyến tính (Linear filtering) (Công thức tuyến tính là công thức có dạng a1x1 + a2x2 + ... + anxn=0). Và để thực hiện việc tính toán một cách nhanh chóng, công thức biến đổi sẽ được biểu diễn thành một ma trận nhân/lõi (kernel/mask/convolution matrix), và việc biến đổi ảnh sẽ được thực hiện bằng cách tính tích chập (Convolution) giữa ma trận nhân (Kernel).
Conv

1.3 Convolution

Trong toán học, Convolution (tích chập) là một phép toán với đầu vào là 2 hàm số (hàm f và g), với kết quả đầu ra là một hàm mới được tạo từ 2 hàm f và g. Đối với bài toán xử lý ảnh, ta có thể coi:

  • Hàm f: ảnh đầu vào
  • Hàm g: ma trận nhân kernel
  • Kết quả Convolution (f*g): ảnh sau khi biến đổi
    Công thức tính Convolution (1D) được định nghĩa như sau [2]:
    Conv_def
    Đối với bài toán xử lý ảnh, chúng ta sẽ áp dụng công thức tính Convolution 2D (sau khi đã sampling về dạng rời rạc) được định nghĩa tại [3] (chi tiết chứng minh xem tại [3]):
    Conv_2_def
    Áp dụng công thức trên vào, ta có kết quả như hình dưới
    Conv2
    Như hình trên, mục tiêu của phép convolution này là tính giá trị mới của e (tọa độ [2,2]) sau khi biến đổi. Theo công thức tính Convolution 2D, ta có:
    $$ f*g([2,2]) = f([2,2]) * g([2,2]) $$ (Chú ý: Phép * ở đấy là phép Convolution, không phải phép nhân thông thường)
    $$= f[1,1].g[4-1, 4-1] + f[1,2].g[4-1, 4-2] + f[1,3].g[4-1, 4-2] +$$
    $$f[2,1].g[4-2, 4-1]+ ... + f[3,3].g[4-3, 4-3]$$
    $$= a.9 + b.8 + c.7 + d.6 + ... + i.1$$

2. Làm mờ ảnh (Blur) với OpenCV

2.1 Cách làm mờ ảnh

Trước khi bàn về cách làm mờ ảnh, chúng ta sẽ nói về vấn đề tại sao phải làm mờ ảnh.
lela_noise
Hình bên trên là một ví dụ minh họa cho ảnh bị nhiễu. Một cách trực quan, bạn có thể định nghĩa điểm nhiễu là những điểm đốm trắng đen khiếh cho hình khó nhìn. Nếu để ý kĩ hơn, bạn có thể thấy, các điểm nhiễu này có mức sáng (intensity) chênh lệch rõ ràng so với những điểm xung quanh (điểm trắng nổi lên giữa khu vực màu đen, etc.)
Để loại bỏ những điểm nhiễu này, hay nói cách khác, loại bỏ những điểm có mức sáng chênh lệch lớn bất thường, Làm mờ ảnh (blurring) là một trong những phương pháp phổ biến nhất được áp dụng.
Vậy làm thế nào để làm mờ ảnh ? Làm thế nào để làm mờ những điểm có độ sáng bất thường trên ảnh ? Biện pháp đơn giản nhất chính là thay thế những điểm có độ sáng bất thường bằng trung bình độ sáng của những điểm lân cận.
Với tư tưởng trên, chúng ta sẽ tính tích chập của ảnh với ma trận nhân (kernel) có giá trị
blur_normalized
Khi nhân tích chập với ma trận này, giá trị đầu ra của điểm được tính sẽ bằng trung bình cộng của 8 điểm xung quanh và giá trị của chính điểm đó.

2.2 Implement với OpenCV

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('image009.png')
kernel = np.ones((3,3), np.float32)/9
blur_img = cv2.filter2D(img, -1, kernel)

f, axs = plt.subplots(1,2, figsize=(10,5))
axs[0].imshow(img)
axs[1].imshow(blur_img)
plt.show()

Đầu tiên, chương trình sẽ load hình ảnh vào bằng câu lệnh cv2.imread. Câu lệnh kernel = np.ones((3,3), np.float32)/9 tạo ra một ma trận vuông 3x3 với tất cả các phần tử có giá trị là 1/9. Hàm cv2.filter2D thực hiện việc nhân convolution giữa ảnh img và ma trận nhân kernel để cho ra kết quả là ảnh đã làm mờ blur_img. Tham số -1 trong hàm cv2.filter2D để chỉ ảnh đầu ra có cùng độ sâu màu ddepth với ảnh gốc.
uc?id=1D4AKT57Xkoo16xE6aHLHQy9SEdgHe--j&export=download

Tổng kết

Bài viết vừa đề cập một cách khái quát về nguyên lý Lọc ảnh (Filtering). Trong xử lý ảnh, việc lọc ảnh thường được biểu diễn bằng ma trận nhân Kernel. Quá trình lọc ảnh được thực hiện bằng cách tính tích chập Convolution giữa ảnh và Ma trận nhân. Bài viết cũng trình bày về cách thực hiện kĩ thuật Làm mờ ảnh (Blurring) trên OpenCV. Bài viết kế tiếp, chúng ta sẽ làm quen với một số bộ lọc ảnh khác như Làm sắc ảnh (Sharpening), xác định viền (Edge detection).

HMD.

References

[1] https://www.mathworks.com/help/images/what-is-image-filtering-in-the-spatial-domain.html
[2] https://en.wikipedia.org/wiki/Kernel_(image_processing)
[3] http://www.songho.ca/dsp/convolution/convolution.html#convolution_2d