Code phát hiện biên xử lý ảnh

Trong bài trước, mình đã giới thiệu đến các bạn kỹ thuật lọc ảnh. Hôm nay, chúng ta sẽ đến với một kỹ thuật khác, đó chính là Kỹ thuật tìm biên ảnh với bộ lọc Canny (Canny Edge Detection). 

Bộ lọc Canny là gì?

Bộ lọc Canny là sự kết hợp của nhiều bước khác nhau để tìm và tối ưu đường biên, kết quả là cho ra một đường biên khá mảnh và chính xác. Quá trình tìm biên sử dụng phương pháp Canny có thể được thực hiện qua 4 bước sau:

1. Loại bớt nhiễu trong ảnh

Người ta loại nhiễu trong ảnh, làm mờ ảnh đi bằng cách nhân chập ảnh với một bộ lọc Gauss, chẳng hạn bộ lọc Gauss 5x5 với hệ số δ = 1.4:

Code phát hiện biên xử lý ảnh

2. Tính toán giá trị Gradien trong ảnh

Vì đường biên trong ảnh là nơi phân cách giữa các đối tượng khác nhau, nên tại đó gradien của nó sẽ có biến đổi mạnh mẽ nhất. Để tính toán gradien trong ảnh, ta có thể sử dụng bộ lọc Sobel, hoặc trực tiếp nhân chập ma trận ảnh với các mặt nạ theo hướng x và y, chẳng hạn:

Code phát hiện biên xử lý ảnh

Sau đó tính độ lớn gradien trong ảnh:

Code phát hiện biên xử lý ảnh

Trong đó, Gx và Gy chính là đạo hàm theo hướng x, y của ảnh ta đang xét. Góc θ sẽ được làm tròn theo các hướng thẳng đứng, nằm ngang và theo hướng chéo. nghĩa là nó sẽ được làm tròn để nhận các giá trị trong 0, 45, 90 và 135 độ.

3. Loại bỏ các giá trị không phải cực đại

Bước này sẽ tìm ra những điểm ảnh có khả năng là biên ảnh nhất bằng cách loại đi những giá trị không phải là cực đại trong bước tìm gradien ảnh ở trên. Ta thấy rằng, với giá trị của góc θ  ở trên thì biên của đối tượng có thể tuân theo bốn hướng, và ta có bốn khả năng sau:

  • Nếu θ = 0 , khi đó điểm A sẽ được xem xét là một điêm trên biên độ nếu độ lớn gradien tại A lớn hơn gradien của các điểm tại A3, A7.
  • Nếu θ = 45, khi đó điểm A được xem là một điểm trên biên độ nếu độ lớn gradien tại A lớn hơn độ lớn gradien của các điểm tại A4, A8
  • Nếu θ = 90, khi đó điểm A sẽ được coi là một điểm nằm trên biên độ nếu độ lớn gradien tại A lớn hơn độ lớn gradien của các điểm tại A1, A5.
  • Nếu θ = 135, khi đó điểm A được xem là một điểm nằm trên biên độ nếu độ lớn gradien tại A lớn hơn độ lớn gradien của các điểm tại A2, A6

Code phát hiện biên xử lý ảnh

4. Chọn ra biên của đối tượng trong ảnh

 Sau bước trên, ta thu được tập hợp các điểm tương ứng trên đường biên khá mỏng. Vì những điểm có giá trị gradien lớn bao giờ cũng có xác suất biên thật sự hơn những điểm có gradien bé, do đó để xác định chính xác hơn nữa biên của các đối tượng, ta sử dụng các ngưỡng. Theo đó, bộ lọc Canny sẽ sử dụng một ngưỡng trên (upper threshold) và một ngưỡng dưới (lower threshold), nếu gradien tại một điểm trong ảnh có giá trị lớn hơn ngưỡng trên thì ta xác nhận điểm đó là một điểm biên trong ảnh, nếu giá trị này bé hơn ngưỡng dưới thì ta xác nhận điểm đó không phải là điểm biên. Trong trường hợp giá trị gradien nằm giữa ngưỡng trên và ngưỡng dưới thì nó chỉ được tính là điểm trên biên khi các điểm liên kế bên cạnh của nó có giá trị gradien lớn hơn ngưỡng trên.

