Search…

Xử Lý Ảnh Với OpenCV: Lọc Số Trong Ảnh

15/09/202011 min read
Giới thiệu lọc số ảnh, khái niệm và công thức nhân chập ma trận, một số kỹ thuật lọc nhiễu và một số bộ lọc trong OpenCV.

Trong bài biết này giới thiệu 1 số bộ lọc ảnh số trong OpenCV.

Lọc số ảnh là gì?

  • 1 hệ thống dùng để làm biến dạng sự phân bố tần số của các thành phần tín hiệu theo các chỉ tiêu gọi đã cho được gọi là bộ lọc số.
  • Lọc số ảnh (Filter) có ý nghĩa quan trọng trong việc tạo ra các hiệu ứng trong ảnh, 1 số hiệu ứng nhờ sử dụng các bộ lọc làm mờ (Blur), làm trơn (Smooth),...

Nguyên tắc chung của các phương pháp lọc là cho ma trận ảnh nhân chập với 1 ma trận lọc (Kernel) hay còn được gọi là các phép tính nhân chập trên ảnh. Ma trận lọc lọc (Kernel) còn có thể được gọi là cửa số chập, cửa sổ lọc, mặt nạ,... trong bài viết này sử dụng thuật ngữ ma trận lọc (Kernel).

Công thức

Idst = M * Isrc
  • Isrc: ảnh gốc được sử dụng để lọc số ảnh.
  • Idst: ảnh ra sau khi thực hiện xong phép lọc số ảnh.
  • M: ma trận lọc (Mask, kernel).

Với mỗi phép lọc có những ma trận lọc (Kernel) khác nhau, không có 1 quy định cụ thể nào cho việc xác định M. Kích thước ma trận M là 1 số lẻ. Ví dụ: 3x3, 5x5. 

Tổng Tpt các phẩn tử trong ma trận M thường là 1.

  • Tpt > 1: Ảnh sau khi thực hiện xong phép lọc số ảnh (Idst) có độ sáng lớn hơn so với ảnh ban đầu (Isrc). 
  • Tpt < 1: Ảnh sau khi thực hiện xong phép lọc số ảnh (Idst) có độ sáng nhỏ hơn so với ảnh ban đầu (Isrc).

Ví dụ

ss_1

Nhân chập ảnh với phép lọc số ảnh

Để thực hiện 1 phép lọc số ảnh, cần phải tiến ảnh nhân chập ảnh đầu vào với 1 ma trận lọc hay cửa sổ nhân chập (Kernel). Toàn bộ các điểm ảnh (Pixel) trên ảnh sẽ được tiến hành nhân chập với ma trận lọc, tâm của ma trận lọc sẽ được đặt trùng vào vị trí của điểm ảnh (Pixel) đang được tính nhân chập làm thay đổi các giá trị của pixel ban đầu.

Công thức tính nhân chập

ss_2
  • n = (kích thước ma trận lọc - 1)/2.
  • Lấy tâm của ma trận lọc (Kernel) làm điểm gốc.

Ví dụ

ss_3

1 ma trận điểm ảnh I và ma trận lọc (Kernel) M:

n = (3 - 1) / 2 = 1.
Idst(2,2) = Isrc(2, 2) * M(u, v) 
= Isrc(1, 1) * M(-1, -1) + Isrc(1, 2) * M(-1, 0) + Isrc(1, 3) * M(-1, 1) +
Isrc(2, 1) * M(0, -1) + Isrc(2, 2) * M(0, 0) + Isrc(2, 3) * M(0, 1) +
Isrc(3, 1) * M(1, -1) + Isrc(3, 2) * M(1, 0) + Isrc(3, 3) * M(1, 1)
= 2*1 + 4*2 + 3*3 + 5*4 + 7*5 + 2*6 + 7*7 + 6*8 + 2*9 = 201.

1 số kỹ thuật lọc nhiễu

Lọc trung bình - Median Filter

Ý tưởng

Với lọc trung bình, mỗi điểm ảnh (Pixel) được thay thế bằng trung bình trọng số của các điểm trong vùng lân cận.

