【Opencv入门】RGB三通道直方图的计算与绘制

文章目录

  • 一、 直方图概述 Overview of histogram
  • 二、直方图的建立 Establishment of histogram
  • 三、直方图的作用 The function of histogram
    • 1)图像匹配
    • 2)判断成像质量
    • 3)二值化阈值
  • 四、编程实现 Programming implementation
  • 总结


一、 直方图概述 Overview of histogram

直方图(Histogram),又称质量分布图,是一种统计报告图,由一系列高度不等的纵向条纹或线段表示数据分布的情况。 一般用横轴表示数据类型,纵轴表示分布情况。
直方图是图像处理中的一个有着广泛应用的工具,直方图本质是概率分布的图形化,同时直方图也可以用来表示向量。

二、直方图的建立 Establishment of histogram

下面以一幅分辨率为8*8、8级灰度的图像来介绍如何建立一幅直方图

【Opencv入门】RGB三通道直方图的计算与绘制_第1张图片
以Pi(i = 0、1、2、3…7)表示八级灰度每一级的概率,Ni(i = 0、1、2、3…7)表示88共64个像素点中各级灰度出现的次数,则Pi = Ni/(88),即灰度出现的次数对像素点总数的归一化。
由此绘制出的Pi-灰度的关系图就是灰度直方图。横坐标为灰度值,纵坐标为该灰度值出现的概率。
当然一般是以8位表示一个像素点,灰度一般有256级。
【Opencv入门】RGB三通道直方图的计算与绘制_第2张图片
从这张直方图上可以很直观地看到,它的灰度值以0、1、2为主,显然是一张偏暗的图片。

对于图像,不光可以建立灰度直方图,也可以自己设计方法建立任意形式的直方图。如可以对RGB图像三个颜色通道分别建立直方图,得到彩色直方图。也可以针对图像的HSV色彩模型,建立HSC直方图。
两个直方图的比较可以使用欧几里德距离,马氏距离等。Opencv中的compareHist函数提供了四种方法:Correlation、Chi-Square,Intersection,Bhattacharyya距离。
可以想象,如果两个直方图的欧几里德距离为零的话,那么就应该是两个完全一样的直方图。

三、直方图的作用 The function of histogram

1)图像匹配

比较两幅图像的直方图,可以得到两幅图像的相似程度。其本质是对比灰度出现的概率是否相似。
在图像匹配的时候,也可以对其他的特征量建立直方图,来进行匹配。
同样的图像直方图显然相同。反之却不一定成立,这是因为直方图只能记录各级灰度出现的概率,却丢失了空间信息。
如下图就是一个明显的反例:
【Opencv入门】RGB三通道直方图的计算与绘制_第3张图片
那么为何要对灰度进行归一化呢,假设我们有一张原图和一张对原图进行放大缩小后的图片,两种图片实际上是同一张图片,只是大小不同,它们的直方图理应是相同的。但是如果不仅归一化,仅仅是以各级灰度出现的次数作为纵坐标,由于放大缩小后的图像与原图像素点存在差异,它呈现出来的纵坐标也是不同的,这就导致了两张相同的图片直方图不同,因此要进行归一化。

2)判断成像质量

下面以几张图片作为示例:
【Opencv入门】RGB三通道直方图的计算与绘制_第4张图片

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3)二值化阈值

在图像二值化的过程中,我们可以通过分析直方图选择一个适当的阈值,把灰度图像转换为二值化图像,通常的目的是分离前景和背景
【Opencv入门】RGB三通道直方图的计算与绘制_第5张图片
以一张256级灰度的细胞的图片为例,下面是它的直方图
【Opencv入门】RGB三通道直方图的计算与绘制_第6张图片
可以明显地看到,它的直方图呈现双峰性,它的前景分布集中在一个区域,背景分布集中在另一个区域,这时候我们只要选择直方图中的谷值,就能得到较为理想的二值化图像。
【Opencv入门】RGB三通道直方图的计算与绘制_第7张图片

四、编程实现 Programming implementation

通过Opencv编程实现读取一张图片计算并显示RGB三通道直方图。
这里用的Opencv版本是4.40。
当然这里可以用Opencv自带的函数calcHist()函数计算直方图,自己写一个算直方图的函数只是为了能够加深对直方图和OpencvMat类的理解。

#include 
#include 
using namespace cv;
#define hist_rgb_width  800
#define hist_rgb_height 400

