C++ 超详细8Bit图像直方图统计并绘制显示,不使用calcHist()函数,自己手写统计

C++ OpenCv中提供的函数calcHist()可以很方便的帮助我们统计一幅8Bit图像的直方图,但是有时候我们不想用OpenCv提供的函数来统计,想自己根据自己的需求来做一个统计,那么看这个教程就对了,学会这种方法不仅是8Bit图像可以自己手写统计,10Bit,12Bit,14Bit,16Bit,24Bit,32Bit等图像,都可以自己手写代码实现直方图统计并显示出来。关于16Bit图像直方图统计并显示的具体教程,看我下一篇文章。
自己手撸统计8Bit图像直方图的代码见下:

#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>

using namespace std;
using namespace cv;



int main()
{
	Mat image = imread("Images/16bit/8bit_3M_Lj/Original_3M_Lj.png", -1);  //输入8Bit图像
	

	Mat histogram = Mat::zeros(Size(256, 1), CV_32SC1);    	 //注意,Size对应的是x和y,也就是第一个元数是矩阵的列数。比如我这里创建的是256列1行的画布,即大小为1*255
	
	int rows = image.rows;   	 //输入图像的行数
	int cols = image.cols;		 //输入图像的列数

	for (int i = 0; i<rows; i++)
	{
		for (int j = 0; j<cols; j++)
		{
			int index = int(image.at<uchar>(i, j));	    //获取每个点的像素值
			histogram.at<int>(0, index) += 1;			//获取了一个像素值,在相应的位置上加1
		}
	}

	//开始直观的显示直方图——绘制直方图  
	//首先先创建一个黑底的图像,为了可以显示彩色,所以该绘制图像是一个8位的3通道图像,3通道图像画直方图的时候就可以画彩色的直方图了  
	Mat drawImage1 = Mat::zeros(Size(256, 400), CV_8UC3);   //这里是最后画直方图的地方,大小为400行,256列,这里画布的大小可以自定义

	//因为任何一个图像的某个像素的总个数,都有可能会有很多,会超出所定义的图像的尺寸,针对这种情况,先对个数进行范围的限制  
	//先用 minMaxLoc函数来得到计算直方图后的像素的最大个数  
	double g_dHistMaxValue;    //最大的像素值数目
	minMaxLoc(histogram, 0, &g_dHistMaxValue, 0, 0);

	//将像素的个数整合到 图像的最大范围内  
	//遍历直方图得到的数据  
	 
	for (int i = 0; i < 256; i++)
	{
		int value = cvRound(histogram.at<int>(0, i) * 256 * 1.4 / g_dHistMaxValue);   //这里公式中乘的1.4是为了使显示的直方图更高更好看一下,可以适当调整此值,自己看着舒服

		line(drawImage1, Point(i, drawImage1.rows - 1), Point(i, drawImage1.rows - 1 - value), Scalar(255, 255, 0));   //以线条的方式来绘画直方图
		//rectangle(drawImage1, Point(i, drawImage1.rows - 1), Point(i, drawImage1.rows - 1 - value), Scalar(255, 255, 0));  //以矩形框的方式绘画直方图,这里和上面的显示方式任意一种都可以
	}

	imshow("图像直方图", drawImage1);


	cout << "histogram = " << histogram;



	waitKey();
	return 0;
}

上面代码运行的原始图像见下,大家可以自行下载图片后运行代码看看。
注意:这里输入的原始图像是单通道的灰度图像,大家可以依次原理,通过通道拆分依次处理多通道图像,也是可以自己手撸代码统计出来的。
在这里插入图片描述

上面代码运行结果见下:
在这里插入图片描述
最终统计的直方图为:
在这里插入图片描述
为了验证一下自己手撸的代码,统计8Bit图像的直方图对不对,下面采用OpenCv提供的calcHist()函数对同一张图像统计对比一下,下面是OpenCv提供的calcHist()函数统计的代码:

#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>

using namespace std;
using namespace cv;

void show_histogram(Mat& img)
{
	//为计算直方图配置变量  
	//首先是需要计算的图像的通道,就是需要计算图像的哪个通道(bgr空间需要确定计算 b或g货r空间)  
	int channels = 0;
	//然后是配置输出的结果存储的 空间 ,用MatND类型来存储结果  
	MatND dstHist;
	//接下来是直方图的每一个维度的 柱条的数目(就是将数值分组,共有多少组)  
	int histSize[] = { 256 };       //如果这里写成int histSize = 256;   那么下面调用计算直方图的函数的时候,该变量需要写 &histSize  
	//最后是确定每个维度的取值范围,就是横坐标的总数  
	//首先得定义一个变量用来存储 单个维度的 数值的取值范围  
	float midRanges[] = { 0, 256 };
	const float *ranges[] = { midRanges };

	calcHist(&img, 1, &channels, Mat(), dstHist, 1, histSize, ranges, true, false);

	//calcHist  函数调用结束后,dstHist变量中将储存了 直方图的信息  用dstHist的模版函数 at<Type>(i)得到第i个柱条的值  
	//at<Type>(i, j)得到第i个并且第j个柱条的值  

	//开始直观的显示直方图——绘制直方图  
	//首先先创建一个黑底的图像,为了可以显示彩色,所以该绘制图像是一个8位的3通道图像  
	Mat drawImage = Mat::zeros(Size(256, 400), CV_8UC3);
	//因为任何一个图像的某个像素的总个数,都有可能会有很多,会超出所定义的图像的尺寸,针对这种情况,先对个数进行范围的限制  
	//先用 minMaxLoc函数来得到计算直方图后的像素的最大个数  
	double g_dHistMaxValue;    //最大的像素值数目
	minMaxLoc(dstHist, 0, &g_dHistMaxValue, 0, 0);
	//minMaxLoc(hist, &minValue, &maxValue, 0, 0);//找到全局最小、最大的像素值数目
	//将像素的个数整合到 图像的最大范围内  
	//遍历直方图得到的数据  
	for (int i = 0; i < 256; i++)
	{
		int value = cvRound(dstHist.at<float>(i) * 256 * 1.4 / g_dHistMaxValue);

		line(drawImage, Point(i, drawImage.rows - 1), Point(i, drawImage.rows - 1 - value), Scalar(255, 255, 0));
	}

	imshow("直方图统计", drawImage);
}

int main()
{
	//Mat image = imread("Images/16bit/8bit_3M_Lj/16bitZhiFanTuJunHen_PinTai_3M_Lj(7).png", -1);
	Mat image = imread("Images/16bit/8bit_3M_Lj/Original_3M_Lj.png", -1);   //输入8Bit图像
	//Mat image = imread("Images/16bitwhq.png", -1);
	show_histogram(image);

	
	waitKey();
	return 0;
}

上面采用OpenCv提供的calcHist()函数对图像直方图统计的结果见下:
在这里插入图片描述
通过以上对比可以看出,我自己写的代码统计出来的结果和采用OpenCv提供的calcHist()函数统计的直方图结果是一致的,验证了我的这种不借助OpenCv提供的calcHist()函数方法是可行的。

以上就是通过自己手撸代码统计8Bit图像直方图的详细过程,关于16Bit图像直方图的统计,见我下一篇文章,如果这种方法对你有用,点个赞,鼓励一下,谢谢!

猜你喜欢

转载自blog.csdn.net/qq_40280673/article/details/124324942