Giả sử rằng có 1 ma trận lọc (Kernel) (3x3) quét qua từng điểm ảnh của ảnh đầu vào Isrc. Tại vị trí mỗi điểm ảnh lấy giá trị của các điểm ảnh tương ứng trong vùng (3x3) của ảnh gốc đặt vào ma trận lọc (Kernel). Giá trị điểm ảnh của ảnh đầu ra Idst là giá trị trung bình của tất cả các điểm trong ảnh trong ma trận lọc (Kernel).

Thuật toán

1 ảnh đầu vào với I(x,y) là giá trị điểm ảnh tại 1 điểm (x,y) và 1 ngưỡng θ.

  • Bước 1: Tính tổng các thành phần trong ma trận lọc (Kernel).
  • Bước 2: Chia lấy trung bình của tổng các thành phần trong ma trận được tính ở trên với số lượng các phần tử của cửa sổ lọc ra 1 giá trị Itb(x, y).
  • Bước 3: Hiệu chỉnh:
    • Nếu I(x,y) - Itb(x,y) > θ thì I(x,y) = Itb(x,y).
    • Nếu I(x,y) - Itb(x,y) <= θ thì I(x,y) = I(x,y).

Chú ý

θ là 1 giá trị cho trước và có thể có hoặc không tùy thuộc vào mục đích.

Tác dụng

Trong lọc trung bình, thường ưu tiên cho các hướng để bảo vệ biên của ảnh khỏi bị mờ khi làm trơn ảnh. Các kiểu ma trận lọc (Kernel) được sử dụng tùy theo các trường hợp khác nhau. Các bộ lọc trên là bộ lọc tuyến tính theo nghĩa là điểm ảnh ở tâm cửa sổ sẽ được thay bởi tổ hợp các điểm lân cận chập với ma trận lọc (Kernel). 

Ví dụ

Ma trận lọc (Kernel) M ở nội dung phần Ví dụ: Lọc số ảnh là gì? như ở trên và phía dưới lần lượt là ma trận điểm ảnh đầu vào (Isrc) và ma trận điểm ảnh đầu ra (Idst).

ss_4
Idst(3,3) = (7+2+1+6+2+8+6+7+7) / 9 = 46 / 9 = 5. 

Chú ý

Các hệ số trong ma trận lọc (Kernel) này có tổng bằng 1, nên độ sáng ảnh giữ nguyên, và các hệ số đều dương nên nó có khuynh hướng làm nhoè ảnh.

Lọc trung vị - Mean Filter

Ý tưởng

Lọc trung vị là lọc phi tuyến. 1 phép lọc phi tuyến là 1 kết quả không thể thu được từ 1 tổng trọng số của các điểm ảnh (Pixel) lân cận.  Sau khi đã định nghĩa kích thước vùng lân cận, giá trị điểm ảnh (Pixel) trung tâm được thay bằng trung vị tức là giá trị chính giữa của tất cả các giá trị của các điểm trong vùng lân cận.

Cho 1 dãy số X1, X2, X3, ..., Xn được sắp xếp theo thứ tự tăng dần hoặc giảm dần. Khi đó Xtv được tính bởi công thức:

Nếu n lẻ:

ss_5

Nếu n chẵn:

Kích thước của ma trận lọc (Kernel) thường dùng có kích thước 3x3, 5x5, 7x7.

Thuật toán

  • Bước 1: Xác định điểm ảnh (Pixel) I(x, y).
  • Bước 2: Sắp xếp các điểm ảnh (Pixel) lân cận của theo 1 thứ tự nhất định (Tăng dần hoặc giảm dần).
  • Bước 3: Xác định Itv (Chính là Xtv trong nội dung phía trên).
  • Bước 4: Hiệu chỉnh:
    • Nếu I(x,y) - Itb(x,y) >  θ thì I(x,y) = Itb(x,y).
    • Nếu I(x,y) - Itb(x,y) <=  θ thì I(x,y) = I(x,y).

Chú ý

