Kỹ Thuật Lọc Ảnh Với OpenCV Trong C++ Cho Beginner

Kỹ Thuật Lọc Ảnh Với OpenCV Trong C++ Cho Beginner

Trong phần trước mình đã giới thiệu đến các bạn các kỹ thuật cơ bản như: điều chỉnh độ sáng, độ tương phản, phóng to, thu nhỏ và xoay ảnh. Trong phần 2 của Series "Xử lý ảnh trong C++ với OpenCV cho người mới bắt đầu" lần này, mình sẽ giới thiệu tới các bạn thêm một kỹ thuật cơ bản nữa, đó là lọc ảnh.

Các bạn cần nắm rõ các kỹ thuật cơ bản này để có thể hiểu sâu hơn về các kỹ thuật nâng cao sau này.

Lọc số trong ảnh

Lọc số trong ảnh có ý nghĩa quan trọng trong việc tạo ra các hiệu ứng ảnh, một số hiệu ứng nhờ sử dụng các bộ lọc như làm mở ảnh(Blur), làm trơn ảnh(Smooth),.... Nguyên tắc chung của phương pháp lọc ảnh là cho ảnh nhân chập với một ma trận lọc,

IsrcIdst là ảnh gốc và ảnh sau khi thực hiện phép lọc ảnh bằng cách nhân ma trận lọc với M. Ma trận M đôi khi còn gọi là mặt nạ(mask), nhân(kernel). Với mỗi phép lọc ta có những ma trận lọc M khác nhau, không có quy định cụ thể nào cho việc xác định M, tuy nhiên ma trận này có một số đặc điểm như sau:

  • Kích thước của ma trận thường là một số lẻ chẳng hạn 3x3, 5x5, .... Khi đó, tâm của ma trận sẽ nằm ở giao của hai đường chéo và là điểm áp đặt lên ảnh mà ta cần tính nhân chập.
  • Tổng các phần tử trong ma trận thông thường bằng 1. Nếu tổng này lớn hơn 1, ảnh qua phép lọc sẽ có độ sáng lớn hơn ảnh ban đầu. Ngược lại, ảnh thu được sẽ tối hơn ảnh ban đầu.

Ví dụ về ma trận lọc (ma trận lọc Sobel theo hướng x, y và ma trận Gaussian Blur)

Công thức cụ thể cho việc lọc ảnh như sau:

Trong đó, ta đang tính phép nhân chập cho điểm ảnh có tọa độ (x,y) và vì ta lấy tâm của ma trận lọc là điểm gốc nên u chạy từ -n (điểm bên trái) và v chạy từ -n (điểm phía trên) đến n, với n = (kích thước mặt nạ - 1) / 2. Để dễ hiểu hơn ta xét một ví dụ về việc làm trơn nhờ sử dung một ma trận lọc như sau:

...

Và cuối cùng ta có kết quả

Ta thấy rằng, ảnh ban đầu là ảnh có độ tương phản khá lớn (các giá trị độ lớn pixel chênh lệch lớn: 100, 200, ...), sau phép lọc ảnh có độ tương phản giảm đi hay bị làm mờ đi (lúc này độ chênh lệch giá trị giữa các pixel giảm đi: 100, 144, 167, ...). Sau đây ta sẽ xem xét một số bộ lọc trong OpenCV.

1. Lọc Blur

Ma trận lọc của phương pháp này có dạng 

Bộ lọc này có tác dụng làm trơn ảnh, khử nhiễu hạt.

Hàm cài đặt trong OpenCV có dạng:

cv::blur(const Mat& src, Mat& dst, Size ksize, Point anchor, int borderType)

trong đó, src và dst là ảnh gốc và ảnh sau phép lọc. ksize là kích thước ma trận lọc, ksize Size(rows, cols), anchor là điểm neo của ma trận lọc, nếu để mặc định là (-1, -1) thì điểm này chính là tâm của ma trận. borderType là phương pháp để ước lượng và căn chỉnh các điểm ảnh nếu qua phép lọc chúng bị vượt ra khỏi giới hạn của ảnh. Thông thường giá trị mặc định của nó là 4.

Ảnh qua phép lọc Blur

2. Lọc Sobel

Lọc Sobel chính là cách tính xấp xỉ đạo hàm bậc nhất theo hướng x, y, nó cũng chính là cách tính Gradien trong ảnh. Bộ lọc này thông thường được áp dụng cho mục đích tìm biên trong ảnh. Ma trận lọc theo các hướng x, y lần lượt như sau:

Trong OpenCV, hàm cài đặt phép lọc này như sau:

cv::Sobel(const Mat& src, Mat& dst, int depth, int xorder, int yorder, int ksize, double scale, double delta, int borderType)

