Gia Bao TSC

[OpenCV] Image Denoising - Khử nhiễu hình ảnh

Trong các bài viết trước, các bạn đã có thể hình dung được OpenCV là gì và nó có thể làm được những gì. Trong bài viết này tôi sẽ giới thiệu cách sử dụng OpenCV để khử nhiễu ảnh. Đây là một chức năng thuộc về Image Processing.

 OpenCV  posted at January 20, 2019

Tư liệu bài viết có sử dụng từ OpenCV.org, tôi không giỏi trong vấn đề translation nhưng cùng ông bạn Google, sẽ cố gắng đem đến cho các bạn những nội dung không phải là "pó hand". Chúng ta sẽ nhìn lại một chút lý thuyết liên quan đến xử lý nhiễu.

Theory - Lý thuyết

Để có thể xử lý nhiễu, chúng ta cần có kiến thức liên quan đến Image Smoothing Techniques như Gaussian Blurring, Media Blurring ... Những thứ vừa nhắc đến là gì? Nghe thì có vẻ hàn lâm đấy, nhưng nôm na là để tích trữ một lượng kiến thức nho nhỏ trong việc khử nhiễu. Trong các kỹ thuật này, thì chúng ta thực hiện lấy vùng lân cận nhỏ xung quanh pixel và thực hiện một số thao tác như trung bình có trọng số gaussian, trung vị của các giá trị, v.v... để thay thế các phần tử trung tâm. Nói tóm lại, loại bỏ nhiễu tại một pixel là cục bộ đối với vùng lân cận của nó.

Chúng ta đi xem xét việc khử nhiễn dưới góc độ của toán học. Nhiễu thường được coi là một biến ngẫu nhiên với giá trị trung bình bằng không. Xem xét một pixel nhiễu, p = p0 + n, trong đó p0 là giá trị thực của pixel và n là nhiễu trong pixel đó. Bạn có thể lấy số lượng lớn các pixel giống nhau (giả sử N) từ các hình ảnh khác nhau và tính trung bình của chúng. Tốt nhất, chúng ta nên lấy p = p0 vì giá trị trung bình của nhiễu bằng không.

Chúng ta có thể tự xác minh nó bằng một thiết lập đơn giản. Giữ một camera tĩnh đến một vị trí nhất định trong vài giây. Điều này sẽ cung cấp cho chúng ta nhiều khung hình hoặc rất nhiều hình ảnh của một cảnh. Sau đó viết một đoạn mã để tìm trung bình của tất cả các khung hình trong video. So sánh kết quả cuối cùng và khung hình đầu tiên. Bạn có thể thấy giảm được nhiễu. Thật là không may khi mà phương pháp đơn giản này không mạnh đối với chuyển động của máy ảnh và cảnh. Ngoài ra thường chỉ có một ảnh nhiễu có sẵn.

Vì vậy, ý tưởng rất đơn giản, chúng ta cần một tập hợp các hình ảnh tương tự để giảm nhiễu. Hãy xem xét một cửa sổ nhỏ (giả sử kích thước 5x5) trong hình ảnh. Cơ hội lớn là những bản patch tương tự có thể ở trong bức ảnh. Đôi khi là ở những khu vực nhỏ xung quanh. Vậy điều gì về việc sử dụng các bản patch tương tự nhau và tìm trung bình của chúng? Đối với cửa sổ cụ thể, đó là điều tốt. Xem hình ảnh phía dưới:

Các patch màu xanh blue trong ảnh trong có vẻ giống nhau. Các patch màu xanh green cũng vậy. Vì vậy chúng ta lấy một pixel và lấy vùng xung quanh của pixel đó, sau đó tìm các cửa sổ tương tự bên trong bức ảnh, lấy trung bình của tất cả các cửa sổ và thay thế pixel bằng kết quả nhận được. Phương pháp này là phương pháp khử nhiễu không cục bộ (Non-Local Means Denosing). Phải mất nhiều thời gian hơn so với các kỹ thuật làm mờ mà chúng ta có thể biết nhưng kết quả lại là rất tốt.

Đối với các ảnh màu, hình ảnh được chuyển đổi sang không gian màu CIELAB và sau đó nó tách riêng các thành phần L và AB.

Image Denoising in OpenCV - Xử lý nhiễu trong OpenCV

OpenCV cung cấp 4 biến thể của công nghệ này

  1. cv.fastNIMeansDenoising() - làm việc với các ảnh grayscale đơn
  2. cv.fastNIMeansDenosingColored() - làm việc với ảnh màu
  3. cv.fastNIMeansDenoisingMulti() - làm việc với chuỗi hình ảnh được chụp trong thời gian ngắn (grayscale images)
  4. cv.fastNIMeansDenoisingColoredMulti() - như bên trên nhưng là ảnh màu

Các tham số phổ biến là:

  • h : tham số quyết định cường độ bộ lọc. Giá trị h cao hơn sẽ loại bỏ nhiễu tốt hơn, nhưng cũng sẽ loại bỏ các chi tiết hình ảnh nhiều hơn (10 là phù hợp)
  • hForColorComponents : giống như h nhưng chỉ dành cho ảnh màu (thường thiếp lập giống như h)
  • templateWindowSize : nên là số lẻ (được đề nghị là 7)
  • searchWindowSize : nên là số lẻ (được đề nghị là 21)

Trong bài viết này, tôi sẽ làm sáng về biến thể 2 và 4. Phần còn lại các bạn có thể tìm hiểu thêm. Thông tin chi tiết về các biến thể này, hãy bấm vào liên kết tương ứng.

