OpenCV ——直方图及其操作

直方图

       在统计学中,直方图是一种对数据分布情况的图形表示,是一种二维统计图表,他的两个坐标分别是统计样本(图像、视频帧)和样本的某种属性(亮度,像素值,梯度,方向,色彩等等任何特征)。

        也可以这么理解,直方图是对数据的统计,并把统计值显示到事先设定好的bin(矩形条)中,bin中的数值是从数据中计算出的特征的统计量。总之,直方图获取的是数据分布的统计图,通常直方图的维数要低于原始数据。

        图像直方图是用一表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素数。可以借助观察该直方图了解需要如何调整亮度分布的直方图。这种直方图中,横坐标的左侧为纯黑、较暗的区域,而右侧为较亮、纯白的区域。因此,一张较暗图片的图像直方图中的数据多集中于左侧和中间部分,而整体明亮、只有少量阴影的图像则相反。计算机视觉邻域常借助图像直方图来实现图像的二值化。

        灰度直方图是一幅图像中个像素灰度值出现次数或频数的统计结果,它只反映该图像中灰度值出现的频率,而未反映某一灰度值像素所在的位置。也就是说,它只包含了该图像中某个灰度值的像素出现的概率,而丢失了其所在的位置的信息。

       任一幅图像,都能唯一地算出一幅与它对应的直方图。但不同的图像,可能有相同的直方图。即图像与直方图之间是多对一的映射关系。

直方图意义: 
        1. 直方图是图像中像素强度分布的图形表达方式。 
        2. 直方图统计了每一个强度值所具有的像素个数。

       直方图广泛应用于许多计算机视觉应用中。通过标记帧和帧之间显著的边缘和颜色的统计变化,来检测视频中场景的变换。通过在每个兴趣点设置一个有相近特征的直方图所构成的标签,用以确定图像中的兴趣点。边缘、色彩、角度等直方图构成了可以被传递给目标识别分类器的一个通用特征类型。色彩和边缘的直方图还可以用来识别网络视频是否被复制等。直方图是计算机视觉中最经典的工具之一,也是一个很好的图像特征表示手段。

直方图术语: 
dims:需要统计的特征的数目。例如:dims=1,表示我们仅统计灰度值。 
bins:每个特征空间子区段的数目。 
range:每个特征空间的取值范围。

 

直方图均衡化

       直方图均衡化是通过拉伸像素强度的分布范围,使得在0~255灰阶上的分布更加均衡,提高了图像的对比度,达到改善图像主观视觉效果的目的。对比度较低的图像适合使用直方图均衡化方法来增强图像细节。

      API介绍

void equalizeHist(InputArray src, OutputArray dst)
//第一个参数,源图像,需为8位单通道图像
//第二个参数,输出图像,尺寸、类型和源图像一致

该函数采用如下步骤对输入图像进行直方图均衡化:

1. 计算输入图像的直方图H;
2. 进行直方图归一化,直方图的组距的和为255
3. 计算直方图积分:
4. 以H'作为查询表进行图像变换
       也就是把直方图的每个灰度级进行归一化处理,求每种灰度的累积分布,得到一个映射的灰度映射表,然后根据相应的灰度值来修正原图中的每个像素。
 

下面是一个示例:

#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>

using namespace std;
using namespace cv;

int main() {
	Mat src, dst;
	src = imread("tahiti.jpg");
	if (!src.data) {
		cout << "could not load image" << endl;
		return -1;
	}
	cvtColor(src, src, COLOR_BGR2GRAY);
	equalizeHist(src, dst);
	char input[] = "input image";
	char output[] = "output image";

	imshow(input, src);
	imshow(output, src);

	waitKey(0);
	return 0;
}

原图:

 直方图均衡化后:

直方图的计算与绘制

       API介绍

Void calcHist(
	        const Mat* images,//输入图像指针
	        int images,// 图像数目
	        const int* channels,// 通道数
	        InputArray mask,// 输入mask,可选,不用
	        OutputArray hist,//输出的直方图数据
	        int dims,// 维数
	        const int* histsize,// 直方图级数
	        const float* ranges,// 值域范围
	        bool uniform,// true by default
	        bool accumulate)// false by defaut