Tìm biên ảnh bằng bộ lọc Canny trong OpenCV:

OpenCV cung cấp một hàm cho ta xác định biên trong ảnh của các đối tượng. Nguyên mẫu của hàm này như sau:

canny(Mat src, Mat dst, int lower_threshold, int upper_threshold, int kernel_size)

Trong đó,

  • src là ảnh cần phát hiện biên (là ảnh xám).
  • dst là ảnh chứa biên được phát hiện.
  • lower_threshold, upper_threshold là ngưỡng dưới và ngưỡng trên như đã nói ở trên.
  • kernel_size là kích thước của mặt nạ dùng cho bước thứ hai để tính toán gradien của các điểm trong ảnh.

Chương trình demo tìm biên trong ảnh dựa trên bộ lọc Canny:

#include "stdafx.h"
#include 
#include 
#include 

using namespace cv;

void main(){
	Mat gray = cv::imread("TuoiTho.jpg", CV_LOAD_IMAGE_GRAYSCALE);
	Mat dst1, dst2;
	imshow("Anh xam", gray);
	GaussianBlur(gray, gray, Size(9,9), 2);
	double t1 = 30, t2 = 200;
	Canny(gray, dst1, t1, t2, 3, false);
	t1 = 100; t2 = 120;
	Canny(gray, dst2, t1, t2, 3,false);
	imshow("Bien trong anh voi nguong 1", dst1);
	imshow("Bien trong anh voi nguong 2", dst2);
	waitKey(0);
}

Trong chương trình trên, trước khi tìm biên bằng phương pháp Canny ta đã làm trơn một ảnh xám bằng bộ lọc GaussianBlur (việc làm này có ý nghĩa rất lớn trong việc giảm thiểu các tiểu tiết không mong muốn trong đường biên sau này). Về nguyên tắc, bộ lọc GaussianBlur cũng là một bộ lọc số như đã giới thiệu ở bài trước về lọc số trong ảnh, cấu trúc của hàm GaussianBlur như sau:

cv::GaussianBlur(cv::InputArray src, cv::OutputArray dst, cv::Size ksize, double sigmaX, double sigmaY = 0, int borderType = 4 )

Trong đó, src và dst sẽ là các mảng dữ liệu đầu vào và kết quả. Một ảnh cũng có thể coi là một mảng dữ liệu, do đó src và dst chính là ảnh đầu và ảnh thu được sau khi biến đổi, ksize là kích thước của ma trận lọc, sigmaX, sigmaY là độ lệch chuẩn theo hướng x, y. Nếu sigmaY = 0, độ lệch chuẩn theo hướng y sẽ được lấy theo sigmaX. borderType là cách nội suy biên đối với những điểm ảnh tràn ra ngoài kích thước của ảnh.

Kết quả thu được sau các ngưỡng t1, t2 khác nhau, ta được những biên độ có độ chi tiết khác nhau như hình sau:

Code phát hiện biên xử lý ảnh

Code phát hiện biên xử lý ảnh

Tạm kết

Như vậy là mình đã giới thiệu xong đến các bạn kỹ thuật tìm biên ảnh dựa trên bộ lọc Canny. Trong các bài viết tiếp theo, mình sẽ giới thiệu đến các bạn các kỹ thuật khác và đi vào lập trình xử lý ảnh. Để có thể hiểu được những phần tiếp theo, các bạn cần nắm rõ những kĩ thuật ở các bài trước.

  • Xử lý ảnh với OpenCV trong C++ cho người mới bắt đầu: https://codelearn.io/sharing/xu-ly-anh-voi-opencv-trong-cpp
  • Kỹ thuật lọc ảnh với OpenCV trong C++ cho Beginner: https://codelearn.io/sharing/ky-thuat-loc-anh-voi-opencv-trong-cpp

Trên đây là hai bài viết trước của mình, các bạn cần nắm rõ kiến thức của bài ngày hôm nay và hai bài trước để có thể hiểu được các kỹ thuật ở các bài tiếp theo nhé. 
Cảm ơn các bạn đã quan tâm đến bài viết của mình. Các bạn cùng chờ các bài viết tiếp theo của mình nhé.