Trong đó, src và dst là ảnh gốc và ảnh qua phép lọc. depth là độ sâu của ảnh sau phép lọc, có thể là CV_32F, CV_64F,.... xorder và yorder là các đạo hàm theo hướng x, y, để tính đạo hàm theo hướng nào ta đặt giá trị đó lên 1, ngược lại nếu giá trị bằng 0, hàm cài đặt sẽ bỏ qua không tính theo hướng đó. Scale và delta là hai thông số tùy chọn cho việc tính giá trị đạo hàm lựa giá trị vi sai vào ảnh sau phép lọc, chúng có giá trị mặc định là 1 và 0. borderType là tham số như trên.

Ảnh qua phép lọc Sobel:

3. Lọc Laplace

Lọc Laplace là cách tính xấp xỉ đạo hàm bậc hai trong ảnh, nó có ý nghĩa quan trọng trong việc tìm biên ảnh và phân tích, ước lượng chuyển động của vật thể.

Ma trận lọc có dạng như sau:

Trong OpenCV, bộ lọc này được cài đặt qua hàm:

cv::Laplacian(const Mat& src, Mat& dst, int depth, int ksize = 1, double scale = 1, double delta = 0, int borderType)

Các thông số này có ý nghĩa giống như các thông số trong bộ lọc Sobel, chỉ khác ở chỗ ksize là một giá trị mặc định bằng 1 và khi đó ma trận lọc Laplace trên được áp dụng.

Ảnh qua phép lọc Laplace:

Ngoài ba bộ lọc trên, OpenCV còn cài đặt khá nhiều bộ lọc khác nhau như lọc trung vị(medianBlur), lọc Gause(gaussianBlur), pyrDown, pyrUp,.... Tuy nhiên, ta hoàn toàn có thể cài đặt bộ lọc cho riêng mình thông qua hàm cv::filter2D. Nguyên mẫu hàm này như sau:

filter2D(const Mat& src, Mat& dst, int depth, const Mat& kernel, Point anchor, double delta, int borderType).

Trong đó, src dst là ảnh gốc và ảnh thu được qua phép lọc, kernel  là ma trận lọc. Thông số anchor để chỉ ra tâm của ma trận, delta điều chỉnh độ sáng của ảnh sau phép lọc(ảnh sau phép lọc được cộng với delta và borderType là kiểu xác định những pixel nằm ngoài vùng ảnh).

Hàm cv::filter2D thực chất là hàm tính toán nhân chập giữa ảnh gốc và ma trận lọc để cho ra ảnh cuối sau phép lọc. Như vậy qua trên ta thấy rằng để tiến hành việc lọc ảnh ta chỉ cần định nghĩa một ma trận lọc kernel. Có rất nhiều nghiên cứu về toán học đã định nghĩa các ma trận này, do đó việc áp dụng vào lọc ảnh sẽ là khá đơn giản.

Chương trình Demo phương pháp lọc ảnh

Chương trình sau sẽ thực hiện một số bộ lọc ảnh do chính ta định nghĩa, chẳng hạn ta có ma trận lọc:

ma trận này là ma trận sẽ làm nổi bật giống như ảnh khắc 3D, ta sẽ áp dụng ma trận này để lọc một ảnh.

#include "stdafx.h"
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>


using namespace cv;

void main(){
	Mat src = imread("HoaDao.jpg", CV_LOAD_IMAGE_COLOR);
	Mat dst;
	double m[3][3] = {-1, -1, 0,
					  -1, 0, 1,
					   0, 1, 1};
	Mat M = cv::Mat(3,3,CV_64F, m, Point (-1, -1), 128.0);
	cv::filter2D(src, dst. src.depth(), M);
	imshow("Anh goc", src);
	imshow("Anh qua bo loc", dst);
	cv::waitKey(0);
}

Kết quả của chương trình trên như sau:

Một số bộ lọc sau có thể hữu ích cho việc tạo ra các hiệu ứng đẹp mắt:

   

 

 

Tạm kết

Trên đây, mình đã giới thiệu đến các bạn kỹ thuật lọc ảnh sử dụng OpenCv trong C++. Trong các bài viết tiếp theo của Series "Xử lý ảnh với OpenCV trong C++ cho người mới bắt đầu", mình sẽ giới thiệu tới các bạn các kỹ thuật khác. Các kỹ thuật sau sẽ phức tạp hơn các kỹ thuật trước, hy vọng các bạn nắm rõ các kỹ thuật ở hai phần đầu của Series này. Các bạn cùng đón chờ các bài viết tiếp theo của mình nhé.

Cảm ơn các bạn đã quan tâm đến bài viết của mình!

<Tham khảo Internet>