//寻找最值函数
void minMaxLoc(InputArray src, double* minVal, double* maxVal=0, 
                    Point* minLoc=0,Point* maxLoc=0,InputArray mask=noArray())
        //第一个参数:输入单通道阵列
        //第二个参数:返回最小值的指针,若无需返回,此值置为NULL
        //第三个参数:返回最大值的指针,若无需返回,此值置为NULL
        //第四个参数:返回最小位置的指针(二维情况下),若无需返回,此值置为NULL
        //第五个参数:返回最大位置的指针(二维情况下),若无需返回,此值置为NULL
        //第六个参数:用于选择子阵列的可选掩膜
//直方图的计算与绘制
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>

using namespace cv;
using namespace std;

const char* output = "histogram iamge";

int main(int argc, char* argv)
{
	Mat src, dst, dst1;
	src = imread("tahiti.jpg");
	if (!src.data)
	{
		printf("could not load image...\n");
		return -1;
	}
	char input[] = "input image";
	imshow(input, src);

	//分通道显示
	vector<Mat>bgr_planes;
	split(src, bgr_planes);
	//split(// 把多通道图像分为多个单通道图像 const Mat &src, //输入图像 Mat* mvbegin)// 输出的通道图像数组

	//计算直方图
	int histsize = 256;
	float range[] = { 0,256 };
	const float* histRanges = { range };
	Mat b_hist, g_hist, r_hist;
	calcHist(&bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histsize, &histRanges, true, false);
	calcHist(&bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histsize, &histRanges, true, false);
	calcHist(&bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histsize, &histRanges, true, false);


	//归一化
	int hist_h = 400;	//直方图的图像的高
	int hist_w = 512;	//直方图的图像的宽
	int bin_w = hist_w / histsize;//直方图的等级
	Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));//绘制直方图显示的图像

	normalize(b_hist, b_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
	normalize(g_hist, g_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
	normalize(r_hist, r_hist, 0, hist_h, NORM_MINMAX, -1, Mat());

	//绘制直方图
	for (int i = 1; i < histsize; i++)
	{
		//绘制蓝色分量直方图
		line(histImage, Point((i - 1) * bin_w, hist_h - cvRound(b_hist.at<float>(i - 1))),
			Point((i)*bin_w, hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, LINE_AA);
		//绘制绿色分量直方图
		line(histImage, Point((i - 1) * bin_w, hist_h - cvRound(g_hist.at<float>(i - 1))),
			Point((i)*bin_w, hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, LINE_AA);
		//绘制红色分量直方图
		line(histImage, Point((i - 1) * bin_w, hist_h - cvRound(r_hist.at<float>(i - 1))),
			Point((i)*bin_w, hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, LINE_AA);
	}
	imshow(output, histImage);
	waitKey(0);
	return 0;
}

直方图比较

直方图比较,是用一定的标准来判断两个直方图的相似度方法

       API介绍

//对输入的两张图像计算得到直方图H1与H2,归一化到相同的尺度空间然后可以通过计算H1与H2的之间的距离得到两个直方图的相似程度进而比较图像本身的相似程度。
//Opencv提供的比较方法有四种:Correlation 相关性比较   Chi - Square 卡方比较   Intersection 十字交叉性   Bhattacharyya distance 巴氏距离
//步骤:首先把图像从RGB色彩空间转换到HSV色彩空间cvtColor
//计算图像的直方图,然后归一化到[0~1]之间calcHist和normalize;
//使用上述四种比较方法之一进行比较compareHist

       比较方法

 

 注意:方法1和方法3的是当值越大时表示相似度越高

//直方图比较
#include <opencv2/imgproc/types_c.h>
#include <opencv2/imgproc/imgproc_c.h>
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace cv;
using namespace std;

string convertToString(double d);
int main(int argc, char* argv)
{
	Mat base, test1, test2;
	Mat hsvbase, hsvtest1, hsvtest2;
	base = imread("tahiti.jpg");
	test1 = imread("gou.jpg");
	test2 = imread("Hua.jpg");
	if (!base.data)
	{
		printf("could not load image...\n");
		return -1;
	}
	//步骤一:从RGB空间转换到HSV空间
	cvtColor(base, hsvbase, COLOR_BGR2HSV);
	cvtColor(test1, hsvtest1, COLOR_BGR2HSV);
	cvtColor(test2, hsvtest2, COLOR_BGR2HSV);

	//步骤二:计算直方图与归一化
	int h_bins = 50;
	int s_bins = 60;
	int histsize[] = { h_bins,s_bins };

	//hue varies from 0 to 179,saturation from 0 to 255
	float h_ranges[] = { 0,180 };
	float s_ranges[] = { 0,256 };
	const float* histRanges[] = { h_ranges,s_ranges };

	//use the 0-th and 1-st channels
	int channels[] = { 0,1 };
	MatND hist_base;
	MatND hist_test1;
	MatND hist_test2;

	//计算直方图
	calcHist(&hsvbase, 1, channels, Mat(), hist_base, 2, histsize, histRanges, true, false);
	calcHist(&hsvtest1, 1, channels, Mat(), hist_test1, 2, histsize, histRanges, true, false);
	calcHist(&hsvtest2, 1, channels, Mat(), hist_test2, 2, histsize, histRanges, true, false);

	//归一化
	normalize(hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat());//归一化
	normalize(hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat());
	normalize(hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat());

	//步骤三:比较直方图,并返回值
	double basebase = compareHist(hist_base, hist_base, CV_COMP_BHATTACHARYYA);//比较直方图
	double basetest1 = compareHist(hist_base, hist_test1, CV_COMP_BHATTACHARYYA);
	double basetest2 = compareHist(hist_base, hist_test2, CV_COMP_BHATTACHARYYA);
	double test1test2 = compareHist(hist_test1, hist_test2, CV_COMP_BHATTACHARYYA);
	printf("test1 with test2 correlation value :%f", test1test2);

	//在原图中显示相关性参数
	putText(base, convertToString(basebase), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
	putText(test1, convertToString(basetest1), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
	putText(test2, convertToString(basetest2), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
	putText(test2, convertToString(test1test2), Point(100, 100), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);

	imshow("base", base);
	imshow("test1", test1);
	imshow("test2", test2);

	waitKey(0);
	return 0;
}

//由于comparehist计算出来的相关性的值是一个double型,这个函数就是把double转变为string
string convertToString(double d)
{
	ostringstream os;
	if (os << d)
		return os.str();
	return "invalid conversion";
}

直方图反向投射

       反向投影是反映直方图模型在目标图像中的分布情况;简单点说就是用直方图模型去目标图像中寻找是否有相似的对象。通常用HSV色彩空间的HS两个通道直方图模型
步骤:

  1. 建立直方图模型
  2. 计算待测图像直方图并映射到模型中
  3. 从模型反向计算生成图像

  

     API介绍

void calcBackProject(const Mat* arrays, int narrays, const int* channels, InputArray hist, OutputArray backProject, const float** ranges, double scale=1, bool uniform=true )

// 参数解释
const Mat* arrays:输入图像,图像深度必须位CV_8U,CV_16U或CV_32F中的一种,尺寸相同,每一幅图像都可以有任意的通道数 

int narrays:输入图像的数量 

const int* channels:用于计算反向投影的通道列表,通道数必须与直方图维度相匹配,第一个数组的通道是从0到image[0].channels()-1,第二个数组通道从图像image[0].channels()到image[0].channels()+image[1].channels()-1计数 

InputArray hist:输入的直方图,直方图的bin可以是密集(dense)或稀疏(sparse) 

OutputArray backProject:目标反向投影输出图像,是一个单通道图像,与原图像有相同的尺寸和深度 

const float ranges**:直方图中每个维度bin的取值范围 

double scale=1:可选输出反向投影的比例因子 

bool uniform=true:直方图是否均匀分布(uniform)的标识符,有默认值true
void mixChannels(const Mat*src, size_t nsrcs, Mat* dst, size_t ndsts, const int* fromTo, size_t npairs)
//src– 输入矩阵,可以为一个也可以为多个,但是矩阵必须有相同的大小和深度.
    //nsrcs– 输入矩阵的个数。
    //dst– 输出矩阵,可以为一个也可以为多个,但是所有的矩阵必须事先分配空间(如用create),大小和深度须与输入矩阵等同.
    //ndsts– Number of matrices in dst.输出矩阵的个数。
    //fromTo –设置输入矩阵的通道对应输出矩阵的通道,规则如下:首先用数字标记输入矩阵的各个通道。输入矩阵个数可能多于一个并且每个矩阵的通道可能不一样,
    //第一个输入矩阵的通道标记范围为:0 ~src[0].channels() - 1,第二个输入矩阵的通道标记范围为:src[0].channels() ~src[0].channels() + src[1].channels() - 1,
    //以此类推;其次输出矩阵也用同样的规则标记,第一个输出矩阵的通道标记范围为:0 ~dst[0].channels() - 1,第二个输入矩阵的通道标记范围为:dst[0].channels()
    //~dst[0].channels() + dst[1].channels() - 1, 以此类推;最后,数组fromTo的第一个元素即fromTo[0]应该填入输入矩阵的某个通道标记,而fromTo的第二个元素即
    //fromTo[1]应该填入输出矩阵的某个通道标记,这样函数就会把输入矩阵的fromTo[0]通道里面的数据复制给输出矩阵的fromTo[1]通道。fromTo后面的元素也是这个
    //道理,总之就是一个输入矩阵的通道标记后面必须跟着个输出矩阵的通道标记。
    //npairs– Number of index pairs in fromTo.即参数fromTo中的有几组输入输出通道关系,其实就是参数fromTo的数组元素个数除以2.

下面是一个示例:

//直方图的反向投影
#include<Windows.h>
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>

using namespace std;
using namespace cv;
Mat src, hsv_src;
Mat hue;
int bins = 12;
void Hist_And_Backprojection(int, void*);
int main(int argc, char* argv)
{
	src = imread("tahiti.jpg");
	if (!src.data)
	{
		printf("could not load image...\n");
		return -1;
	}
	//将图像从RGB色彩空间转换到HSV色彩空间
	cvtColor(src, hsv_src, COLOR_BGR2HSV);

	hue.create(hsv_src.size(), hsv_src.depth());
	int nchannels[] = { 0,0 };

	//mixChannels主要就是把输入的矩阵(或矩阵数组)的某些通道拆分复制给对应的输出矩阵(或矩阵数组)的某些通道中,其中的对应关系就由fromTo参数制定.
	mixChannels(&hsv_src, 1, &hue, 1, nchannels, 1);
	Hist_And_Backprojection(0, 0);
	createTrackbar("Histogram Bins:", "input", &bins, 180, Hist_And_Backprojection);
	

	imshow("input", src);
	waitKey(0);
	return 0;
}

void Hist_And_Backprojection(int, void*)
{
	//计算直方图
	float range[] = { 0,180 };
	const float* histRanges = { range };
	Mat h_hist;
	calcHist(&hue, 1, 0, Mat(), h_hist, 1, &bins, &histRanges, true, false);
	//归一化
	normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());

	//计算反向投影图像 - calcBackProject
	Mat backProjectIamge;
	calcBackProject(&hue, 1, 0, h_hist, backProjectIamge, &histRanges, 1, true);

	imshow("BackProjectIamge", backProjectIamge);

	//画直方图
	int hist_h = 400;
	int hist_w = 400;
	int bin_w = (hist_w / bins);
	Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
	for (size_t i = 1; i < bins; i++)
	{
		rectangle(histImage,
			Point((i - 1) * bin_w, (hist_h - cvRound(h_hist.at<float>(i - 1) * (400 / 255)))),
			Point(i * bin_w, (hist_h - cvRound(h_hist.at<float>(i) * (400 / 255)))),
			Scalar(0, 0, 255), 2, LINE_AA);
	}
	imshow("Histogram", histImage);
	return;
}

 

 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DDsoup

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值