Series bài viết

Mục tiêu bài viết

Bài viết sẽ giúp các bạn có thể thực hiện một số phép xử lý ảnh phổ biến với OpenCV trên C++ bắt đầu bằng việc biến đổi ảnh màu về thành ảnh xám (grayscale image) và ảnh đen trắng (black and white).

Mỗi một mục được trình bày nhằm giải đáp bốn câu hỏi chính

  • What: Cái gì? Kỹ thuật này cho ra kết quả như thế nào.
  • Why: Tại sao? Kỹ thuật này được ứng dụng trong trường hợp nào.
  • How: Như thế nào? Cách giải quyết trên mặt lý thuyết.
  • Implement: Cách cài đặt.

1. Biến đổi ảnh xám (Grayscale convert)

What ?
Đưa vào một ảnh màu, bạn sẽ thu được một ảnh xám.
img1

Why ?
Trong xử lý ảnh, việc chuyển đổi ảnh màu sang ảnh xám là công việc vô cùng phổ biến.
Ảnh màu thực chất chỉ là tập hợp của những ma trận số có cùng kích thước (xem lại phần 1). Khi muốn xử lý thông tin trên ảnh, sẽ dễ dàng hơn nếu ta chỉ xử lý dữ liệu trên một ma trận số thay vì nhiều ma trận số. Việc biến đổi ảnh màu về ảnh số (Grayscale converting) xuất hiện vì mục đích trên - biến đổi thông tin ảnh về một ma trận số hai chiều duy nhất.
img_2

How ?
Giả sử, hình ảnh của bạn được lưu trữ dưới dạng RGB (Red-Green-Blue). Điều này có nghĩa bạn có ba ma trận xám tương ứng cho màu Red, Green, Blue. Công việc của bạn là tìm cách tổng hợp ba ma trận này về thành một ma trận duy nhất. Một trong số các công thức phổ biến để thực hiện việc đó là
Y = 0.2126R + 0.7152G + 0.0722B
Trong đó:

  • Y: ma trận xám cần tìm
  • R: ma trận xám đỏ của ảnh
  • G: ma trận xám lục của ảnh
  • B: ma trận xám lam của ảnh

Nếu bạn chưa quen về việc thực hiện phép toán trên ma trận, hãy hình dung Y, R, G, B là giá trị mức xám trên các ô có tọa độ giống nhau.

Implement
- Cách 1: Đọc trực tiếp ảnh dưới dạng ảnh xám

#include<opencv2\opencv.hpp>

using namespace cv;

int main(){
	Mat img = imread("img_sample.png", CV_LOAD_IMAGE_GRAYSCALE);
	imshow("Sample", img);	
	waitKey(0);
	return 0;
}

Trong đoạn code trên, việc chuyển đổi ảnh sang dạng xám ngay từ lúc đọc ảnh vào bằng cách truyền thêm tham số CV_LOAD_IMAGE_GRAYSCALE trong hàm imread. Tham số CV_LOAD_IMAGE_GRAYSCALE thực chất là số 0 nên bạn có thực hiện lệnh imread("img_sample.png", 0) với chức năng tương tự.
Nếu muốn giữ nguyên việc đọc ảnh đầu vào là ảnh màu, bạn có thể sử dụng một trong các lệnh sau
imread("img_sample.png")
imread("img_sample.png",1)
imread("img_sample.png",CV_LOAD_IMAGE_COLOR)
với chức năng tương tự

- Cách 2: Sử dụng lệnh cvtColor

#include<opencv2\opencv.hpp>

using namespace cv;

int main(){
	Mat img = imread("img_sample.png", CV_LOAD_IMAGE_COLOR);
	Mat grayscale_img;
	cvtColor(img, grayscale_img, CV_BGR2GRAY);
	imshow("Sample", grayscale_img);
	waitKey(0);
	return 0;
}

Lệnh cvtColor(source, dest, mode) cho phép bạn chuyển đổi ảnh màu source sang ảnh xám và lưu vào dest với độ chuyển đổi màu là mode. CV_BGR2GRAY là chế độ cho phép chuyển từ ảnh màu lưu ở format BGR sang ảnh xám.

2. Biến đổi ảnh đen trắng (Black-and-white convert)

What ?
Từ ảnh xám, bạn có thể biến đổi về thành ảnh chỉ có hai màu đen-trắng (black-and-white)
img_3

Why ?
Ảnh đen trắng thường được ứng dụng trong bài toán phân vùng ảnh (Image segmentation). Giả sử trong hình ví dụ trên, bằng cách biến đổi ảnh về nhị phân, bạn có thể loại bỏ các thông khung cảnh xung quanh và chỉ giữ lại hai con cá heo trên hình.