θ là 1 giá trị cho trước và có thể có hoặc không tùy thuộc vào mục đích.

Tác dụng

  • Loại bỏ các điểm ảnh mà vẫn đảm bảo độ phân giải.
  • Hiệu quả trong việc giảm đi điểm nhiễu trong ma trận lọc (Kernel) lớn hay bằng 1 nửa số điểm trong ma trận lọc (Kernel).

Ví dụ

Idst(1,1) = (2, 2, 2, 2, 2, 2, 4, 7, 5) = 2.
Idst(3,3) = (1, 2, 2, 6, 6, 7, 7, 7, 8) = 6.

Chú ý

Idst(1,1)Idst(1,5)Idst(5,1)Idst(5,5): có thể làm như cách trên hoặc không cần kiểm tra điểm biên.

Lọc thông thấp - Low Pass Filter

LPF - Low Pass Filter là 1 bộ lọc tuyến tính, thường được sử dụng để làm trơn nhiễu. Kỹ thuật lọc nhiễu ảnh này cần sử dụng 1 số ma trận lọc (Kernel) như sau:

ss_8

Hoặc

ss_9

Lọc thông cao - High Pass Filter

HPF - High Pass Filter là 1 bộ lọc phi tuyến tính. Thường dùng trong việc làm trơn ảnh và tìm biên đối tượng có ở trong ảnh. 1 số ma trận lọc (Kernel) dùng trong lọc thông cao:

ss_10

Chú ý

Trong lọc thông cao, các ma trận lọc (Kernel) có tổng hệ số các giá trị của bộ lọc bằng 1.

1 số bộ lọc trong OpenCV

Blur

Là 1 phép lọc làm cho trơn ảnh và khử nhiễu hạt và là 1 bộ lọc trung bình. Ma trận lọc (Kernel) của bộ lọc Blur có dạng:

Trong OpenCV để sử dụng Blur cho 1 hình ảnh, sử dụng hàm sau:

cv::blur(cv::InputArray src, cv::InputArray dst, cv::Size ksize,
          cv::Point anchor = cv::Point(-1,-1), int borderType = 4)
  • src: Là ảnh gốc.
  • dst: Là ảnh sau khi thực hiện phép lọc số ảnh.
  • ksize: Là kích thước của ma trận lọc.
  • anchor: Là Anchor Point của ma trận lọc. Giá trị mặc định là (-1,-1).
  • borderType: Là phương pháp để ước lượng và căn chỉnh các điểm ảnh nếu phép lọc chúng vượt ra khỏi giới hạn của ảnh. Giá trị mặc định là 4.

Ví dụ

cv::blur(imageSrc, imageDst, 3);
ss_12

Sobel

Kỹ thuật sử dụng 2 ma trận lọc (Kernel) xấp xỉ đạo hàm theo hướng x và y:

ss_13

Là 1 phép lọc giúp tìm đường biên cho ảnh. Trong OpenCV để sử dụng Sobel cho 1 hình ảnh, sử dụng hàm sau:

cv::Sobel(cv::InputArray src, cv::OutputArray dst, int ddepth, int dx,
          int dy, int ksize = 3, double scale = (1,0) , double delta = (0,0),
          int borderType = 4);
  • src: ảnh gốc.
  • dst: ảnh sau khi thực hiện phép lọc số ảnh.
  • ksize: kích thước của ma trận lọc. Giá trị mặc định là 3.
  • ddepth: độ sâu của ảnh sau phép lọc: VD: CV_32F, CV_64F,...
  • dx: đạo hàm theo hướng x. dx = 1 nếu đạo hàm theo hướng x.
  • dy: đạo hàm theo hướng y. dy = 1 nếu đạo hàm theo hướng y.
  • scaledelta: 2 thông số tùy chọn cho việc tính giá trị đạo hàm lưu giá trị ví sai vào ảnh sau phép lọc. Mặc định là 1 và 0.
  • borderType: phương pháp để ước lượng và căn chỉnh các điểm ảnh nếu phép lọc chúng vượt ra khỏi giới hạn của ảnh. Giá trị mặc định là 4.