1. cv.fastNIMeansDenoisingColored()

Như đã đề cập ở trên, nó được sử dụng để khử nhiễu cho các ảnh màu. (Nó được dự kiến là gaussian). Xem source code bên dưới (source code được viết bằng python tôi sẽ bổ sung vào bài viết)


#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main() {

	// load image
	Mat noisedImg = imread("img1.jpg");

	// denoising
	Mat denoisedImg;
	cv::fastNlMeansDenoisingColored(noisedImg, denoisedImg, 10, 10, 7, 21);

	// write the result
	imwrite("img2.jpg", denoisedImg);

	// create windows
	namedWindow("noisedImg", WINDOW_AUTOSIZE);
	namedWindow("denoisedImg", WINDOW_AUTOSIZE);

	// show images
	imshow("noisedImg", noisedImg);
	imshow("denoisedImg", denoisedImg);

	waitKey(0);
	return 0;
}

Dưới đây là một phiên bản của kết quả. Ảnh đầu vào có gaussian noise là σ=25. Xem kết quả:

Mã nguồn tham khảo: https://github.com/giabaovn/opencv/tree/master/OpenCV_ImageDenoising

2. cv.fastNIMeansDenoisingColoredMulti()

Bây giờ chúng ta sẽ áp dụng phương pháp tương tự đối với một chuỗi ảnh màu. Các tham số đầu vào được mô tả như bên dưới:

  • Tham số thứ nhất (srcImgs): là một danh sách ảnh 8-bit 3-channel. Tất cả hình ảnh nên cùng loại và có kích thước giống nhau.
  • Tham số thứ 2 (dst): hình ảnh đầu ra có cùng kích thước và loại như đầu vào (srcImgs)
  • Tham số thứ 3 (imgToDenoiseIndex): chỉ định khung nào chúng ta cần khử nhiễu, vì chúng ta sẽ truyền index trong danh sách đầu vào (srcImgs).
  • Tham số thứ 4 (temporalWindowSize): chỉ định số lượng khung hình gần đó sẽ được sử dụng để khử nhiễu. Nó nên là một số lẻ. Trong trường hợp đó, tổng số khung hình của templateWindowSize được sử dụng và trong đó khung trung tâm là khung được khử nhiễu.
  • Tham số thứ 5 (h): quyết định cường độ của bộ lọc. Giá trị h cao hơn sẽ khử nhiễu tốt hơn nhưng ngược lại cũng sẽ có nhiều tiết bị xóa bỏ hơn (khoảng 10 là OK).
  • Tham số thứ 6 (hColor): cũng giống như h nhưng áp dụng cho ảnh màu.
  • Tham số thứ 7 (templateWindowSize): kích thước (pixel) của các bản patch mẫu được sử dụng để tính trọng lượng. Nó nên là một số lẻ (được đề xuất là 7).
  • Tham số thứ 8 (searchWindowSize): kích thước (pixel) của cửa sổ được sử dụng để tính trung bình có trọng số cho số lượng pixel đã cho. Nó nên là một số lẻ. Tham số này ảnh hưởng đến hiệu suất tuyến tính: lớn hơn searchWindowSize - thời gian khử nhiễu lớn hơn (được đề xuất là 21).

Cùng xem ví dụ bên dưới:

#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/video.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <vector>

using namespace std;
using namespace cv;

int main() {

	// create vector for images
	vector<Mat> buffer(5);

	// load images
	// create 5 Mat object for showing
	Mat img1 = imread("im1.jpg", IMREAD_COLOR);
	buffer[0] = img1;

	Mat img2 = imread("im2.jpg", IMREAD_COLOR);
	buffer[1] = img2;

	Mat img3 = imread("im3.jpg", IMREAD_COLOR);
	buffer[2] = img3;

	Mat img4 = imread("im4.jpg", IMREAD_COLOR);
	buffer[3] = img4;

	Mat img5 = imread("im5.jpg", IMREAD_COLOR);
	buffer[4] = img5;

	// denoising color multi
	Mat desnoisedImg;
	int imgs_count = buffer.size();
	cv::fastNlMeansDenoisingColoredMulti(buffer, desnoisedImg, imgs_count / 2, imgs_count, 10, 10, 7, 21);

	// show all images
	imshow("noisedImg1", img1);
	imshow("noisedImg2", img2);
	imshow("noisedImg3", img3);
	imshow("noisedImg4", img4);
	imshow("noisedImg5", img5);
	imshow("desnoisedImg", desnoisedImg);

	waitKey(0);
	return 0;
}

Và đây là kết quả (đã được thu nhỏ hơn so với ảnh thực tế)

Để có được kết quả trên phải mất một lượng thời gian đáng kể khi tính toán. Hình ảnh đầu tiên (noisedImg1) là hình ảnh gốc, các hình ảnh tiếp theo (noisedImg2, noisedImg3, noisedImg4, noisedImg5) là các hình ảnh nhiễu. Và hình ảnh cuối cùng (denoisedImg) là hình ảnh đã được khử nhiễu.

Mã nguồn tham khảo: https://github.com/giabaovn/opencv/tree/master/OpenCV_ImageDenoisingVideo

Additional resources - Tài liệu bổ sung

  1. http://www.ipol.im/pub/art/2011/bcm_nlm/ (It has the details, online demo etc. Highly recommended to visit. Our test image is generated from this link)
  2. Online course at coursera (First image taken from here)

Gia Bao TSC


Bình luận