Search…

Xử Lý Ảnh Với OpenCV: Độ Sáng, Độ Tương Phản Và Biểu Đồ Tần Số Histogram

15/09/20208 min read
Hướng dẫn thay đổi Brightness, Contrast của ảnh, tìm hiểu biểu đồ tần số Histogram.

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ủa Example.
  • CV_8UC3: Với mỗi điểm trên cv::Mat Example được lưu trữ dưới dạng: Loại unsigned 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ên Mat 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. 
ss_1

Để 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ủa G(x, y)F(x, y). Hay ảnh G tương phản gấp A lần với ảnh F.
  • B: Với B != 0, ảnh G 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ức G(x,y) = A*F(x, y) + B thì giá trị có thể là G(x, y) < 0 hoặc G(x,y) > 255.
uchar a = saturate_cast<uchar>(-100); // a = 0 (UCHAR_MIN)
short b = saturate_cast<short>(33333.33333); // b = 32767 (SHRT_MAX)
ss_2

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.
ss_3
  • 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.
ss_4
  • Ả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;
}
ss_6

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);
ss_8
IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024