Implementación del histograma de dibujo de OpenCV

que es un histograma

Un histograma es un gráfico estadístico de datos de uso común. Averigüe los valores máximos y mínimos de una cierta cantidad física o característica, luego determine un intervalo para incluir todos los datos de medición, divida el intervalo en varios intervalos pequeños y cuente la frecuencia de los resultados de medición que aparecen en cada pequeño intervalo o proporción , tome los datos medidos como abscisas, y tome la frecuencia o proporción como ordenadas, dibuje los intervalos de cada área pequeña y su correspondiente altura de frecuencia o proporción, y luego puede obtener un diagrama rectangular, es decir, un histograma estadístico.

Estadísticas de histograma

El número de pequeños intervalos divididos en el histograma se denomina número de grupos, que se representa mediante contenedores, y la diferencia en abscisas entre los dos extremos de cada grupo se denomina distancia de grupo. El rango de los valores mínimo y máximo de los datos representados por el histograma se denomina rango del histograma.

Un histograma se llama histograma uniforme si las distancias de grupo de todos los grupos son las mismas; de lo contrario, se llama histograma no uniforme.

Un histograma basado en una cantidad física o valor característico de un objeto es un histograma unidimensional. De hecho, también se puede establecer un histograma multidimensional basado en múltiples cantidades físicas o valores característicos. Por ejemplo, en datos de población, un histograma de distribución de población se establece en base a edad + años de educación.El gráfico es un histograma bidimensional. El número de cantidades físicas o cantidades de características se denomina dimensión del histograma, expresada en dims.
inserte la descripción de la imagen aquí

histograma de imagen

En el procesamiento de imágenes, la abscisa del histograma es el valor establecido compuesto por los datos de valor de píxel del canal específico de la imagen, y el número o la proporción de cada valor se utiliza como ordenada. El histograma resultante es el histograma de imagen.
De hecho, no solo se crean histogramas en función de los valores de los píxeles de la imagen, sino que también se pueden crear histogramas para todos los valores de los atributos de la imagen, como los gradientes de la imagen y los ángulos de cada píxel. Pero los histogramas basados ​​en valores de píxeles de imagen son los más comunes en el procesamiento de imágenes. Generalmente, los histogramas de imagen son histogramas unidimensionales.
Los histogramas de imagen son invariantes a la traslación, rotación y escala. Para una imagen de traslación, cuando se gira el ángulo de la imagen, la distribución del histograma antes y después de la operación de la imagen permanece sin cambios. Para una imagen ampliada, la distribución del histograma antes y después de la operación de la imagen tampoco cambia básicamente. Puede juzgar si un la imagen es oscura o no por el histograma correspondiente de la imagen Luz brillante o normal, el lado izquierdo de la abscisa del histograma de la imagen es un área más oscura, negra pura, mientras que el lado derecho es un área blanca pura más brillante. Por lo tanto, los datos en el histograma de una imagen más oscura están más concentrados en las partes izquierda y media, mientras que la imagen brillante general con solo unas pocas sombras es todo lo contrario. Como se muestra abajo:
inserte la descripción de la imagen aquí

Dibujo de histograma de OpenCV

Para imágenes generales de 8 bits, puede utilizar directamente las funciones de opencv para el procesamiento. Para obtener más información, consulte este blog . A continuación pego el código directamente:

Trazado de histograma de una imagen de tres canales:

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();
}

El efecto de procesamiento es el siguiente:
inserte la descripción de la imagen aquí

Gráfico de histograma de imagen en escala de grises:

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

Las estadísticas del histograma de la misma imagen después de que esté en escala de grises:
inserte la descripción de la imagen aquí

Histograma personalizado

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

inserte la descripción de la imagen aquí

Referencias:
[1] Operaciones básicas del procesamiento de imágenes C++OpenCV (cinco) - histograma
[2] Tabla de colores RGB

Supongo que te gusta

Origin blog.csdn.net/qq_44924694/article/details/130594829
Recomendado
Clasificación