void Calc_histogram(cv::Mat src)
{
	/*定义直方图矩阵
	 *这里以一个256*3的矩阵存放各个通道各个灰度值的个数
	 *以数组下标标记灰度值,数组内容为该灰度值的个数
	 */
	float histogram[256][3] = { 0 };
	//定义vector作为三个通道的容器
	std::vector<cv::Mat>	channels;
	//分离三个通道像素值
	cv::split(src, channels);
	cv::Mat B = channels.at(0);
	cv::Mat G = channels.at(1);
	cv::Mat R = channels.at(2);
	//*****遍历*****
	//获取向量长度
	int height = src.rows;
	int width  = src.cols;
	long int size = height * width;
	for (int j = 0; j < height; ++j)
	{
		for (int i = 0; i < width; ++i)
		{
			++histogram[B.at<uchar>(j, i)][0];
			++histogram[G.at<uchar>(j, i)][1];
			++histogram[R.at<uchar>(j, i)][2];
		}
	}
	//*****归一化*****
	/* 由于直方图归一化以后的纵坐标小于1,需要放大以后显示才直观
	 * 而放大多少倍才能使现有的画布容纳所有放大后的线段
	 * 这里用三个变量记录归一化后最大的值,将最大值放大为画布高度400
	 * 最大值能容纳,其他的值也自然能容纳了
	 */
	float BMax = 0;
	float GMax = 0;
	float RMax = 0;
	for (int i = 0; i < 256; ++i)
	{
		histogram[i][0] = histogram[i][0] / size;
		histogram[i][1] = histogram[i][1] / size;
		histogram[i][2] = histogram[i][2] / size;

		if (histogram[i][0] > BMax)
			BMax = histogram[i][0];
		if (histogram[i][1] > GMax)
			GMax = histogram[i][1];
		if (histogram[i][2] > RMax)
			RMax = histogram[i][2];
	}
	cv::Mat dispMat(hist_rgb_height, hist_rgb_width, CV_8UC3, Scalar(0, 0, 0));
	cv::Point pt1, pt2;
	pt1.y = 400;
	pt1.x = 0;
	pt2.x = 0;

	int Coeficient = 0;
	//绘制R通道直方图
	Coeficient = (int)(400 / RMax);
	for (int i = 0; i < 256; ++i)
	{
		pt2.y = pt1.y - histogram[i][2] * Coeficient;
		cv::line(dispMat, pt1, pt2, Scalar(0, 0, 255), 1, 8, 0);
		pt2.x = ++pt1.x;
	}
	//绘制G通道直方图
	Coeficient = (int)(400 / GMax);
	for (int i = 0; i < 256; ++i)
	{
		pt2.y = pt1.y - histogram[i][1] * Coeficient;
		cv::line(dispMat, pt1, pt2, Scalar(0, 255, 0), 1, 8, 0);
		pt2.x = ++pt1.x;
	}
	//绘制B通道直方图
	Coeficient = (int)(400 / BMax);
	for (int i = 0; i < 256; ++i)
	{
		pt2.y = pt1.y - histogram[i][0] * Coeficient;
		cv::line(dispMat, pt1, pt2, Scalar(255, 0, 0), 1, 8, 0);
		pt2.x = ++pt1.x;
	}
	imshow("Histogram", dispMat);
}
int main()
{
	/*读入要计算直方图的图片
	 *注意这里要用“\\”
	 */
	cv::Mat src = imread("C:\\Users\\STAR ZHANG\\Pictures\\3.jpg");
	//调用函数
	Calc_histogram(src);
	/*WaitKey(n)是Opencv自带的函数
	 *其中n表示等待按键n毫秒后,关闭显示的窗口
	 *n为0或者WaitKey()(形参缺省)表示一直等待按键
	*/
	waitKey(0);
	return 0;
}

运行结果:
原图:
【Opencv入门】RGB三通道直方图的计算与绘制_第8张图片
分离出的RGB三通道直方图:
【Opencv入门】RGB三通道直方图的计算与绘制_第9张图片

总结

直方图在数字图像处理中一个非常重要的概念,具有广泛的应用。 以上作为课堂学习和课外练习的总结整理,部分资料出自老师的课件。 我永远热爱hdms lizhu老师!

你可能感兴趣的:(Computer,Vision,opencv,计算机视觉)