14.1 引言
直方图是对数据进行统计的一种方式。其实就是对值进行范围分段,统计每个值出现的频率情况。
图像直方图可以用来表示数字图像灰度的分布情况,是指对整个图像在灰度范围内的像素值出现频次的统计。此外,对图像的梯度、每个像素的角度等一切图像的属性值我们都可以建立直方图。可以直观地看出图像的灰度特征、梯度、方向等。如下图所示:
直方图最常见的几个属性:
- dims--表示维度,对灰度图像来说只有一个通道值dims=1
- bins--表示在维度中子区域大小的划分,bins=256,划分为256个级别
- range--表示值的范围,灰度值范围是0~255之间
14.2 直方图计算
14.2.1 相关API
void cv::split(const Mat &src, Mat *mvbegin)
参数 | 含义 | |
作用 | 将多通道图形分为多个单通道图像 | |
输入 | src | 输入图像 |
vbegin | 输出的多通道图像数组 | |
返回值 | void |
void cv::calcHist(const Mat *images, int nimages,const int* channels,InputArray mask,OutputArray hist, int dims,const int *histsize, const float* ranges,bool uniform, bool accumulate)
参数 | 含义 | |
作用 | 将多通道图形分为多个单通道图像 | |
输入 | images | 输入的图像的指针 |
nimages | 输入图像个数 | |
channels | 需要统计直方图的第几通道 | |
mask | 掩模,mask必须是一个8位(CV_8U)的数组并且和images的数组大小相同 | |
hist | 直方图计算的输出值 | |
dims | 输出直方图的维度(由channels指定); | |
histsize | 直方图中每个dims维度需要分成多少个区间(如果把直方图看作一个一个竖条的话,就是竖条的个数) | |
ranges | 统计像素值的区间 | |
uniform | 是否对得到的直方图数组进行归一化处理 | |
accumulate | 在多个图像时,是否累积计算像素值的个数 | |
返回值 | void |
14.2.2 代码示例
int main(int argc, char** argv)
{
Mat src = imread("D:\\testimg\\CSet12\\lena.png");
if (src.empty())
{
printf("Could not load the image...\n");
return -1;
}
else;
char input_win[] = "srcImg";
namedWindow(input_win, WINDOW_AUTOSIZE);
imshow(input_win, src);
//分离多通道图像;
vector<Mat> bgrImgs;
split(src, bgrImgs);
//imshow("B", bgrImgs[0]);
//定义直方图参数
int histsize = 256;
float range[] = { 0,256 };
const float *histrange = { range };
Mat bHist, gHist, rHist;
calcHist(&bgrImgs[0],1,0,Mat(),bHist,1,&histsize,&histrange,true,false);
calcHist(&bgrImgs[1], 1, 0, Mat(), gHist, 1, &histsize, &histrange, true, false);
calcHist(&bgrImgs[2], 1, 0, Mat(), rHist, 1, &histsize, &histrange, true, false);
//绘制直方图
int histh = 256;
int histw = 512;
int binw = histw / histsize;
Mat histImg(histh, histw,CV_8UC3,Scalar(0,0,0));
//灰度值归一化到画布高度
normalize(bHist, bHist,0, histh,NORM_MINMAX,-1,Mat());
normalize(gHist, gHist, 0, histh, NORM_MINMAX, -1, Mat());
normalize(rHist, rHist, 0, histh, NORM_MINMAX, -1, Mat());
for (int i = 1; i < histsize; i++)
{
line(histImg, Point((i - 1)*binw, histh - cvRound(bHist.at<float>(i - 1))), Point((i)*binw, histh - cvRound(bHist.at<float>(i))), Scalar(255, 0, 0), 2);
line(histImg, Point((i - 1)*binw, histh - cvRound(gHist.at<float>(i - 1))), Point((i)*binw, histh - cvRound(gHist.at<float>(i))), Scalar(0, 255, 0), 2);
line(histImg, Point((i - 1)*binw, histh - cvRound(rHist.at<float>(i - 1))), Point((i)*binw, histh - cvRound(rHist.at<float>(i))), Scalar(0, 0, 255), 2);
}
imshow("histImg", histImg);
waitKey(0);
return 0;
}
14.3直方图均衡化
直方图均衡化是一种提高图像对比度的方法,拉伸图像灰度值范围。可以将较少像素的灰度分配到别的灰度中去,像素相对集中的,放大器灰度变化范围,是对比度变大,有效增强图像。
14.3.1 实现方式
将图像的灰度分布映射到另一个分布上,得到映射后的像素值即可。
- 计算原图像的直方图H;
- 将柱状图归一化,是柱状图bin的总和为255;
- 计算直方图积分
- 使用H'作为查找表的转换图像:
14.3.2 相关API:equalizeHist
void cv::equalizeHist(InputArray src, OutputArray dst)
参数 | 含义 | |
作用 | 直方图均衡化,用于提高图像的质量 | |
输入 | src | 原图,8位单通道的图像 |
dst | 输出图像,大小与输入图像一致 | |
返回值 | void |
14.3.3 代码示例
//绘制直方图
void ShowHist(Mat &Hist,string sName)
{
int histw = 513;
int histh = 256;
int width = 2;
Mat histImage = Mat::zeros(histh, histw, CV_8UC3); //准备histImage为全黑背景色,大小为512*256
for (int i = 0; i < Hist.rows; i++)
{
rectangle(histImage, Point(width*(i), histh - 1), Point(width*(i + 1), histh - cvRound(Hist.at<float>(i) /4)),Scalar(255, 255, 255), -1);
}
namedWindow(sName, WINDOW_AUTOSIZE);
imshow(sName, histImage);
//waitKey(0);
}
int main(int argc, char** argv)
{
Mat src = imread("D:\\testimg\\CSet12\\lena.png");
if (src.empty())
{
printf("Could not load the image...\n");
return -1;
}
else;
char input_win[] = "srcImg";
namedWindow(input_win, WINDOW_AUTOSIZE);
imshow(input_win, src);
//转为灰度图
Mat grayImg;
cvtColor(src, grayImg,COLOR_BGR2GRAY);
//namedWindow("grayImg", WINDOW_AUTOSIZE);
imshow("grayImg", grayImg);
//原灰度图的直方图
const int channels[] = { 0 };
Mat srcHist;//定义输出Mat类型
int dims = 1;//设置直方图维度
const int histSize[] = { 256 }; //直方图每一个维度划分的柱条的数目//每一个维度取值范围
float pranges[] = { 0, 255 };//取值区间
const float* ranges[] = { pranges };
calcHist(&grayImg, 1, channels, Mat(), srcHist, dims, histSize, ranges, true,false);//计算直方图
ShowHist(srcHist, "srcHist");
//灰度均匀化
Mat equalizeImg;
equalizeHist(grayImg, equalizeImg);
imshow("equalizeImg", equalizeImg);
Mat equalizeHist;
calcHist(&equalizeImg, 1, channels, Mat(), equalizeHist, dims, histSize, ranges, true, false);//计算直方图
ShowHist(equalizeHist, "equalizeHist");
waitKey(0);
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/d069b4b9f91a8da469574d55ac84d1e3.png)
14.4 直方图比较
14.4.1 直方图比较方法
对输入的两张图像计算得到直方图H1和H2,归一化到相同的尺度空间,然后可以通过计算H1、H2之间的距离得到两个直方图的相似程度,进而比较图像本身的相似程度。opencv提供了四种比较方法:
- Correlation--相关性比较
- Chi-Square--卡方比较
- Intersection--十字交叉性
- Bhattacharyya distance--巴氏距离
(1)相关性计算(CV_COMP_CORREL)
其中 :
其中N是直方图的bin的个数,是均值。
(2)卡方计算(CV_COMP_CHISQR)
H1/H2分别表示两个图像的直方图数据。
(3)十字计算(CV_COMP_INTERSECT)
,H1/H2分别表示两个图像的直方图数据。
(4)巴氏距离计算(CV_COMP_BHATTACHARYYA)
;0-1相关性减小
14.4.2 图像直方图比较步骤
- 把图像从RGB色彩空间转换到HSV色彩空间(由于直方图对亮度和灰度图比较敏感,色彩空间转换就是突出这两个因素尽量去除其他因素)
- 计算图像直方图,然后归一化到[0~1]
- 进行直方图比较
14.4.3 相关API
double cv::compareHist(InputArray h1, InputArray h2,int method)
参数 | 含义 | |
作用 | 直方图比较 | |
输入 | h1 | 直方图1 |
h2 | 直方图2 | |
method | 比较方法 | |
返回值 | double | 相关性 |
14.4.4 代码示例
int main(int argc, char** argv)
{
Mat src = imread("D:\\testimg\\CSet12\\lena.png");
if (src.empty())
{
printf("Could not load the image...\n");
return -1;
}
else;
char input_win[] = "srcImg";
namedWindow(input_win, WINDOW_AUTOSIZE);
imshow(input_win, src);
//给原图加噪
Mat GaussianImg,medianImg;
GaussianBlur(src, GaussianImg,Size(3,3),0);
medianBlur(src,medianImg,3);
//转化到hsv空间;
Mat hsvImg;
cvtColor(src,hsvImg,COLOR_BGR2HSV);
cvtColor(GaussianImg, GaussianImg, COLOR_BGR2HSV);
cvtColor(medianImg, medianImg, COLOR_BGR2HSV);
//计算H和S两通道直方图参数并归一化
int hbins = 50; //H通道设置50
int sbins = 50; //s通道设置50
int histsize[] = { hbins ,sbins };
//h:0~179,s:0~255
float hrange[] = { 0,180 };
float srange[] = { 0,256 };
const float *histrange[] = { hrange,srange };
int channels[] = {0,1}; // 计算H和S两通道
MatND histhsv;
MatND histgaussian;
MatND histmedian;
calcHist(&hsvImg,1,channels,Mat(), histhsv,2,histsize,histrange,true,false);
normalize(histhsv, histhsv,0,1,NORM_MINMAX,-1,Mat());
calcHist(&GaussianImg, 1, channels, Mat(), histgaussian, 2, histsize, histrange, true, false);
normalize(histgaussian, histgaussian, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&medianImg, 1, channels, Mat(), histmedian, 2, histsize, histrange, true, false);
normalize(histmedian, histmedian, 0, 1, NORM_MINMAX, -1, Mat());
double compare1 = compareHist(histhsv, histhsv, 2);
double compare2 = compareHist(histhsv, histgaussian, 2);
double compare3 = compareHist(histhsv, histmedian, 2);
printf("src compare with src correlation value :%f\n", compare1);
printf("src compare with GaussianImg correlation value :%f\n", compare2);
printf("src compare with medianImg correlation value :%f\n", compare3);
waitKey(0);
return 0;
}
method:0
method:1
method:2
method:3
14.5 直方图反向投影
14.5.1 概述
反向投影是反映直方图模型在目标图像中的分布情况,简单点说计算某一特征的直方图模型,然后使用模型去寻找图像中存在的该特征的方法。通常用HSV色彩空间的HS两个通道直方图模型。
14.5.2 反向投影步骤
- 建立直方图模型;
- 计算待测图像直方图并映射到模型中;
- 从模型反向计算生成图像;
14.5.3 实现步骤和相关API
- 加载图像imread;
- 将图像从BGR色彩空间转换到HSV色彩空间cvtColor;
- 计算直方图并进行归一化处理calcHist、normalize;(Mat二维,MatND多维)
- 计算反向投影图像图像 calcBackProject
void cv::calcBackProject(const Mat *images,int nimages,const int *channels,InputArray hist,OutputArray backProject, const float **ranges,double scale=1,bool uniform=true)
参数 | 含义 | |
作用 | 直方图比较 | |
输入 | images | 输入图像指针,图像深度必须位CV_8U, CV_16U或CV_32F中的一种 |
nimages | 输入图像的数量 | |
channels | 通道列表 | |
hist | 输入的直方图 | |
backProject | 目标反向投影输出图像,单通道图像,与原图像有相同的尺寸和深度 | |
ranges | 直方图中每个维度bin的取值范围 | |
scale | 可选输出反向投影的比例因子 | |
uniform | 直方图是否均匀分布(uniform)的标识符,有默认值true | |
返回值 | double | 相关性 |
14.5.4 代码示例
Mat src;
Mat hsv, hue;
int bins = 12;
void HistBackproject(int, void*)
{
float range[] = {0,180};
const float *histRange = {range};
Mat h_hist;
calcHist(&hue,1,0,Mat(),h_hist,1,&bins,&histRange,true,false);
normalize(h_hist, h_hist,0,255,NORM_MINMAX,-1,Mat());
Mat backProject;
calcBackProject(&hue,1,0, h_hist, backProject, &histRange,1,true);
imshow("backproject", backProject);
}
int main(int argc, char** argv)
{
src = imread("D:\\testimg\\CSet12\\yingbi.png");
if (src.empty())
{
printf("Could not load the image...\n");
return -1;
}
else;
char input_win[] = "srcImg";
namedWindow(input_win, WINDOW_AUTOSIZE);
namedWindow("output_win", WINDOW_AUTOSIZE);
//转化到hsv空间;
cvtColor(src,hsv,COLOR_BGR2HSV);
hue.create(hsv.size(),hsv.depth());
int nchannels[] = {0,0};
//从hsv中将0,0通道拷贝到hue中
mixChannels(&hsv,1,&hue,1,nchannels,1);
createTrackbar("hist bins:", input_win,&bins,180, HistBackproject);
HistBackproject(0,0);
imshow(input_win, src);
waitKey(0);
return 0;
}