How ?
Ảnh đen trắng, như tên gọi, chỉ có hai màu đen với giá trị là 0 và trắng với giá trị 255. Bài toán đặt ra là làm thế nào để biến đổi ảnh xám (hay nói cách khác là một ma trận 2 chiều với giá trị mỗi ô trong khoảng 0-255) về thành một ma trận 2 chiều với giá trị mỗi ô là 0 hoặc 255.
Cách giải quyết là chọn một ngưỡng (threshold) để xác định đâu là điểm ảnh đen và đâu là điểm ảnh trắng. Nếu giá trị trên ảnh xám lớn hơn ngưỡng threshold, đấy là điểm ảnh trắng và ngược lại.
Gọi ảnh xám là Gray, ảnh đen trắng cần xác định là BW, tọa độ các pixel trên hình là (x,y), ta có
- BW(x,y) = 255 nếu Gray(x,y) > threshold
- BW(x,y) = 0 nếu Gray(x,y) <= threshold

Implement
Sử dụng hàm threshold

#include<opencv2\opencv.hpp>

using namespace cv;

int main(){
	Mat img = imread("img_sample.png", CV_LOAD_IMAGE_GRAYSCALE);	
	Mat bw;
	threshold(img, bw, 150, 255, 0);
	imshow("Sample", bw);
	waitKey(0);
	return 0;
}

Lệnh threshold(src, img, thres, max_value, type) dùng để biến đổi ảnh xám src về thành ảnh đen trắng và lưu vào dst. Ngưỡng để xác định đen và trắng được truyền qua tham số thres. Ngoài ra, bạn có thể định nghĩa lại màu sáng nhất thông qua max_value (ngưỡng trắng). Hàm threshold hộ trợ một số loại biến đổi như sau:
-type = 0 Threshold Binary Biến đổi đen trắng giống định nghĩa phía trên
-type = 1 Threshold Binary, Inverted Biến đổi đen trắng ngược với định nghĩa trên
-type = 2 Truncate Nếu giá trị điểm ảnh lớn hơn thres, giá trị sẽ được đặt lại bằng thres (chặn mức sáng nhất)
-type = 3 Threshold to Zero Nếu giá trị điểm ảnh nhỏ hơn thres, điểm ảnh đó sẽ trở thành màu đen
-type = 4 Threshold to Zero, Inverted Ngược lại với type = 3

Imgur

Tùy vào ngưỡng threshold, chúng ta sẽ có kết quả hình khác nhau
Imgur

Sử dụng hàm adaptiveThreshold
Như ở ví dụ trên, chúng ta có thể thấy kết quả phụ thuộc rất nhiều vào ngưỡng threshold. Trong thực tế, đối với các chương trình xử lý ảnh real-time, việc sử dụng một threshold cố định là điều không khả thi do cường độ sáng thay đổi liên tục. Để khắc phục tình trạng này, chúng ta có thể sử dụng hàm adaptiveThreshold do OpenCV hỗ trợ.

#include<opencv2\opencv.hpp>

using namespace cv;

int main(){
	Mat img = imread("img_sample.png", CV_LOAD_IMAGE_GRAYSCALE);	
	Mat bw;	
	adaptiveThreshold(img, bw, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 11, 12);
	imshow("sample", bw);
	waitKey(0);
	return 0;
}

Hàm adaptiveThreshold(src, dst, maxValue, adaptiveMethod, thresholdType, blockSize, C) giúp biến đổi ảnh xám src về ảnh đen trắng dst với ngưỡng sáng tối đa maxValue. adaptiveThreshold hỗ trợ 2 phương pháp tự động chọn ngưỡng threshold là ADAPTIVE_THRESH_MEAN_C ADAPTIVE_THRESH_GAUSSIAN_C; cùng với 2 chế độ thresholdTypeTHRESH_BINARYTHRESH_BINARY_INV. Các tham số blockSizeC dùng để chọn số lượng điểm lân cận để tính ngưỡng threshold tốt nhất.
Imgur

Tổng kết

Bài viết vừa đề cập đến hai kĩ thuật chuyển đổi ảnh cực kỳ phổ biến trong xử lý ảnh là biến đổi ảnh xám (Grayscale convert) và biến đổi ảnh nhị phân (Black-and-white convert). Hi vọng các bạn đã hiểu rõ về các phương pháp mình vừa trình bày trên.
Hẹn gặp lại các bạn trong bài kế tiếp - các phương pháp lọc ảnh.

HMD.