前言
什么是直方图?
直方图是对数据的集合做统计,并将统计结果分布于一系列预定义的bins中。
例如:
如果我按照某种方式去统计通道中的这些数字,会发生什么情况呢?既然以知数字的范围是包含256个色值(这里是8bit无符号四通道图像 2^8 = 256),我们可以将这个范围分割成子区域(称作 bins), 如:
[0, 255] = { [0, 15] U [16, 31]U ....U[240,255] }
range = { bin_{1} U bin_{2} U ....U bin_{n = 15} }
然后在统计掉入每个bin中的像素数目。采用这一方法来统计上面的数字矩阵,我们可以得到下图:(x轴表示bin,y轴表示各个bin中的像素个数)。
以上只是一个说明直方图如何工作以及它的用处的简单示例。直方图可以统计的不仅仅是颜色灰度, 它可以统计任何图像特征 (如 梯度, 方向等等)。
让我们再来搞清楚直方图的一些具体细节:
dims: 需要统计的特征的数目, 在上例中, dims = 1 因为我们仅仅统计了灰度值(灰度图像)。
bins: 每个特征空间 子区段 的数目,在上例中, bins = 16
range: 每个特征空间的取值范围,在上例中, range = [0,255]
怎样去统计两个特征呢? 在这种情况下, 直方图就是3维的了,x轴和y轴分别代表一个特征, z轴是掉入 (bin_{x}, bin_{y}) 组合中的样本数目。 同样的方法适用于更高维的情形 (当然会变得很复杂。
OpenCV的直方图计算
OpenCV提供了一个简单的计算数组集(通常是图像或分割后的通道)的直方图函数 calcHist 。 支持高达 32 维的直方图。下面的代码演示了如何使用该函数计算直方图!
基本的逻辑就是将一张图像通道化,计算各通道图像的直方图并返回显示。
首先将UIImage对象转成OpenCV Mat 数据结构:
Mat img = [FaceDetection CVMatFromUIImage:image];//这段代码OpenCV已经写好了
然后我们需要将Mat图像数据通道化:
cv::split(img, img_channels);
CV_EXPORTS void split(const Mat& src, Mat* mvbegin);
@param src input multi-channel array.
@param mvbegin output array; the number of arrays must match src.channels(); the arrays themselves arereallocated, if needed.
意思就是传一个多通道图像,传出一个装载各通道的数组。
接着就需要通过直方图CvHistogram类来处理每一个通道数据。
CVAPI(CvHistogram*) cvCreateHist( int dims, int* sizes, int type,float** ranges CV_DEFAULT(NULL),int uniform CV_DEFAULT(1));API是有这个函数😓
@param dims Number of histogram dimensions.这参数前言中已经提到过。
@param sizes Array of the histogram dimension sizes. 就相当于 bins
@param type Histogram representation format. CV_HIST_ARRAY means that the histogram data is represented as a multi-dimensional dense array CvMatND. CV_HIST_SPARSE means that histogram data is represented as a multi-dimensional sparse array CvSparseMat.直方图疏密程度
@param ranges Array of ranges for the histogram bins. Its meaning depends on the uniform parameter value. The ranges are used when the histogram is calculated or backprojected to determine which .
@param uniform.....这里看都不用看了😅因为通过我的实验OpenCV 3.2里只有下边这个函数
那我说如果我几就需要用Range uniform这些呢?
CVAPI(void) cvSetHistBinRanges( CvHistogram* hist, float** ranges,int uniform CV_DEFAULT(1));
cvSetHistBinRanges(CvHistogram *hist, float **ranges)
又骗我?😄。我感觉它这些函数有规律的,只要有CV_DEFAULT的这些参数的,就不需要填,它知道你也填DEFAULT,就帮你填好了,当然你填了就调用的你填的参数,很强!
接下来就需要调用Histogram类的计算直方图方法:
CVAPI(void) cvCalcArrHist( CvArr** arr, CvHistogram* hist,int accumulate CV_DEFAULT(0),const CvArr* mask CV_DEFAULT(NULL) );/** @overload */
CV_INLINE void cvCalcHist( IplImage** image, CvHistogram* histint accumulate CV_DEFAULT(0),const CvArr* mask CV_DEFAULT(NULL) ){
cvCalcArrHist( (CvArr**)image, hist, accumulate, mask );
}
给了一个重载方法。我们用重载函数就好了。需要说的是 CvArr,IplImage,CvMat,其实是一个东西,它自己没说,但确实有人说了😄:
§ CvArr -This is the "metatype" used only as a function parameter.
It denotes that the function accepts arrays of multiple types, such as IplImage*, CvMat* or even CvSeq* sometimes. The particular array type is determined at runtime by analyzing the first 4 bytes of the header. In C++ interface the role of CvArr is played by InputArray and OutputArray.
我们就可以这样想:到这里channel里的图像数据已经被Histogram做好Collection了,现在需要做的就是拿出bins绘制图像。需要注意的是,在IplImage结构体中有这样一个属性int origin.
int origin; /**< 0 - top-left origin,1 - bottom-left origin (Windows bitmaps style). */我是这样理解的:你给我的图片有可能是从下边开始计算的,不一定所有的IplImage都是从左上角开始计算的。
然后就比如我现在创建一个画布:
IplImage *image = cvCreateImage(cvSize(256, 80), 8, 1);cvSetZero(image);
获取bins开始绘图。
float histValue = cvGetReal1D(hist->bins, i);
float histNextVable = cvGetReal1D(hist->bins, i+1);
这个画出来的图像如果使用cvFillConvexPoly填充的时候就是蓝色线条上半部分。
cvFillConvexPoly(image, points, numPts, cvScalar(255));
那画下半部分就很简单了,将得到第i个点 使用size Max y - i.bins 就是相反的图像么。😄
OpenCV的直方图均衡化
直方图均衡化处理的“中心思想”是把原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度范围内的均匀分布。
使用起来很简单
那么效果怎样呢?
基本逻辑:将一张图像均衡化,然后输出显示。
po:我突然发现,只要代码思路清晰,写起代码很舒服😌。
OpenCV的直方图对比
其实对于直方图对比,就是按照enum cv::HistCompMethods标准取相似度。拿出待比对的bins与基准图像的bins求交集嘛😄。
就单单两张相同的图片,如果调整一下亮度,相似度就下来了。
需要注意的是相关性不能证明因果关系,尽管它可能符合因果关系。下面就介绍一下这几个Methods👇。
HISTCMP_CORREL
Correlation (co-relation) refers to the degree of relationship (or dependency) between two variables.
Linear correlation refers to straight-line relationships between two variables.
A correlation can range between -1 (perfect negative relationship) and +1 (perfect positive relationship), with 0 indicating no straight-line relationship. -出处 学术文档看起来总是很清晰。
相关系数是用以反映变量之间相关关系密切程度的统计指标.
👇解释一下吧:
HISTCMP_CHISQR
A Pearson's chi-square test can be used as an inferential test of the independence of two nominal variables.出处 这个屌,尤其是对例子的解析👇。
用来监测实际观测值与理论推断值之间的偏离程度。其根本思想就是在于比较理论频数和实际频数的吻合程度或拟合优度问题。
If you wanted to test a die to see whether it’s a fair die, what would you do?
Class Answers: Roll it over and over and see what kind of distribution you get
此处很经典,我叫我王牌翻译来翻译一下:
王牌翻译说:如果你想要测试一个死之人是否害怕死亡你会怎么做?😄一遍一遍去实验。
我觉得我得把文档中的这个example用我的语言Roll一次。
如果你要推测一场死亡属于正常死亡,你会怎么做?让他再活一次?
假设我们使他复活了60次,那你会用什么样的理由去接受其中的一次死亡属于正常死亡?对数据分组 1/6 ?取其中的10次?很显然这个方法很愚蠢。本来60次就不怎么可靠,然后你再去随机取其中的10次?
👇解释一下吧:
我觉得我解释明白了,如果你还没有看明白,点这里吧。
HISTCMP_INTERSECT
这个就不用说了,单单就这个英文单词就能解释:交集么。
HISTCMP_BHATTACHARYYA
在统计中,Bhattacharyya距离测量两个离散或连续概率分布的相似性。它与衡量两个统计样品或种群之间的重叠量的Bhattacharyya系数密切相关。原文点这里
HISTCMP_HELLINGER
在概率论和统计理论中,Hellinger距离被用来度量两个概率分布的相似度。它是f散度的一种(f散度——度量两个概率分布相似度的指标)
总之,这些属性都是来判断两组数据的相关性。
总结
那么图像直方图有什么用?其实我也是百度了好多的文章,说的意思很多。
对图像进行分析、观察,形成一个有效的处理方法
就我的理解:
1.对于纯色图像来说,在直方图中只显示一条柱状,一个bins。
2.对于图像来说,如果图像的峰值偏左,bins 集合会偏向0;相反,如果图像的峰值偏右,图像bins集合偏向255;
3.直方图具有二峰性,这表明这个图像有较亮和较暗的区域。
最重要的一点是这个可以做特征提取。
我感觉检测某个图像中: 是真实的人脸、还是照片中的人脸,判断背景区域的直方图就好了。如果拿着照片去充当人脸,那么直方图中会出现多个峰值。
对于前景图像和背景图像比较接近的图像可以使用直方图均衡化,修改对比度。