opencv图像的直方图,二维直方图,直方图均衡化

文章目录

    • opencv图像的直方图,二维直方图,直方图均衡化
      • 一、图像的直方图
        • 1、什么是图像的直方图:
        • 2、直方图的作用:
        • 3、如何绘制图像的直方图:
          • (1)cv::calcHist()函数原型:英文单词 calculator histogram
          • (2)代码示例:
            • cv::cvRound()函数:将浮点数值四舍五入为最接近的整数;
            • 灰度图像直方图:
            • 彩色图像直方图:
      • 二、二维直方图
        • 1、什么是二维直方图:
        • 2、二维直方图与直方图有哪些不同:
        • 3、二维直方图的作用:
        • 4、如何绘制二维直方图:
      • 三、直方图均衡化
        • 1、直方图均衡化的原理:
        • 2、opencv中,可以使用 `cv::equalizeHist()`函数来实现直方图均衡化:
          • (1)cv::equalizeHist()函数原型:
          • (2)代码示例:
            • 灰度直方图均衡化:
            • 彩色直方图均衡化:

opencv图像的直方图,二维直方图,直方图均衡化

一、图像的直方图

1、什么是图像的直方图:
  • 要理解直方图,绕不开“亮度”这个概念,人们把亮度分为0到255共256个数值,数值越大,代表的亮度越高,其中0代表纯黑色,最暗区域,255表示最亮,纯白色,而中间的数字就是不同亮度的灰色;
  • 图像的直方图是一种统计图,它显示了图像中各个灰度级别的分布情况(也就是统计一幅图某个亮度像素的数量),通常它的横轴代表像素的灰度级别(从0到255),而纵轴代表该灰度级别在图像中出现的频次或概率;
2、直方图的作用:
  • 可视化图像亮度分布:直方图能够帮助我们直观地了解图像的亮度分布情况,从而为图像处理提供重要参考;
  • 对比度调整:通过观察直方图,我们可以确定图像的对比度是否合适,从而决定是否需要进行对比度调整;
  • 灰度级别选择:直方图可以帮助我们选择合适的灰度级别,以使图像的细节更加清晰;
3、如何绘制图像的直方图:

在opencv中,可以使用 cv::calcHist()函数来计算图像的直方图,这个函数可以接受一个通道的图像(灰度图像)或多个通道的图像(彩色图像);

(1)cv::calcHist()函数原型:英文单词 calculator histogram
void cv::calcHist(
	const Mat* images, 
	int nimages, 
	const int* channels, 
	InputArray mask, 
	OutputArray hist, 
	int dims, 
	const int* histSize, 
	const float** ranges, 
	bool uniform = true, 
	bool accumulate = false
);

参数解释:
images:输入图像,可以是单通道或多通道图像;
nimages:输入图像的个数(可以输入多张图像);
channels:需要统计直方图的第几通道,比如{0}表示统计第一个通道,{0, 1, 2}表示统计所有三个通道;
mask:掩膜,用于指定计算直方图的区域(必须是一个8(CV_8U)的数组并且和images的数组大小相同);
hist:输出的直方图数组,calcHist函数只是计算直方图的数据,直方图数据需要一个cv::Mat类型的变量来存储;
dims:输出直方图的维度(
	由channels值决定dims的数值,比如int channels[] = { 0 }则dims=1int channels[] = { 0, 1 }则dims=2;
	对于灰度图像dims为1,因为我们只考虑了一个通道(亮度通道);
	对于彩色图像,通常会考虑多个通道,比如在HSV色彩空间中,dims为2(H和S两个通道) 
);
histSize:指的是直方图横坐标分成多少个区间,就是bin的个数(用于控制直方图的精细度);
把直方图横坐标ranges分成histSize个区间(比如ranges=180,histSize=30,则横坐标被分成了30个小竖条,每个小竖条的长度为6);
ranges:横轴代表像素的灰度级别,ranges相当于横坐标的取值范围(
	对于灰度图像,单通道只有1个range,灰度级别的范围从0255(
		int bins = 256;               		// X轴被分成了256个小区间
		int histSize[] = { bins };   		// histSize只有1个bins
		float xRanges[] = { 0, 256 }; 		// X轴的取值范围
		const float* ranges[] = { xRanges };    // ranges只有1个
	);
	对于彩色图像有多个通道就有多个range,对于每个通道,ranges指定了取值范围,通常在彩色图像中,H(色相)的范围是0180S(饱和度)V(明度)的范围是0255(
		// H通道X轴被分成了30个小区间,S通道X轴被分成了32个小区间
		int hueBins = 30, satBins = 32;	  
		int histSize[] = { hueBins, satBins };			// histSize有2个bins
		float hueRanges[] = { 0, 180 };				// H通道X轴的取值范围
		float satRanges[] = { 0, 256 };				// S通道X轴的取值范围
		const float* ranges[] = { hueRanges, satRanges };    	// ranges有2个
	));
