Giới thiệu
OpenCV (Open Computer Vision) là 1 thư viện mã nguồn mở chuyên dùng để xử lý các vấn đề liên quan đến thị giác máy tính. Nhờ 1 hệ thống các giải thuật chuyên biệt, tối ưu cho việc xử lý thị giác máy tính, vì vậy tính ứng dụng của OpenCV là rất lớn. Xử lý ảnh là quá trình xử lý, thao tác hình ảnh để có 1 hình ảnh khác phù hợp với nhu cầu của người dùng, ...
Bài viết giới thiệu khái niệm và kỹ thuật ảnh xám (Grayscale) và ảnh nhị phân và nhị phân hóa (Adaptive Threshold).
GrayScale là gì?
- Là 1 hệ thống màu có mô hình màu đơn giản nhất với 256 cấp độ xám biến thiên từ màu đen đến màu trắng.
- Kết quả được xuất ra sẽ có màu trắng đen.
- Được sử dụng cả trong công nghiệp in lẫn dùng trong việc thể hiện ảnh lên các thiết bị số.
- Ảnh xám (Gray image) hay còn gọi là ảnh đơn sắc (Monochromatic), mỗi giá trị điểm ảnh (Pixel) trong ma trận điểm ảnh mang giá trị từ
0
đến255
. - Trong không gian màu RGB, để có 1 ảnh xám cần có phải có giá trị kênh màu
Red(x, y) = Green(x, y) = Blue(x, y)
(với x, y lần lượt là tọa độ của điểm ảnh).
Chuyển đổi hệ thống màu RGB sang Grayscale
Ảnh là tập hợp của 1 ma trận điểm ảnh (pixel), mỗi điểm ảnh có thể được biểu diễn bằng n bytes dưới các kênh màu khác nhau. Việc chuyển đổi giữa các hệ màu thông thường được thực hiện thông qua các phép biến đổi ma trận.
Bài viết sẽ giới thiệu cách chuyển đổi từ ảnh 24 bits RGB sang ảnh 8 bits Grayscale.
Công thức
Công thức tính cường độ sáng tại 1 điểm ảnh từ ảnh RGB:
I(x, y) = 0.3086 * Red(x, y) + 0.6094 * Green(x, y) + 0.0820 * Blue(x, y) I(x, y) = 0.299 * Red(x, y) + 0.587 * Green(x, y) + 0.114 * Blue(x, y)
Hoặc
I(x, y) = ( 2 * Red(x, y) + 5 * Green(x, y) + 1 * Blue(x, y) ) / 8
Phân tích
I(x, y):
cường độ sáng tại điểm ảnh (x, y) của ảnh xám.Red(x, y)
: giá trị của kênh màu Red (Đỏ) tại điểm ảnh (x, y) của ảnh màu (RGB).Green(x, y
): giá trị của kênh màu Green (Xanh lá cây) tại điểm ảnh (x, y) của ảnh màu (RGB).Blue(x, y)
: giá trị của kênh màu Blue (Xanh lơ) tại điểm ảnh (x, y) của ảnh màu (RGB).
Chú ý
- Các phép toán trong số nguyên (Int) nhanh hơn rất nhiều trong số thực (Float).
- Trong OpenCV, hệ thống màu có thứ tự các kênh màu là Blue-Green-Red.
- Các thông số dùng để tính toán cường độ sáng cho ảnh xám như:
0.3086
,0.6094
,0.0820
,... được coi là những con số đẹp do người ta nghiên cứu ra. Các con số này có thể thay đổi. Có thể chọn 1 giá trị 1 kênh màu hoặc chia trung bình cộng của 3 kênh màu để tìm cường độ sáng tại 1 điểm ảnh (Pixel).
Chuyển đổi ảnh xám trong OpenCV
Phương thức cvtColor
Trong OpenCV, để chuyển 1 tấm ảnh có hệ màu RGB sang Grayscale, hay thậm chí là các không gian màu qua lại với nhau nhờ phương thức cvtColor()
(Convert color).
cv::cvtColor(cv::InputArray src, cv::OutputArray dst, int code)
Phân tích
src
: Là hình ảnh gốc (Trong bài viết này là ảnh màu).dst
: Là ảnh thu được (Trong bài viết này là ảnh xám).code
: Là mã chuyển màu. Ví dụ: code =CV_BGR2GRAY
là chuyển đổi ảnh màu thành ảnh xám,...
Code minh hoạ phương thức cvtColor trong OpenCV
// www.stdio.vn // www.stdio.vn/users/index/11/truong-dat #include <stdio.h> #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" using namespace cv; int main() { // Read image Mat image = imread("stdio.png", CV_LOAD_IMAGE_COLOR); Mat imageGrayscale; // Check for valid if (!image.data) { printf("Could not open or find the image\n"); return -1; } cvtColor(image, imageGrayscale, CV_BGR2GRAY); // Create and show image in window imshow("STDIO OpenCV Sample", imageGrayscale); // Wait input and exit waitKey(0); return 0; }
Ảnh nhị phân
- Là ảnh mà giá trị của các điểm ảnh chỉ được biểu diễn bằng hai giá trị là
0
(Đen) và255
(Trắng) (Tương ứng với0
và1
, nhưng để nguyên giá trị0
và255
để có thể hiểu hơn trong việc tính toán). - Vì giá trị của điểm ảnh được biểu diễn bởi 2 giá trị là
0
hoặc1
, nên 1 điểm ảnh được biểu diễn bằng1
bit nên ảnh có kích thước rất nhỏ.
Nhị phân hóa
Là quá trình biến đổi 1 ảnh xám thành ảnh nhị phân.
- Gọi giá trị cường độ sáng tại 1 điểm ảnh là
I(x,y)
. INP(x,y
) là cường độ sáng của điểm ảnh trên ảnh nhị phân .- (Với
0 < x < image.width
) và (0 < y < image.height
).
Để biến đổi ảnh xám thành ảnh nhị phân. So sánh giá trị cường độ sáng của điểm ảnh với 1 ngưỡng nhị phân T
.
- Nếu
I(x,y) > T
thìINP(x, y) = 0 (0)
. - Nếu
I(x,y) > T
thìINP(x, y) = 255 (1)
.
Chú ý
- Có thể chọn giá trị
T
từ0
đến255
, nhưng thông thường nhiều người hay chọn 1 giá trị đó là128
tức là giá trị trung bình của max(255
) và min(0
) của cường độ sáng (Intensity) của điểm ảnh. - Dễ dàng nhận thấy với mỗi
T
thì có 1 ảnh nhị phân khác nhau (Khác nhau ở đây là cường độ sáng của các tấm ảnh nhị phân với mỗi giá trịT
).
Có 1 kỹ thuật gọi là nhị phân hóa ngưỡng động giúp thu được ảnh nhị phân mà không quan tâm tới cường độ sáng.
Nhị phân hóa trong OpenCV
Phương thức threshold
Để chuyển 1 ảnh thành 1 ảnh nhị phân, sử dụng phương thức threshold()
.
threshold(cv::InputArray src, cv::OutputArray dst, double thresh, double maxval, int type);
Phân tích
src
: hình ảnh gốc (Trong bài viết này là ảnh màu).dst
: ảnh thu được (Trong bài viết này là ảnh nhị phân).thresh
: ngưỡng nhị phânT
.maxval
: giá trị lớn nhất trong ảnh (maxval = 255
đối với ảnh xám).type
: kiểu nhị phân.
Code minh hoạ phương thức threshold trong OpenCV
// www.stdio.vn // www.stdio.vn/users/index/11/truong-dat #include <stdio.h> #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" using namespace cv; int main() { // Read image Mat image = imread("stdio.png", CV_LOAD_IMAGE_GRAYSCALE); Mat imageBinary; // Check for valid if (!image.data) { printf("Could not open or find the image\n"); return -1; } threshold(image, imageBinary, 220, 255, CV_THRESH_BINARY); // Create and show image in window imshow("STDIO OpenCV Sample", imageBinary); // Wait input and exit waitKey(0); return 0; }
Nhị phân hóa ngưỡng động
Ý tưởng:
- Chia ảnh thành nhiều khu vực, cửa sổ khác nhau (Region).
- Dùng 1 thuật toán để tìm 1 giá trị
T
phù hợp với từng khu vực, cửa sổ (Region). - Áp dụng phương pháp nhị phân hóa cho từng khu vực, cửa sổ (Region) với
T
phù hợp.
Điều quan trọng trong kỹ thuật này là phải tìm 1 giá trị T
phù hợp với từng khu vực, cửa sổ (Region) hoặc cả tấm ảnh. Có rất nhiều phương pháp để tìm T
, ở nội dung tiếp theo sẽ giới thiệu 1 số thuật toán giúp tìm kiếm giá trị T
này.
Thuật toán Otsu
Bước 1: Xác định T1
. Giá trị cho T1
ban đầu nên chọn là (0+255) / 2 = 128
.
Bước 2: Phân loại thành 2 nhóm điểm ảnh.
- Loại 1 (
Type1
): chứa tất cả các điểm ảnh có giá trị cường độ sáng(Intensity) <= T
. - Loại 2 (
Type2
): chứa tất cả các điểm ảnh có giá trị cường độ sáng(Intensity) > T
.
Bước 3: Tính giá trị cường độ sáng trung bình (iAverage
) cho Type1 (iAverage1
) và Type2 (iAverage2
).
Bước 4: Tính giá trị T2
theo công thức (iAverage1 + iAverage2) /2
.
Bước 5: So sánh T1
và T2
.
- Nếu giá trị chênh lệch của
T1
vàT2 <= Delta
(1 giá trị cho trước) thìT2
chính làT
cần tìm. - Nếu giá trị chênh lệch của
T1
vàT2 > Delta
thì quay lại Bước 1.
Thuật toán đối xứng
Bước 1: khởi tạo mảng Histogram(histogram)
. Tìm giá trị cường độ sáng (intensityMax
) có tuần suất xuất hiện nhiều nhất histogram[intensityMax]
.
Bước 2: duyệt toàn bộ các mức xám giảm từ 255
đến intensityMax
. Nếu tại mức xám nào có tuần suất xuất hiện trên ảnh là 5%
thì dừng lại. Lấy giá trị đối xứng qua histogram[intensityMax]
là ngưỡng động T
.
for (int indexIntensity = 255; indexIntensity >= intensityMax; indexIntensity--) { frequency = histogram[indexIntensity]; if ((float)frequency / (image.Width * image.Height) == 0.05f) { split = indexInteensity; break; } } T = intensityMax - (split - intensityMax);
Thuật toán tam giác
Bước 1: Khởi tạo mảng Histogram(histogram)
.
- Tìm giá trị
intensityMax
vàhistogram[intensityMax]
. - Tìm giá trị
intensityMin
vàhistogram[intensittyMin]
.
Bước 2: Duyệt toàn bộ các mức xám từ intensityMin
đến intensityMax
. Tính khoảng cách tương ứng sau đó xét ngưỡng T
bằng giá trị mức xám có khoảng cách lớn nhất.
for(int index = intensityMin+1; index < intensityMax; index++) { distance = findDistance (Point(intensityMax, histogram[intensityMax]), Point(intensityMin, histogram[intensityMin]), Point(index, histogram[index])); if (distanceMax < distance) { distanceMax = distance; T = index; } }
Phân tích
Phương thức findDistance(Point a, Point b, Point c)
là tìm khoảng cách từ 1 điểm đến 1 đường thẳng với các tham số:
a
vàb
: lần lượt là 2 điểm khác biệt trên đường thẳng.c
: là điểm cần tìm khoảng cách tới đường thẳng.
Nhị phân hóa ngưỡng động trong OpenCV
Phương thức adaptiveThreshold
Để chuyển 1 ảnh thành 1 ảnh nhị phân, sử dụng phương thức adaptiveThreshold()
adaptiveThreshold(cv::InputArray src, cv::OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C);
Phân tích
src
: hình ảnh gốc (Trong bài viết này là ảnh màu).dst
: ảnh thu được (Trong bài viết này là ảnh nhị phân).thresh
: ngưỡng nhị phân T.maxValue
: giá trị lớn nhất trong ảnh (maxval = 255
đối với ảnh xám).adaptiveMethod
: cách thức nhị phân với ngưỡng động, nó chính là cách tính giá trị ngưỡng nhị phân trong từng vùng cần nhị phân.thresholdType
: kiểu nhị phân.blockSize
: kích thước của cửa sổ (Region) áp dụng cho việc tính toán ngưỡng động (nên chọn các giá trị%3 = 0 || %5 = 0 || %7 = 0
).C
: thông số để bù trừ trong trường hợp ảnh có độ tương phản quá lớn.
Code minh hoạ phương thức adaptiveThreshold trong OpenCV
#include <stdio.h> #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" using namespace cv; int main() { // Read image Mat image = imread("stdio.png", CV_LOAD_IMAGE_GRAYSCALE); Mat imageBinary; // Check for valid if (!image.data) { printf("Could not open or find the image\n"); return -1; } adaptiveThreshold(image, imageBinary, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 9, 0); // Create and show image in window imshow("STDIO OpenCV Sample", imageBinary); // Wait input and exit waitKey(0); return 0; }