C++ ultra-detailed 8Bit image histogram statistics and drawing display, do not use the calcHist() function, write statistics by yourself

The function calcHist() provided in C++ OpenCv can conveniently help us count the histogram of an 8Bit image, but sometimes we don’t want to use the function provided by OpenCv to make statistics, and want to make a statistics according to our own needs, then see This tutorial is right. After learning this method, not only 8Bit images can be handwritten and counted by themselves, but 10Bit, 12Bit, 14Bit, 16Bit, 24Bit, 32Bit and other images can also be handwritten to realize histogram statistics and display them. For a specific tutorial on the statistics and display of 16Bit image histograms, see my next article.
The code for counting the histogram of 8Bit images by hand is as follows:

#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;
}

The original image of the above code running is shown below. You can download the image and run the code to have a look.
Note : The original image input here is a single-channel grayscale image. You can process multi-channel images sequentially through channel splitting, and you can also count the code by yourself.
insert image description here

The result of running the above code is as follows:
insert image description here
The final statistical histogram is:
insert image description here
In order to verify the code that I hand rolled, and count the histogram of the 8Bit image, the following uses the calcHist() function provided by OpenCv to compare statistics on the same image, as follows It is the code of the calcHist() function statistics provided by OpenCv:

#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;
}

The results of image histogram statistics using the calcHist() function provided by OpenCv are as follows:
insert image description here
From the above comparison, it can be seen that the results of the statistics of the code I wrote and the histogram results of the statistics of the calcHist() function provided by OpenCv are Consistent, it is verified that my method without using the calcHist() function provided by OpenCv is feasible.

The above is the detailed process of counting the histogram of 8Bit image through the code by hand. For the statistics of the histogram of 16Bit image, see my next article. If this method is useful to you, please like it and encourage it, thank you!

Guess you like

Origin blog.csdn.net/qq_40280673/article/details/124324942