Giới thiệu
OpenCV (Open Computer Vision) là một 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ờ một 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ó một hình ảnh khác phù hợp với nhu cầu của người dùng.
Bài viết hướng dẫn thay đổi độ sáng, tương phản của một hình ảnh và tìm hiểu biểu đồ tần số Histogram.
Tăng giảm độ sáng, độ tương phản trong OpenCV
Một hình ảnh được lưu trữ dưới dạng một ma trận các điểm ảnh (Pixel).
Trong OpenCV được biểu diễn dưới dạng cv::Mat
.
cv::Mat Example(2, 3, CV_8UC3, Scalar(1, 2, 3));
Phân tích
Cách khởi tạo đơn giản với kiểu dữ liệu cv::Mat
. Bản chất của cv::Mat
là một ma trận. Ví dụ này giống như cấu trúc một ảnh có không gian màu RGB.
- Hai tham số đầu tiên là kích thước của ma trận lần lượt là hàng (
Rows
) = 2 và cột (Colums
) = 3 củaExample
. CV_8UC3
: Với mỗi điểm trêncv::Mat Example
được lưu trữ dưới dạng: Loạiunsigned char
(0-255) có 8 bit và có 3 kênh.
CV_[Số bit cho mỗi kênh của điểm][Signed hoặc Unsigned][Kiểu dữ liệu]C[Số lượng kênh]
Scalar(1, 2, 3)
: Với mỗi điểm trênMat Example
được khởi tạo các giá trị 1, 2 và 3 tương ứng với các kênh 1, kênh 2 và kênh 3.
Để có thể truy cập giá trị điểm ảnh (Pixel) với những kênh màu:
image.at<cv::Vec3b>(row, col)[channel]
row
: Là hàng thứ row trong image.col
: Là cột thứ col trong image.channel
: Là kênh màu của image. Ví dụ: Với không gian màu R-G-B, channel có giá trị lần lượt làchannel = 1
,channel = 2
,channel = 3
. Trong OpenCV kênh màu của không gian R-G-B được xếp theo thứ tự là B-G-R.
Công thức
Để thay đổi độ sáng, độ tương phản của một tấm ảnh, sử dụng công thức sau:
G(x,y) = A*F(x, y) + B
Phân tích:
F(x, y)
: là giá trị pixel trong ảnh chưa được xử lý ở vị trí (x, y).G(x, y)
: là giá trị pixel trong ảnh đã được xử lý ở vị trí (x, y).A
: Là giá trị đặc trưng cho chênh lệch độ tương phản củaG(x, y)
vàF(x, y)
. Hay ảnhG
tương phản gấpA
lần với ảnhF
.B
: VớiB != 0
, ảnhG
có độ sáng thay đổi một lượng làB
.
Code
#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 imageDst = image.clone(); // sao chép 2 Mat // Check for valid if (!image.data) { printf("Could not open or find the image\n"); return -1; } #define NUMBER_CHANNEL_RGB_COLOR_MODEL 3 double contrast = 2.0; // A int brightness = 30; // B for (int indexRow = 0; indexRow < image.rows; indexRow++) { for (int indexCol = 0; indexCol < image.cols; indexCol++) { for (int channel = 0; channel < NUMBER_CHANNEL_RGB_COLOR_MODEL; channel++) { imageDst.at<Vec3b>(indexRow, indexCol)[channel] = saturate_cast<uchar> (contrast*(imageDst.at<Vec3b>(indexRow, indexCol)[channel]) + brightness); } } } imshow("Before", image); imshow("After", imageDst); // Wait input and exit waitKey(0); return 0; }
Phân tích:
- Phương thức
saturate_cast()
giúp tránh tình trạng tràn số hay kiểu dữ liệu không tương thích. Vì giá trị điểm ảnh (Pixel) phải nằm trong khoảng 0-255 (unsigned char
), trong khi theo công thứcG(x,y) = A*F(x, y) + B
thì giá trị có thể làG(x, y) < 0
hoặcG(x,y) > 255
.
uchar a = saturate_cast<uchar>(-100); // a = 0 (UCHAR_MIN) short b = saturate_cast<short>(33333.33333); // b = 32767 (SHRT_MAX)
Biểu đồ phân bố tần số Histogram
Khái niệm
- Biểu đồ phân bố tần số (Biểu đồ phân bố mật độ, biểu đồ cột) dùng để đo tần số xuất hiện của một vấn đề nào đó, cho thấy rõ hình ảnh sự thay đổi, biến động của một tập dữ liệu. Đây là một khái niệm rất phổ biến, được sử dụng rộng rãi từ lĩnh vực kỹ thuật tới kinh tế.
- Trong xử lý ảnh, biểu đồ Histogram của một ảnh là biểu đồ mô tả sự phân bố của các giá trị mức xám của các điểm ảnh (Pixel) trong một bức ảnh hoặc một vùng ảnh (Region).
- Trong một ảnh, giá trị của một điểm ảnh (Pixel) thường là từ 0-255.
- Trục tung (Oy) biểu diễn số lượng điểm ảnh (Pixel) của mức xám.
- Trục hoành (Ox) biểu diễn mức xám.
- Giá trị lớn nhất của trục hoành chính là số lượng điểm ảnh (Pixel) có trong một bức ảnh.
- Với ảnh màu như RGB thì có tới 3 biểu đồ Histogram thể hiện từng kênh màu.
- Một biều đồ tốt à biểu đồ có số lượng điểm ảnh nhiều nhất ở vùng giữa (Độ sáng trung bình) và ít dần ra 2 vùng sáng tối (Ngọn núi).
#define MAX_INTENSITY 255 int histogram[] = new int[MAX_INTENSITY]; for (int x = 0; x < image.Width-1; x++) { for (int y = 0; y < image.Height-1; y++) { histogram[image.GetPixel[x, y].R] += 1; } }
Ứng dụng
- Dựa vào biều đồ Histogram có thể biết được hình ảnh sáng tối như thế nào.
- Áp dụng cho các xử lý ảnh cao cấp khác.
- Ảnh tối là ảnh có tập trung quá nhiều điểm ảnh bên vùng tối.
- Ảnh sáng là ảnh có tập trung quá nhiều điểm ảnh bên vùng sáng.
- Ảnh có độ tương phản cao là ảnh có điểm ảnh tập trung nhiều ở 2 vùng sáng và tối và tập trung ít ở vùng giữa.
- Ảnh có độ tương phản thấp là ảnh có điểm ảnh tập trung nhiều ở vùng giữa và tập trung rất ít ở vùng hai vùng sáng và tối.
Histogram trong OpenCV
Một số phương thức cơ bản
calcHist()
: tính toán hình dạng của biểu đồ Histogram.equalizeHist()
: bình thường hóa hình ảnh và độ tương phản.compareHist()
: so sánh hai biểu đồ Histograms.
Tính toán và vẽ biểu đồ histogram
Ảnh xám
Đoạn code dưới đây sẽ tính toán và vẽ biểu đồ Histogram của một tấm ảnh.
#include <stdio.h> #include <iostream> #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" using namespace cv; using namespace std; int main() { Mat imageSrc = imread("carStdio.png", CV_LOAD_IMAGE_GRAYSCALE); Mat imageGrayscale; int width = 400, height = 400; int sizeHistogram = 255; float range[] = { 0, 255 }; const float* histogramRange = { range }; calcHist(&imageSrc, 1, 0, Mat(), imageGrayscale, 1, &sizeHistogram, &histogramRange, true, false); int bin = cvRound((double)width / sizeHistogram); Mat dispHistogram(width, height, CV_8UC3, Scalar(255, 255, 255)); normalize(imageGrayscale, imageGrayscale, 0, dispHistogram.rows, NORM_MINMAX, -1, Mat()); for (int i = 0; i < 255; i++) { line(dispHistogram, Point(bin*(i), height), Point(bin*(i), height - cvRound(imageGrayscale.at<float>(i))), Scalar(0, 0, 0), 2, 8, 0); } imshoe("Car Stdio.vn", imageSrc); imshow("Hitogram", dispHistogram); // Wait input and exit waitKey(0); return 0; }
Ảnh màu
Với ảnh màu có không gian màu R-G-B, để vẽ biểu đồ Histogram cho 3 kênh màu của hình ảnh. Cần phải tách ảnh thành 3 kênh màu riêng biệt.
int main() { Mat imageSrc = imread("carStdio.png", CV_LOAD_IMAGE_COLOR); vector<Mat> imageRGB; // Khởi tạo các biến lưu trữ 3 bênh màu Mat imageRed, imageGreen, imageBlue; int width = 250, height = 250; int sizeHistogram = 255; float range[] = { 0, 255 }; const float* histogramRange = { range }; // Hàm này có tác dụng tách imageSrc thành 3 kênh màu. split(imageSrc, imageRGB); // Tính toán cho từng kênh màu và vẽ biểu đồ Histogram calcHist(&imageRGB[0], 1, 0, Mat(), imageRed, 1, &sizeHistogram, &histogramRange, true, false); calcHist(&imageRGB[1], 1, 0, Mat(), imageGreen, 1, &sizeHistogram, &histogramRange, true, false); calcHist(&imageRGB[2], 1, 0, Mat(), imageBlue, 1, &sizeHistogram, &histogramRange, true, false); int bin = cvRound((double)width / sizeHistogram); Mat dispRed(width, height, CV_8UC3, Scalar(255, 255, 255)); Mat dispGreen = dispRed.clone(); Mat dispBlue = dispRed.clone(); normalize(imageBlue, imageBlue, 0, dispBlue.rows, NORM_MINMAX, -1, Mat()); normalize(imageGreen, imageGreen, 0, dispGreen.rows, NORM_MINMAX, -1, Mat()); normalize(imageRed, imageRed, 0, dispRed.rows, NORM_MINMAX, -1, Mat()); for (int i = 0; i < 255; i++) { line(dispRed, Point(bin*(i), height), Point(bin*(i), height - cvRound(imageRed.at<float>(i))), Scalar(0, 0, 255), 2, 8, 0); line(dispGreen, Point(bin*(i), height), Point(bin*(i), height - cvRound(imageGreen.at<float>(i))), Scalar(0, 255, 0), 2, 8, 0); line(dispBlue, Point(bin*(i), height), Point(bin*(i), height - cvRound(imageBlue.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0); } namedWindow("src", 0); imshow("STDIO OpenCV Sample", imageSrc); imshow("H blue", dispBlue); imshow("H green", dispGreen); imshow("H red", dispRed); // Wait input and exit waitKey(0); return 0; }
Cân bằng Histograms
Cân bằng Histogram (Histogram equalization) là phương pháp làm cho biểu đồ Histogram của ảnh được phân bố một cách đồng đều. Đây là một cách giúp nâng cao chất lượng hình ảnh. Để cân bằng dùng phương thức equalizeHist()
.
Ảnh xám
Mat imageSrc = imread("carStdio.png", CV_LOAD_IMAGE_SCALE); Mat imageDst; equalizeHist(imageSrc, Dst); imshow("imageSrc", src); imshow("imageDst", dst);
Ảnh màu
Để cân bằng Histogram cho một tấm ảnh màu, trước hết cần phải chuyển ảnh màu ở dạng RGB sang HSV, sau đó cân bằng thành phần kênh màu V (Value mức độ sáng) và sau đó chuyển đổi ngược lại.
Mat imageSrc = imread("carStdio.png", CV_LOAD_IMAGE_COLOR); Mat imageHsv, imageDst; cvtColor(imageSrc, imageHsv, CV_BGR2HSV); vector<Mat> hsvChannels; // Tách imageHsv thành 3 kênh màu split(imageHsv, hsvChannels); // Cân bằng histogram kênh màu v (Value) equalizeHist(hsvChannels[2], hsvChannels[2]); // Trộn ảnh merge(hsvChannels, imageHsv); // Chuyển đổi HSV sang RGB để hiển thị cvtColor(imageHsv, imageDst, CV_HSV2BGR); imshow("imageSrc", imageSrc); imshow("imageDst", imageDst); waitKey(0);