opencv#24 图像直方图绘制

直方图统计

对图像中所有像素值进行统计,并绘制一个能够表示这个图像中每个像素所出现次数的直方图。opencv#24 图像直方图绘制_第1张图片

 通过统计得到的图像直方图,我们可以得到一些图像中的性质。直观的来看,就可以知道每个像素所出现的次数,进而反映出图像的亮暗,因为图像的像素值代表着图像的亮暗。如果某个像素在在统计之后如果某个像素出现的次数特别多,那么也就表示这图像中可能它的亮暗程度要受这个像素影响,同时,若某个像素出现的次数很少,那么可能图像中受这个像素值的影响较小。此外通过图像直方图,也可以清晰的看出图像的对比度,如果图像直方图分布比较均匀,那么这个图像直方图所表示的图像就是比较柔和的图,像素亮暗变化较柔和,若统计结果波动较大,那么可能此图像所显示的视觉效果就是对比度比较明显。

若想判断两个图像是否相同,那么首先可以统计这两个图像的直方图。若直方图一样,那么两张图片可能一致,如果不一样,那么两张图像一定不同。

统计像素直方图

calcHist()

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:输入的图像数量,若要统计两张图像直方图,那么设置参数为2,可以理解为第二个参数就是第一个参数的数组维度。

·channels:需要统计的通道索引数组,比如要统计两个图像直方图,那么这个参数就需要给出每个图象需要统计的直方图通道数,例如,需要统计第一张图像中的第0个通道,将数组设置为[0],要统计第二个图像的前两个通道,将数组设置为[1,2],由于是两张图像同时统计,因此第三个参数需要给出的是一个数组中包含数组的形式。

·mask:可选的操作掩码,需要统计哪些区域的像素,若要统计全部区域,那么就给出Mat []形式即可。

·hist:输出的统计直方图结果,是一个dims维度的数组。

·dims:需要计算直方图的维度,必须是整数。比如说只统计了一张单通道图像的直方图,那么维度就可以设置为1,它是一个一列的数据,如果设置成2,那么它就是一个二维的数据

·histSize:存放每个维度直方图的数组的尺寸,我们可以将其定义为图像中像素灰度值最大的数值,因为一个图像中像素有0-255个,每一个像素都要统计,因此是一个255*1的形式。

·ranges:每个图像通道中灰度值的取值范围,比如第一个图象中是0-255,取值范围就是0-255,由于此函数可以统计多个图像,因此它所能够表示的形式,与第三个参数类似,需要分别给出每一个图像中的通道的取值范围,通过数组的形式。比如第一个图像的通道取值范围是0-255,那么第二个通道的取值范围是0-8,因此每一个通道的取值范围,都是一个由两个数组成的数组,再由着两个数组成的数组,再次集成一个数组,就是第三个参数。

·uniform:直方图是否均匀的标识符,默认状态下为均匀(true)

·accumulate:是否累积统计直方图的标志。如果统计的是一个多个通道,如果将其设为"是",那么统计的结果就会出现一个完整的直方图,将两个通道中的数据生成一张直方图,如果选择的是"否",就分别统计每个通道的图像直方图。

但是opencv中没有提供直接绘制直方图的函数,因此我们需要根据这样一个输出结果自行绘制直方图。这里通常采用将绘制的直方图以矩阵的形式进行显示。

#include 
#include 

using namespace cv; //opencv的命名空间
using namespace std;




int main()
{
	Mat img = imread("E:/opencv/opencv-4.6.0-vc14_vc15/opencv/lenac.png");
	if (img.empty())
	{
		cout << "请确认图像文件名称是否正确" << endl;
		return -1;
	}
	Mat gray;
	cvtColor(img, gray, COLOR_BGR2GRAY); //转为灰度图像,只有一个通道
	//设置提取直方图的相关变量
	Mat hist;//用于存放直方图计算结果
	const int channels[1] = { 0 };//通道索引
	const int bins[1] = { 256 }; //直方图的维度,其实就是像素灰度值的最大值
	float inRanges[2] = { 0,255 };//一个图像灰度值取值范围0~255
	const float * ranges[1] = { inRanges };//像素灰度值范围,我们这里只有一个图像

	calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);//计算图像直方图

	//绘制直方图
	int hist_w = 512; //宽
	int hist_h = 400; //高
	int width = 2; //直方图矩形的宽度
	Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3); //定义一张全黑图像

	/*直方图输出方法一*/
	//for (int i = 1; i <= hist.rows; i++)
	//{
	//	rectangle(histImage, Point(width * (i - 1), hist_h - 1), //绘制矩形,给出坐标轴上的点
	//		Point(width * i - 1, hist_h - cvRound(hist.at(i - 1) / 25)), //给出统计结果上的点,读取后将数据长度缩小15倍,防止直方图过大,无法在背景图中显示
	//		Scalar(255, 255, 255), -1); //框图颜色为白,矩形填充
	//}

	/*直方图输出方法二*/
	//如果像素数目很大,那么除以15也不足以显示在图像中,那么就会出现部分缺失的情况,为了解决这样的问题,我们有这种方法:统计出直方图中最大的数值,将其设置为最高值,其他数与之相比较,这样无论图像有多么高或者多么小,都可以完整的显示,我们称之为"归一化"
	Mat hist_INF;//归一化矩阵
	normalize(hist, hist_INF, 1, 0, NORM_INF, -1, Mat());
	for (int i = 1; i <= hist_INF.rows; i++)
	{
		rectangle(histImage, Point(width * (i - 1), hist_h - 1), //给出坐标轴上的点
			Point(width * i - 1, hist_h - cvRound(hist_h*hist_INF.at(i - 1)) - 1 ), //给出统计结果上的点
			Scalar(255, 255, 255), -1); //框图颜色为白,矩形填充
	}


	imshow("histImage", histImage);
	imshow("gray", gray);


	waitKey(0);//等待函数用于显示图像
	return 0;

}

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