Ví dụ

cv::Sobel(imageSrc, imageDst, imageSrc.depth(), 1, 0, 3);
ss_14
cv::Sobel(imageSrc, imageDst, imageSrc.depth(), 0, 1, 3);
ss_15
cv::Sobel(imageSrc, imageDst, imageSrc.depth(), 1, 1, 3);

Laplace

Là 1 phép lọc giúp tìm đường biên cục bộ cho ảnh. Tư tưởng là lấy đạo hàm bậc hai của các điểm. Ma trận lọc (Kernel) của bộ lọc Laplace có dạng:

ss_17

 

Trong thực tế dùng nhiều kiểu ma trận lọc (Kernel) khác nhau để xấp xỉ rời rạc đạo hàm bậc hai Laplace. 3 kiểu ma trận lọc (Kernel) thường dùng:

ss_18

Trong OpenCV để sử dụng Laplace cho 1 hình ảnh, sử dụng hàm sau:

cv::Laplace(cv::InputArray src, cv::OutputArray dst, int ddepth, int ksize = 1,
             double scale = (1,0), double delta = (0,0), int borderType = 4);

Chú ý

  • Có 1 phươn pháp lọc dùng để tìm đường biên cục bộ khác là Gradient, phương pháp này làm việc khá tốt khi độ sáng thay đổi rõ nét, khi mức xám thay đổi chậm hoặc miền chuyển tiếp trải rộng thì phương pháp này tỏ ra kém hiệu quả. Vậy nên sử dụng phương pháp laplace để khắc phục nhược điểm này.
  • Trong kỹ thuật lọc laplace, điểm biên được xác định bởi điểm cắt điểm không. Và điểm không là duy nhất do vậy kỹ thuật này cho đường biên rất mảnh (Rộng 1 pixel). Rất nhạy cảm với nhiễu do đạo hàm bậc 2 không ổn định.

Ví dụ

cv::Laplacian(imageSrc, imageDst, CV_64F);
ss_19

Cài đặt bộ lọc trong OpenCV

Trong nội dung ở phía trên bài viết đã giới thiệu 1 số bộ lọc có sẵn trong OpenCV. Trong OpenCV có 1 hàm tính chập, nhờ đó có thể tự cài các bộ lọc cho riêng để phù hợp với mục đích xử lý ảnh. Hàm tính chập trong OpenCV như sau:

cv::Filter2D(cv::InputArray src, cv::OutputArray dst, int ddepth
              cv::InputArray kernel, cv::Point anchor=cv::Point(-1, -1)
              double delta = (0,0), int borderType = 4);
  • src: ảnh ban đầu.
  • dst: ảnh sau khi nhân chập
  • kernel: mặt nạ nhân chập
  • anchor: điểm neo để đặt mặt nạ nhân chập.

Điều quan trọng nhất với các tự cài đặt các bộ lọc đó chính là ma trận lọc (Kernel). Có thể dễ dàng tìm được các định ma trận lọc (Kernel). Trong các nội dung ở phần trên, các bộ lọc được cài đặt sẵn trong OpenCV có giới thiệu các ma trận lọc đi kèm cho từng phép lọc.

Ví dụ

Đoạn code ví dụ cho việc tự cài đặt 1 1 lọc để xử lý hình ảnh.

#include "stadafx.h"
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highui\highui.hpp>

int main() {
    cv::Mat imageSrc = imread("stdio.vn", CV_LOAD_IMAGE_COLOR);
    cv::Mat imageDst;
   
    cv::Mat kernel = cv::Mat::ones(Size(3, 3), CV_32F) / (float)(9);

    cv::filter2D(imageSrc, imageDst, imageSrc.depth(), kernel,
                 Point(-1, -1), 0, BORDER_DEFAULT);

    imshow("Image Srd", imageSrc);
    imshow("Image Dst", imageDst);

    cv::waiKey(0);
    return(0);
}

Kết quả ví dụ ở trên tương tự như khi sử dụng bộ lọc Blur.

ss_20
IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024