Làm mịn ảnh (smoothing) là công việc quen thuộc trong xử lý ảnh. Nó giúp loại bỏ các dữ liệu không cần thiết như hạt nhiễu, làm mượt biên ảnh,…
Có nhiều phương thức khác nhau được OpenCV cung cấp sẵn. Bài này giới thiệu về nguyên lý hoạt động & mục đích ứng dụng ở mỗi trường hợp. Tùy theo từng bài toán khác nhau mà sử dụng phương thức khác nhau, thậm chí phải kết hợp nhiều loại.
Bài viết này dịch từ document của OpenCV kết hợp với kinh nghiệm cá nhân.
Khái niệm
Convolution: là các phép tính toán giữa ảnh và toán tử (kernel)
Kernel: là mảng 2 chiều (ma trận) có kích thước cố định do người dùng chỉ định
Anchor point: là điểm neo nằm bên trong kernel, thường là vị trí trung tâm kernel
Hình bên dưới là 1 kernel, với anchor point là điểm trung tâm
Các khái niệm này mình không dịch lại tiếng Việt vì không có từ tương đương, nếu có cũng không sát nghĩa với bài gốc.
Cách hoạt động
- Cho kernel lặp qua hết các pixel trên ảnh, với anchor đặt tại pixel cần tính
- Nhân giá trị của từng pixel với kernel, lấy kết quả gán lại cho pixel đó
Như vậy kết quả của ảnh sẽ phụ thuộc hoàn toàn vào giá trị của kernel. Và với các điểm ngoài biên như (0,0) thì kernel sẽ bị tràn ra ngoài, phần bị tràn sẽ không tính.
Sử dụng filter2D() để làm mịn ảnh
Hàm filter2D() sử dụng kernel do người dùng chỉ định. Ví dụ bên dưới tạo ra 1 kernel có kích thước 5×5, giá trị của mỗi phần tử là 0.1 (1/10).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('opencv_logo.png') kernel = np.ones((5,5),np.float32)/10 print(kernel) dst = cv2.filter2D(img,-1,kernel) plt.subplot(121),plt.imshow(img),plt.title('Original') plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(dst),plt.title('Averaging') plt.xticks([]), plt.yticks([]) plt.show() |
Kết quả
Sử dụng hàm blur()
Hàm này sử dụng kernel với mỗi phần tử có giá trị là 1, còn kích thước kernel do người dùng chỉ định. Hàm blur() sẽ tính giá trị trung bình của các pixel nằm trong kernel.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('opencv_logo.png') blur = cv2.blur(img,(5,5)) plt.subplot(121),plt.imshow(img),plt.title('Original') plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(blur),plt.title('blurred') plt.xticks([]), plt.yticks([]) plt.show() |
Gaussian blur
Để hiểu về công thức toán thì khá phức tạp, hiểu đơn giản là kernel này có giá trị lớn nhất bên trong và nhỏ dần khi ra ngoài biên. Giá trị giảm dần này gọi là độ lệch chuẩn, các bạn sinh viên có thể tìm trong giáo trình toán cao cấp 1,2,3 tùy theo từng trường đại học.
Kernel này phải là số dương và là số lẻ (3,5,7,…)
1 |
blur = cv2.GaussianBlur(img,(5,5),0) |
Để giá trị default kết quả tương tự như Blur
Median blur
Median là giá trị của phần tử nằm giữa trong mảng đã sắp xếp. Ví dụ:
Cho mảng {10, 11, 13, 15, 16, 23, 26} thì 15 nằm giữa, do đó giá trị median của mảng là 15
Cho mảng {10, 11, 13, 15, 16, 23, 26, 52} thì 15 và 16 nằm giữa, do đó giá trị median của mảng là (15+16/2)=15.5
1 |
median = cv2.medianBlur(img,5) |
Hàm medianBlur() lấy giá trị median của các pixel nằm dưới kernel rồi gán lại cho pixel đó. Hàm này dùng để lọc nhiễu rất tốt, bên dưới là ví dụ về đếm xe máy trên đường.
Bilateral Filtering
Đây là phương pháp làm mịn mà vẫn giữ lại được biên của ảnh. Phương pháp này phù hợp với làm mịn da mặt của chị em (anh em nào thích mịn thì làm :v ), không biết app Camera 360 có dùng không.
Nhược điểm của phương thức này là xử lý rất chậm.
1 |
blur = cv2.bilateralFilter(img,9,75,75) |
Tham khảo: https://docs.opencv.org/3.1.0/d4/d13/tutorial_py_filtering.html