OpenCV drawing histogram implementation

What is a histogram

A histogram is a commonly used statistical graph of data. Find out the maximum and minimum values ​​of a certain physical or characteristic quantity, then determine an interval to include all the measurement data, divide the interval into several small intervals, and count the frequency of measurement results appearing in each small interval Or proportion, take the measured data as the abscissa, and take the frequency or proportion as the ordinate, draw the intervals of each small area and its corresponding frequency or proportion height, and then you can get a rectangular diagram, that is, a statistical histogram.

Histogram statistics

The number of small intervals divided into the histogram is called the number of groups, which is represented by bins, and the difference in abscissa between the two endpoints of each group is called the group distance. The range of the minimum and maximum values ​​of the data represented by the histogram is called the range of the histogram.

A histogram is called a uniform histogram if the group distances of all groups are the same, otherwise it is called a non-uniform histogram.

A histogram based on a physical quantity or characteristic value of an object is a one-dimensional histogram. In fact, a multidimensional histogram can also be established based on multiple physical quantities or characteristic values. For example, in population data, a population distribution histogram is established based on age + years of education. The graph is a two-dimensional histogram. The number of physical quantities or feature quantities is called the dimension of the histogram, expressed in dims.
insert image description here

image histogram

In image processing, the abscissa of the histogram is the set value composed of the pixel value data of the specific channel of the image, and the number or proportion of each value is used as the ordinate. The resulting histogram is the image histogram.
In fact, not only histograms are created based on image pixel values, but also histograms can be created for all image attribute values ​​such as image gradients and angles of each pixel. But histograms based on image pixel values ​​are the most common in image processing. Generally, image histograms are one-dimensional histograms.
Image histograms are invariant to translation, rotation and scaling. For the translation image, when the image angle is rotated, the histogram distribution before and after the image operation remains unchanged. For the zoom image, the distribution of the histogram before and after the image operation is also basically unchanged. You can judge whether an image is dark or not by the corresponding histogram of the image. Bright or normal light, the left side of the abscissa of the image histogram is a pure black, darker area, while the right side is a brighter, pure white area. Therefore, the data in the histogram of a darker image is more concentrated on the left and middle parts, while the overall bright image with only a few shadows is the opposite. As shown below:
insert image description here

OpenCV histogram drawing

For general 8bit images, you can directly use opencv functions for processing. For details, please refer to this blog . Below I paste the code directly:

Histogram plotting of a three-channel image:

Mat histogram_demo(Mat &image) 
{
    
    
	/*图像直方图是图像像素值的统计学特征,计算代价较小,具有图像的平移、旋转、缩放不变性的优点。
	Bins是指直方图的大小范围
	*/
	//三通道分离
	std::vector<Mat>bgr_plane;
	split(image, bgr_plane);
	//定义参数变量
	const int channels[1] = {
    
     0 };
	const int bins[1] = {
    
     256 };//一共有256个灰度级别
	float hranges[2] = {
    
     0,255 };//每个通道的灰度级别是0-255
	const float* ranges[1] = {
    
     hranges };
	Mat b_hist;
	Mat g_hist;
	Mat r_hist;
	//计算Blue、Green、Red通道的直方图,1表示只有一张图,因为可以支持多张图多个通道;0表示只有1个通道;raanges就是直方图的取值范围0-25
	calcHist(&bgr_plane[0], 1, 0, Mat(), b_hist, 1, bins, ranges);
	calcHist(&bgr_plane[1], 1, 0, Mat(), g_hist, 1, bins, ranges);
	calcHist(&bgr_plane[2], 1, 0, Mat(), r_hist, 1, bins, ranges);
	//显示直方图
	int hist_w = 512;
	int hist_h = 400;
	int bin_w = cvRound((double)hist_w / bins[0]);
	Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
	//归一化直方图数据
	normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
	normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
	normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
	//绘制直方图曲线
	for (int i = 1; i < bins[0]; i++) {
    
    
		line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),
			Point(bin_w * (i), hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);
		line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),
			Point(bin_w * (i), hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, 8, 0);
		line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),
			Point(bin_w * (i), hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, 8, 0);
	}
	//显示直方图
	//namedWindow("Histogram Demo", WINDOW_AUTOSIZE);
	//imshow("Histogram Demo", histImage);
	return histImage.clone();
}

The processing effect is as follows:
insert image description here

Histogram plot of grayscale image:

