图像处理基础(8):图像的灰度直⽅图、直⽅图均衡化、直⽅图规定化(匹
配)本⽂主要介绍了灰度直⽅图相关的处理,包括以下⼏个⽅⾯的内容:
利⽤OpenCV 计算图像的灰度直⽅图,并绘制直⽅图曲线直⽅图均衡化的原理及实现
直⽅图规定化(匹配)的原理及实现
图像的灰度直⽅图
⼀幅图像由不同灰度值的像素组成,图像中灰度的分布情况是该图像的⼀个重要特征。图像的灰度直⽅图就描述了图像中灰度分布情况,能够很直观的展⽰出图像中各个灰度级所占的多少。图像的灰度直⽅图是灰度级的函数,描述的是图像中具有该灰度级的像素的个数:其中,横坐标是灰度级,纵坐标是该灰度级出现的频率。
不过通常会将纵坐标归⼀化到[0,1]区间内,也就是将灰度级出现的频率(像素个数)除以图像中像素的总数。灰度直⽅图的计算公式如下:
p (r k )=n k
MN
其中,r k 是像素的灰度级,n k 是具有灰度r k 的像素的个数,MN 是图像中总的像素个数。
OpenCV 灰度直⽅图的计算
直⽅图的计算是很简单的,⽆⾮是遍历图像的像素,统计每个灰度级的个数。在OpenCV 中封装了直⽅图的计算函数calcHist ,为了更为通⽤该函数的参数有些复杂,其声明如下:
void calcHist( const Mat* images, int nimages,
const int* channels, InputArray mask,
儿童急性荨麻疹
OutputArray hist, int dims, const int* histSize,
const float** ranges, bool uniform = true, bool accumulate = fal );
该函数能够同时计算多个图像,多个通道,不同灰度范围的灰度直⽅图.其参数如下:
images ,输⼊图像的数组,这些图像要有相同⼤⼤⼩,相同的深度(CV_8U CV_16U CV_32F ).
nimages ,输⼊图像的个数
channels ,要计算直⽅图的通道个数。
mask ,可选的掩码,不使⽤时可设为空。要和输⼊图像具有相同的⼤⼩,在进⾏直⽅图计算的时候,只会统计该掩码不为0的对应像素
hist ,输出的直⽅图
dims ,直⽅图的维度
histSize ,直⽅图每个维度的⼤⼩
ranges ,直⽅图每个维度要统计的灰度级的范围
uniform ,是否进⾏归⼀化,默认为true
accumulate ,累积标志,默认值为fal 。
为了计算的灵活性和通⽤性,OpenCV 的灰度直⽅图提供了较多的参数,但对于只是简单的计算⼀幅灰度图的直⽅图的话,⼜显得较为累赘。这⾥对calcHist 进⾏⼀次封装,能够⽅便的得到⼀幅灰度图直⽅图。
class Histogram1D
{
private:
int histSize[1]; // 项的数量
float hranges[2]; // 统计像素的最⼤值和最⼩值
const float* ranges[1];
int channels[1]; // 仅计算⼀个通道
public:
Histogram1D()
{
// 准备1D 直⽅图的参数
histSize[0] = 256;
hranges[0] = 0.0f;
hranges[1] = 255.0f;
ranges[0] = hranges;
channels[0] = 0;
}
MatND getHistogram(const Mat &image)
{
MatND hist;
/
/ 计算直⽅图
calcHist(&image ,// 要计算图像的诚信的格言
1, // 只计算⼀幅图像的直⽅图
channels, // 通道数量
Mat(), // 不使⽤掩码
hist, // 存放直⽅图
1, // 1D 直⽅图
histSize, // 统计的灰度的个数
ranges); // 灰度值的范围
return hist;
}
Mat getHistogramImage(const Mat &image)
{
MatND hist = getHistogram(image);
// 最⼤值,最⼩值
double maxVal = 0.0f;
double minVal = 0.0f;
minMaxLoc(hist, &minVal, &maxVal);
//显⽰直⽅图的图像
Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));
// 设置最⾼点为nbins 的90%
int hpt = static_cast<int>(0.9 * histSize[0]);
/
/每个条⽬绘制⼀条垂直线
for (int h = 0; h < histSize[0]; h++)
{
float binVal = hist.at<float>(h);
int intensity = static_cast<int>(binVal * hpt / maxVal);
// 两点之间绘制⼀条直线
line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0));
}
return histImg;
}
};
Histogram1D 提供了两个⽅法:getHistogram 返回统计直⽅图的数组,默认计算的灰度范围是[0,255];getHistogramImage 将图像的直⽅图以线条的形式画出来,并返回包含直⽅图的图像。测试代码如下:
Histogram1D hist;
Mat histImg;
histImg = HistogramImage(image);
imshow("Image", image);
imshow("Histogram", histImg);其结果如下:
直⽅图均衡化 Histogram Equalization
假如图像的灰度分布不均匀,其灰度分布集中在较窄的范围内,使图像的细节不够清晰,对⽐度较低。通常采⽤直⽅图均衡化及直⽅图规定化两种变换,使图像的灰度范围拉开或使灰度均匀分布,从⽽增⼤反差,使图像细节清晰,以达到增强的⽬的。
直⽅图均衡化,对图像进⾏⾮线性拉伸,重新分配图像的灰度值,使⼀定范围内图像的灰度值⼤致相等。这样,原来直⽅图中间的峰值部分对⽐度得到增强,⽽两侧的⾕底部分对⽐度降低,输出图像的直⽅图是⼀个较为平坦的直⽅图。
均衡化算法
直⽅图的均衡化实际也是⼀种灰度的变换过程,将当前的灰度分布通过⼀个变换函数,变换为范围更宽、灰度分布更均匀的图像。也就是将原图像的直⽅图修改为在整个灰度区间内⼤致均匀分布,因此扩⼤了图像的动态范围,增强图像的对⽐度。通常均衡化选择的变换函数是灰度的累积概率,直⽅图均衡化算法的步骤:
计算原图像的灰度直⽅图 P (S k )=n k
n ,其中n 为像素总数,n k 为灰度级S k 的像素个数
计算原始图像的累积直⽅图 CDF (S k )=k ∑i =0n i n =k
∑i =0P s (S i )
D j =L ⋅CDF (S i ),其中 D j 是⽬的图像的像素,CDF (S i )是源图像灰度为i 的累积分布,L 是图像中最⼤灰度级(灰度图为255)其代码实现如下:在上⾯中封装了求灰度直⽅图的类,这⾥直接应⽤该⽅法得到图像的灰度直⽅图;将灰度直⽅图进⾏归⼀化,计算灰度的累积概率;创建灰度变化的查找表
应⽤查找表,将原图像变换为灰度均衡的图像
具体代码如下:
void equalization_lf(const Mat &src, Mat &dst)
{
Histogram1D hist1D;
MatND hist = Histogram(src);
hist /= (ws * ls); // 对得到的灰度直⽅图进⾏归⼀化
float cdf[256] = { 0 }; // 灰度的累积概率
中国邮政储蓄
Mat lut(1, 256, CV_8U); // 灰度变换的查找表
for (int i = 0; i < 256; i++)
{
// 计算灰度级的累积概率
if (i == 0)
cdf[i] = hist.at<float>(i);
el
圣愈汤cdf[i] = cdf[i - 1] + hist.at<float>(i);
lut.at<uchar>(i) = static_cast<uchar>(255 * cdf[i]); // 创建灰度的查找表
枸杞干吃}
LUT(src, lut, dst); // 应⽤查找表,进⾏灰度变化,得到均衡化后的图像
}
上⾯代码只是加深下对均衡化算法流程的理解,实际在OpenCV 中也提供了灰度均衡化的函数equalizeHist ,该函数的使⽤很简单,只有两个参数:输⼊图像,输出图像。下图为,上述代码计算得到的均衡化结果和调⽤equalizeHist 的结果对⽐
最左边为原图像,中间为OpenCV 封装函数的结果,右边为上⾯代码得到的结果。
参考分数线>考研心得直⽅图规定化
从上⾯可以看出,直⽅图的均衡化⾃动的确定了变换函数,可以很⽅便的得到变换后的图像,但是在有些应⽤中这种⾃动的增强并不是最好的⽅法。有时候,需要图像具有某⼀特定的直⽅图形状(也就是灰度分布),⽽不是均匀分布的直⽅图,这时候可以使⽤直⽅图规定化。
直⽅图规定化,也叫做直⽅图匹配,⽤于将图像变换为某⼀特定的灰度分布,也就是其⽬的的灰度直⽅图是已知的。这其实和均衡化很类似,均衡化后的灰度直⽅图也是已知的,是⼀个均匀分布的直⽅图;⽽规定化后的直⽅图可以随意的指定,也就是在执⾏规定化操作时,⾸先要知道变换后的灰度直⽅图,这样才能确定变换函数。规定化操作能够有⽬的的增强某个灰度区间,相⽐于,均衡化操作,规定化多了⼀个输⼊,但是其变换后的结果也更灵活。在理解了上述的均衡化过程后,直⽅图的规定化也较为简单。可以利⽤均衡化后的直⽅图作为⼀个中间过程,然后求取规定化的变换函数。具体步骤如下:
将原始图像的灰度直⽅图进⾏均衡化,得到⼀个变换函数s =T (r ),其中s 是均衡化后的像素,r 是原始像素
菜芯对规定的直⽅图进⾏均衡化,得到⼀个变换函数v =G (z ),其中v 是均衡化后的像素,z 是规定化的像素
上⾯都是对同⼀图像的均衡化,其结果应该是相等的,s =v ,且z =G −1(v )=G −1(T (r ))
通过,均衡化作为中间结果,将得到原始像素r 和z 规定化后像素之间的映射关系。
详解规定化过程
对图像进⾏直⽅图规定化操作,原始图像的直⽅图和以及规定化后的直⽅图是已知的。假设P r (r )表⽰原始图像的灰度概率密度,P z (z )表⽰规定化图像的灰度概率密度(r 和z 分别是原始图像的灰度级,规定化后图像的灰度级)。
对原始图像进⾏均衡化操作,则有s k =T (r k )=L ⋅i =k
∑
i =0P r (r k )对规定化的直⽅图进⾏均衡化操作,则v k =G (z m )=L ⋅j =m ∑
j =0P z (z m )
由于是对同⼀图像的均衡化操作,所以有s k =v m 。
规定化操作的⽬的就是找到原始图像的像素s k 到规定化后图像像素的z k 之间的⼀个映射。有了上⼀步的等式后,可以得到s k =G (z k ),因此要想找到s k 想对应的z k 只需要在z 进⾏迭代,找到使式⼦G (z m )−s k 的绝对值最⼩即可。
上述描述只是理论的推导过程,在实际的计算过程中,不需要做两次的均衡化操作,具体的推导过程如下:$$
s k =v k L ⋅i =k ∑i =0P r (r k )=L ⋅j =m ∑j =0P z (z m ) i =k ∑i =0P r (r k )=j =m
∑j =0P z (z m )
以$k = 2$为例,
- 直⽅图的均衡化的是将⼀幅图像的直⽅图变平,使各个灰度级的趋于均匀分布,这样能够很好的增强图像对⽐度。直⽅图均衡化是- 直⽅图规定化,也称为直⽅图匹配,经过规定化处理将原图像的直⽅图变换为特定形状的直⽅图(上⾯中的⽰例,就是将图像的直⽅图变换为另⼀幅图像的直⽅图)。它可以按照预先设定的某个形状来Processing math: 100%