uniform:是否对得到的直方图数组进行归一化处理;
accumulate:在多个图像时,是否累积计算像素值的个数;

(2)代码示例:
cv::cvRound()函数:将浮点数值四舍五入为最接近的整数;
// 函数在图像处理过程中经常用于处理像素值,特别是当需要将浮点数转换为整数时,可以保留最接近的整数值
int cvRound(double value)

参数解释:
value:待四舍五入的浮点数;
灰度图像直方图:
#include 
#include 
#include 

using namespace cv;
using namespace std;

int main() {

	// 读取灰度图像
	cv::Mat image = cv::imread("C:\\cpp\\image\\suzy2.jpg", cv::IMREAD_GRAYSCALE);

	if (image.empty()) {
		std::cerr << "Error: 无法读取图像文件." << std::endl;
		return -1;
	}

	// 直方图计算的输出值,是一个cv::Mat对象
	cv::Mat hist;
	// X轴被分成了256个小区间
	int bins = 256;
	// 灰度图只有1个ranges,所以histSize跟要ranges对应,定义成1个
	int histSize[] = { bins };
	// X轴的取值范围
	float xRanges[] = { 0, 256 };
	// 在C++中,数组名本身就代表该数组的首地址,无需使用取地址符号&,
	// const float* ranges = xRanges;
	const float* ranges[] = { xRanges };
	// 我们要计算灰度图像的第0个通道,所以这里channels定义成{0}
	int channels[] = { 0 };
	calcHist(&image, 1, channels, Mat(), hist, 1, histSize, ranges, true, false);

	// 待绘制的目标图像,将直方图计算的输出值hist,绘制到histImage图像上
	int histWidth = 512, histHeight = 400;
	int binWidth = cvRound( (double)histWidth/bins );
	cv::Mat histImage(histHeight, histWidth, CV_8UC3, cv::Scalar(0, 0, 0));
	// 使用normalize()函数将直方图的值缩放到图像的高度范围内
	normalize(hist, hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
	// 使用cv::line()函数绘制直线
	for (int i = 1; i<bins; i++) {
		cv::line(
			histImage,
			Point(binWidth*(i - 1), histHeight - cvRound(hist.at<float>(i - 1))),
			Point(binWidth*(i), histHeight - cvRound(hist.at<float>(i))),
			Scalar(255, 0, 0),
			2,
			LINE_8,
			0
		);
	}

	// 显示图像和直方图
	cv::imshow("Image", image);
	cv::imshow("Histogram", histImage);

	waitKey();
	return 0;
}

运行结果:
opencv图像的直方图,二维直方图,直方图均衡化_第1张图片

彩色图像直方图:

对于彩色图像,你可以分别计算其颜色通道(蓝色、绿色、红色)的直方图,或者将其转换为灰度图像后计算整体的亮度直方图;

在这个示例中,我们首先读取了一个彩色图像,然后使用 cv::split() 函数将图像分离成蓝色、绿色和红色通道,接着,我们分别计算了每个通道的直方图,最后我们绘制了各个通道的直方图并显示了原始图像和直方图;

#include 
#include 
#include 

using namespace cv;
using namespace std;

int main() {

	// 读取彩色图像
	cv::Mat image = cv::imread("C:\\cpp\\image\\suzy2.jpg");

	if (image.empty()) {
		std::cerr << "Error: 无法读取图像文件." << std::endl;
		return -1;
	}

	// 分离通道
	std::vector<cv::Mat> bgrPlanes;
	cv::split(image, bgrPlanes);

	// 直方图计算的输出值,是一个cv::Mat对象
	cv::Mat bHist, gHist, rHist;
	int bins = 256;	// B,G,R通道,X轴都被分成了256个小区间

	// B通道直方图计算
	// B通道,灰度图只有1个ranges,所以histSize跟要ranges对应,定义成1个
	int bHistSize[] = { bins };
	// B通道,X轴的取值范围,亮度级别
	float bXRanges[] = { 0, 256 };
	// 在C++中,数组名本身就代表该数组的首地址,无需使用取地址符号&,
	// const float* ranges = bXRanges;
	const float* bRanges[] = { bXRanges };
	// 图像通道被分离后,B通道图像只有一个通道,第0个通道,所以这里channels定义成{0}
	int bChannels[] = { 0 };
	calcHist(&bgrPlanes[0], 1, bChannels, Mat(), bHist, 1, bHistSize, bRanges, true, false);

	// G通道直方图计算
	// G通道,灰度图只有1个ranges,所以histSize跟要ranges对应,定义成1个
	int gHistSize[] = { bins };
	// G通道,X轴的取值范围,亮度级别
	float gXRanges[] = { 0, 256 };
	// 在C++中,数组名本身就代表该数组的首地址,无需使用取地址符号&,
	// const float* ranges = gXRanges;
	const float* gRanges[] = { gXRanges };
	// 图像通道被分离后,G通道图像只有一个通道,第0个通道,所以这里channels定义成{0}
	int gChannels[] = { 0 };
	calcHist(&bgrPlanes[1], 1, gChannels, Mat(), gHist, 1, gHistSize, gRanges, true, false);

	// R通道直方图计算
	// R通道,灰度图只有1个ranges,所以histSize跟要ranges对应,定义成1个
	int rHistSize[] = { bins };
	// R通道,X轴的取值范围,亮度级别
	float rXRanges[] = { 0, 256 };
	// 在C++中,数组名本身就代表该数组的首地址,无需使用取地址符号&,
	// const float* ranges = rXRanges;
	const float* rRanges[] = { rXRanges };
	// 图像通道被分离后,R通道图像只有一个通道,第0个通道,所以这里channels定义成{0}
	int rChannels[] = { 0 };
	calcHist(&bgrPlanes[2], 1, rChannels, Mat(), rHist, 1, rHistSize, rRanges, true, false);

	// 绘制直方图
	int histWidth = 512, histHeight = 400;
	int binWidth = cvRound( (double)histWidth/bins );
	cv::Mat histImage(histHeight, histWidth, CV_8UC3, cv::Scalar(0, 0, 0));
	normalize(bHist, bHist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
	normalize(gHist, gHist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
	normalize(rHist, rHist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());

	for (int i = 1; i<bins; i++) {
		// 绘制蓝色分量直方图
		cv::line(
			histImage,
			cv::Point( binWidth*(i-1), histHeight-cvRound( bHist.at<float>(i-1) ) ),
			cv::Point( binWidth*(i), histHeight-cvRound( bHist.at<float>(i) ) ),
			cv::Scalar(255, 0, 0),
			2,
			LINE_8,
			0
		);
		// 绘制绿色分量直方图
		cv::line(
			histImage,
			cv::Point( binWidth*(i - 1), histHeight-cvRound( gHist.at<float>(i-1) ) ),
			cv::Point( binWidth*(i), histHeight-cvRound( gHist.at<float>(i) ) ),
			cv::Scalar(0, 255, 0),
			2,
			LINE_8,
			0
		);
		// 绘制红色分量直方图
		cv::line(
			histImage,
			cv::Point( binWidth*(i - 1), histHeight-cvRound( rHist.at<float>(i-1) ) ),
			cv::Point( binWidth*(i), histHeight-cvRound( rHist.at<float>(i) ) ),
			cv::Scalar(0, 0, 255),
			2,
			LINE_8,
			0
		);
	}

	// 显示图像和直方图
	cv::imshow("Image", image);
	cv::imshow("Histogram", histImage);

	cv::waitKey(0);
	return 0;

}


运行结果:
opencv图像的直方图,二维直方图,直方图均衡化_第2张图片

二、二维直方图

1、什么是二维直方图:

二维直方图是对彩色图像进行分析时的一个重要工具,与一维直方图不同,它同时考虑了两个通道的信息,通常是颜色空间中的两个分量,例如在HSV色彩空间中的H(色相)和S(饱和度);

2、二维直方图与直方图有哪些不同:
  • 维度:直方图是一维的,它只考虑了图像的亮度或色彩信息;而二维直方图考虑了两个通道的信息,因此是二维的;
  • 通道:直方图通常只针对一个通道(灰度图只有亮度通道);而二维直方图可以同时考虑多个通道,通常是颜色空间中的两个分量;
3、二维直方图的作用:
  • 颜色分布分析:通过二维直方图,我们可以了解图像中各个颜色组合的分布情况,有助于理解图像的颜色特性;
  • 图像分割与对象识别:在图像分割和对象识别任务中,二维直方图可用于将图像的颜色特征映射到特定的空间,从而实现对图像中的对象进行定位和识别;
  • 颜色调整与匹配:通过分析二维直方图,我们可以进行颜色调整,使图像的颜色分布更加符合预期;
4、如何绘制二维直方图:

首先读取了一个彩色图像,然后将其从BGR颜色空间转换为HSV颜色空间,接着我们分离了H和S通道,并指定了直方图的维度、通道数、亮度级别个数和范围,然后我们使用 cv::calcHist()计算了二维直方图并使用 cv::rectangle()绘制;

#include 
#include 

using namespace cv;
using namespace std;

int main() {

	// 读取彩色图像
	cv::Mat image = cv::imread("C:\\cpp\\image\\suzy2.jpg");

	if (image.empty()) {
		std::cerr << "Error: 无法读取图像文件." << std::endl;
		return -1;
	}

	// 2D 直方图
	Mat hsv, hist;
	cvtColor(image, hsv, COLOR_BGR2HSV);
	// H通道X轴被分成了30个小区间,S通道X轴被分成了32个小区间
	int hueBins = 30, satBins = 32;
	// 彩色图像有多个ranges,所以histSize跟ranges对应也要定义多个,定义成数组的形式
	int histSize[] = { hueBins, satBins };
	float hueRanges[] = { 0, 180 };	// H通道X轴的取值范围
	float satRanges[] = { 0, 256 };	// S通道X轴的取值范围
	// 彩色图像有多个ranges,定义成数组的形式
	const float* ranges[] = { hueRanges, satRanges };
	// 我们要计算HSV图像的第0个通道和第1个通道的直方图,所以这里channels也要定义多个
	int channels[] = { 0, 1 };
	calcHist(&hsv, 1, channels, Mat(), hist, 2, histSize, ranges, true, false);

	// 画出计算后的直方图
	double maxVal = 0;
	minMaxLoc(hist, 0, &maxVal, 0, 0);
	int scale = 10;
	Mat histImage = Mat::zeros(satBins*scale, hueBins*scale, CV_8UC3);
	for (int h=0; h<hueBins; h++) {
		for (int s=0; s<satBins; s++){
			float binVal = hist.at<float>(h, s);
			int intensity = cvRound( binVal*255 / maxVal );
			rectangle(
				histImage, 
				Point( h*scale, s*scale ),
				Point( (h+1)*scale - 1, (s+1)*scale - 1 ),
				Scalar::all(intensity),
				-1
			);
		}
	}
	applyColorMap(histImage, histImage, COLORMAP_JET);

	imshow("Image", image);
	imshow("H-S Histogram", histImage);

	cv::waitKey(0);
	return 0;

}

运行结果:
opencv图像的直方图,二维直方图,直方图均衡化_第3张图片

三、直方图均衡化

直方图均衡化是一种用于增强图像对比度的图像处理技术,它通过重新分配图像的像素值,使得整个亮度范围得到充分利用,从而使图像看起来更清晰和具有更好的对比度;

1、直方图均衡化的原理:
  • 计算直方图:首先计算原始图像的灰度直方图,这个直方图描述了图像中各个灰度级别的分布情况;
  • 计算累积分布函数(CDF):将灰度直方图转换为累积分布函数,CDF表示了每个灰度级别的累积概率;
  • 映射新的像素值:对于每个像素,将其原始灰度值映射到新的值,使得新的值在整个亮度范围内均匀分布;
2、opencv中,可以使用 cv::equalizeHist()函数来实现直方图均衡化:
(1)cv::equalizeHist()函数原型:
void cv::equalizeHist(
	InputArray src, 
	OutputArray dst
);

参数解释:
src:输入图像(灰度图像);
dst:输出图像,用于存储均衡化后的结果;

(2)代码示例:
灰度直方图均衡化:
#include 
#include 

using namespace cv;
using namespace std;

int main() {

	// 读取灰度图像
	cv::Mat image = cv::imread("C:\\cpp\\image\\suzy2.jpg", cv::IMREAD_GRAYSCALE);

	if (image.empty()) {
		std::cerr << "Error: 无法读取图像文件." << std::endl;
		return -1;
	}

	// 均衡化灰度直方图
	cv::Mat equalizedImage;
	cv::equalizeHist(image, equalizedImage);

	 cv::imshow("Original Gray Image", grayImage);
  	 cv::imshow("Equalized Gray Image", equalizedImage);

	cv::waitKey(0);
	return 0;

}

运行结果:
opencv图像的直方图,二维直方图,直方图均衡化_第4张图片

彩色直方图均衡化:

首先将彩色图像转换为HSV色彩空间,然后对亮度(Value/V通道)进行均衡化,最后将其转换回BGR色彩空间以显示

#include 
#include 

using namespace cv;
using namespace std;

int main() {

	// 读取彩色图像
	cv::Mat image = cv::imread("C:\\cpp\\image\\suzy2.jpg", cv::IMREAD_COLOR);

	if (image.empty()) {
		std::cerr << "Error: 无法读取图像文件." << std::endl;
		return -1;
	}

	// 将图像从BGR色彩空间转换为HSV色彩空间
	cv::Mat hsvImage;
	cv::cvtColor(image, hsvImage, cv::COLOR_BGR2HSV);

	// 均衡化HSV通道的直方图
	std::vector<cv::Mat> channels;
	cv::split(hsvImage, channels);

	cv::equalizeHist(channels[2], channels[2]);

	cv::Mat equalizedHSV;
	cv::merge(channels, equalizedHSV);

	cv::cvtColor(equalizedHSV, equalizedHSV, cv::COLOR_HSV2BGR);

	cv::imshow("Original Color Image", image);
	cv::imshow("Equalized Color Image", equalizedHSV);

	cv::waitKey(0);
	return 0;

}

运行结果:

你可能感兴趣的:(opencv,opencv,人工智能,计算机视觉)