Mat histogram_grayImage(const Mat& image)
{
    
    
	//定义求直方图的通道数目,从0开始索引
	int channels[] = {
    
     0 };
	//定义直方图的在每一维上的大小,例如灰度图直方图的横坐标是图像的灰度值,就一维,bin的个数
	//如果直方图图像横坐标bin个数为x,纵坐标bin个数为y,则channels[]={1,2}其直方图应该为三维的,Z轴是每个bin上统计的数目
	const int histSize[] = {
    
     256 };
	//每一维bin的变化范围
	float range[] = {
    
     0,256 };

	//所有bin的变化范围,个数跟channels应该跟channels一致
	const float* ranges[] = {
    
     range };

	//定义直方图,这里求的是直方图数据
	Mat hist;
	//opencv中计算直方图的函数,hist大小为256*1,每行存储的统计的该行对应的灰度值的个数
	calcHist(&image, 1, channels, Mat(), hist, 1, histSize, ranges, true, false);

	//找出直方图统计的个数的最大值,用来作为直方图纵坐标的高
	double maxValue = 0;
	//找矩阵中最大最小值及对应索引的函数
	minMaxLoc(hist, 0, &maxValue, 0, 0);
	//最大值取整
	int rows = cvRound(maxValue);
	//定义直方图图像,直方图纵坐标的高作为行数,列数为256(灰度值的个数)
	//因为是直方图的图像,所以以黑白两色为区分,白色为直方图的图像
	Mat histImage = Mat::zeros(rows, 256, CV_8UC1);

	//直方图图像表示
	for (int i = 0; i < 256; i++)
	{
    
    
		//取每个bin的数目
		int temp = (int)(hist.at<float>(i, 0));
		//如果bin数目为0,则说明图像上没有该灰度值,则整列为黑色
		//如果图像上有该灰度值,则将该列对应个数的像素设为白色
		if (temp)
		{
    
    
			//由于图像坐标是以左上角为原点,所以要进行变换,使直方图图像以左下角为坐标原点
			histImage.col(i).rowRange(Range(rows - temp, rows)) = 255;
		}
	}
	//由于直方图图像列高可能很高,因此进行图像对列要进行对应的缩减,使直方图图像更直观
	Mat resizeImage;
	resize(histImage, resizeImage, Size(256, 256));
	return resizeImage;
}

The histogram statistics of the same image after it is grayscaled:
insert image description here

Custom drawn histogram

void DrawHist(void* srcData, int *m_dim)
{
    
    
	int iWidth = m_dim[0];
	int iHeight = m_dim[1];
	//histgram
	int min = 0;
	int max = 8000;
	int bin = 200;
	int internal = (max - min) / bin;
	MatND dstHist;
	Mat bins = GetBins(internal, 0, bin);
	dstHist = CalcGrayHist(bin, internal, (signed short*)srcData, iWidth, iHeight, 1);
	//get soft value
	Mat histMaxValue = GetMaxBin(dstHist, bins, bin, 500);
	int softValue = histMaxValue.at<int>(0, 0);
	int softIndex = histMaxValue.at<int>(0, 1);

	//设置直方图画布的参数,直方图要花在一个“幕布”上,这个幕布也要设置参数
	int hist_w = 1200;//画布的宽
	int hist_h = 800; //画布的高
	int bin_w = cvRound((double)hist_w / bin);                          //设置直方	图中每一点的步长,直方图有256个横坐标,每个坐标在画布中占多长,通过hist_w/bin计算得出。cvRound()函数是“四舍五入”的作用。
	Mat hist_canvas(hist_h, hist_w, CV_8UC3, cv::Scalar(255, 255, 255));//通过我们设置的参数创建出一个黑色的画布
																		//对三个通道的直方图数据进行归一化处理,这是一个必要环节
	normalize(dstHist, dstHist, 0, hist_h - 100, NORM_MINMAX, -1, Mat());
	int isGrid = 1;
	cv::Scalar scalarGrid = Scalar(255, 206, 135);
	if (isGrid)
	{
    
    
		//添加网格
		for (int i = 0; i < hist_w; )
		{
    
    
			cv::line(hist_canvas, Point(bin_w * i, hist_h - cvRound(0)),
				Point(bin_w * i, hist_h - cvRound(hist_h)), scalarGrid, 1, 8, 0);
			i += bin_w;
		}
		for (int i = 0; i < hist_h; )
		{
    
    
			cv::line(hist_canvas, Point(0, hist_w - bin_w * i),
				Point(hist_w, hist_w - bin_w * i), scalarGrid, 1, 8, 0);
			i += bin_w;
		}
	}
	//以折线统计图的方式绘制三个通道的直方图在画布上
	for (int i = 1; i < bin; i++)
	{
    
    
		cv::line(hist_canvas, Point(bin_w * (i - 1), hist_h - cvRound(dstHist.at<int>(i - 1))),
			Point(bin_w * (i), hist_h - cvRound(dstHist.at<int>(i))), Scalar(0, 0, 255), 2, CV_AA, 0);
	}
	cv::imshow("hist", hist_canvas);
	cv::waitKey(0);
	cv::destroyAllWindows();
}
//获取直方图bins
Mat GetBins(int internal, int min, int bin)
{
    
    
	Mat bins = Mat::zeros(Size(bin, 1), CV_32SC1);
	for (int i = 0; i < bin; i++)
	{
    
    
		bins.at<int>(i) = min + i*internal;
	}
	return bins;
}
//直方图统计
Mat CalcGrayHist(int bin, int internal, signed short* srcImage, int width, int height, int counts)
{
    
    
	Mat histogram = Mat::zeros(Size(bin, 1), CV_32SC1);  //注意,Size对应的是x和y,也就是第一个元数是矩阵的列数
	for (int k = 0; k < counts; k++)
	{
    
    
		for (int i = 0; i < height; i++)
		{
    
    
			for (int j = 0; j < width; j++)
			{
    
    
				int index = k*width*height + i*width + j;	    //获取每个点的像素值
				if (srcImage[index] > 3)
				{
    
    
					int value = srcImage[index] / internal;

					if (value < (bin - 1))
					{
    
    
						histogram.at<int>(0, value) += 1;			//获取了一个像素值,在相应的位置上加1
					}
					else
					{
    
    
						histogram.at<int>(0, bin - 1) += 1;

					}
				}
			}
		}
	}
	return histogram;
}

insert image description here

References:
[1] Basic operations of C++OpenCV image processing (five) - histogram
[2] RGB color table

Guess you like

Origin blog.csdn.net/qq_44924694/article/details/130594829