1,直方图均衡化 (Histogram Equalization)
假如图像的灰度分布不均匀,其灰度分布集中在较窄的范围内,使图像的细节不够清晰,对比度较低。直方图均衡化,对图像进行非线性拉伸,重新分配图像的灰度值,使一定范围内图像的灰度值大致相等。这样,原来直方图中间的峰值部分对比度得到增强,而两侧的谷底部分对比度降低,输出图像的直方图是一个较为平坦的直方图。使图像的灰度范围拉开或使灰度均匀分布,从而增大反差,使图像细节清晰,以达到增强的目的。
均衡化的基本思想是:尽量使得每个灰度级的像素数量相等。直方图均衡化的另一个优势是:不需要额外参数,整个过程是自动的;直方图均衡化的缺点是:拉伸后有些灰度级可能不被映射到,造成图像观感上的颗粒感
均衡化算法
直方图的均衡化实际也是一种灰度的变换过程,将当前的灰度分布通过一个变换函数,变换为范围更宽、灰度分布更均匀的图像。也就是将原图像的直方图修改为在整个灰度区间内大致均匀分布,因此扩大了图像的动态范围,增强图像的对比度。通常均衡化选择的变换函数是灰度的累积概率,直方图均衡化算法的步骤:
(1)计算原图像的灰度直方图 P(Sk)=nk/n,其中n为像素总数,nk为灰度级Sk的像素个数
(2)计算原始图像的累积直方图 CDF(Sk)=∑ni/n=∑Ps(Si)
(3)Dj=L⋅CDF(Si),其中 Dj是目的图像的像素,CDF(Si)是源图像灰度为i的累积分布,L是图像中最大灰度级(灰度图为255)
具体代码如下:
void equalization_self(const Mat &src, Mat &dst)
{
Histogram1D hist1D;
MatND hist = hist1D.getHistogram(src);
hist /= (src.rows * src.cols); // 对得到的灰度直方图进行归一化
float cdf[256] = { 0 }; // 灰度的累积概率
Mat lut(1, 256, CV_8U); // 灰度变换的查找表
for (int i = 0; i < 256; i++)
{
// 计算灰度级的累积概率
if (i == 0)
cdf[i] = hist.at(i);
else
cdf[i] = cdf[i - 1] + hist.at(i);
lut.at(i) = static_cast(255 * cdf[i]); // 创建灰度的查找表
}
LUT(src, lut, dst); // 应用查找表,进行灰度变化,得到均衡化后的图像
}
上面代码只是加深下对均衡化算法流程的理解,实际在OpenCV中也提供了灰度均衡化的函数。
全局直方图均衡化 源代码示例:
import cv2 as cv
import numpy as np
def equalHist_demo(image): #直方图均衡化,自动调整图像对比度,是图像增强的一种手段
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)#首先转化为灰度图
dst = cv.equalizeHist(gray)
cv.imshow("equalHist_demo", dst)
src = cv.imread("F:/images/rice.png")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
equalHist_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
运行结果:
全局直方图均衡后背景对比度有所改善。但导致亮度过高,我们可能会丢失了大部分信息。这是因为它的直方图并不局限于特定区域。
2,自适应直方图均衡化(Adaptive histgram equalization/AHE)
自适应直方图均衡化(AHE)用来提升图像的对比度的一种计算机图像处理技术。和普通的直方图均衡算法不同,AHE算法通过计算图像的局部直方图,然后重新分布亮度来改变图像对比度。因此,该算法更适合于改进图像的局部对比度以及获得更多的图像细节。
图像被分成称为“图块”的小块(在OpenCV中,tileSize默认为8x8)。然后像往常一样对这些块中的每一个进行直方图均衡。所以在一个小区域内,直方图会限制在一个小区域(除非有噪音)。如果有噪音,它会被放大。为避免这种情况,应用对比度限制。如果任何直方图区间高于指定的对比度限制(在OpenCV中默认为40),则在应用直方图均衡之前,将这些像素剪切并均匀分布到其他区间。均衡后,为了去除图块边框中的瑕疵,应用双线性插值。
算法思想:
移动模板W在图像A上逐行移动,并且模板W的中心c(x0,y0)对应图像上的点f(x0,y0);计算模板W区域的直方图均衡化变化关系:g(x,y)= T(f(x,y),计算模板中心点c(x0,y0)的均衡化对应像素值:g(x0,y0) = T(f(x0,y0))。用g(x0,y0)替代f(x0,y0);逐行计算得到整幅图像的自适应直方图均衡化图像。
就是在一个给定框内做直方图均衡化,而框内所有处理(包括均衡化)只为了这个将原始中心点像素a变化到新的像素点b。
代码实现:
function [ output_img ] = my_AHE( input_img, w)
%UNTITLED Summary of this function goes here
% Detailed explanation goes here
% 自适应直方图均衡化
% 输入:input_img:待处理图像
% w: 局部处理的窗口大小
[height,width,channels]=size(input_img);
if channels==1
src = inpit_img;
else
src = rgb2gray(input_img);
end
%1.图像边界扩展
padsize=[(w-1)/2,(w-1)/2];
padSrc = padarray(src,padsize,'symmetric','both');
%2.循环求解每个区域对应的值
output_img = zeros(height,width);
iter = 0;
for i=1:height
for j=1:width
slideWindow = zeros(w,w);
slideWindow = padSrc(i:i+w-1,j:j+w-1);
AHE_piexl = my_AHE_piexl(slideWindow,src(i,j));
output_img(i,j) = AHE_piexl;
iter = iter+1;
disp(iter);
end
end
output_img = uint8(output_img);
end
function [ outPiexl ] = my_AHE_piexl( window,inPiexl )
%UNTITLED2 Summary of this function goes here
% Detailed explanation goes here
% 计算局部图像的直方图均衡化的像素对应值
% 输入:window: 局部图像
% inPiexl:输入像素
% outPiexl:输出像素
[height,width]=size(window);
%1.像素灰度统计
NumPixel = zeros(1,256);
for i=1:height
for j=1:width
grayValue = window(i,j);
NumPixel(1,grayValue+1) = NumPixel(1,grayValue+1)+1;
end
end
%2.计算灰度分布密度
ProbPixel = zeros(1,256);
for k=1:256
ProbPixel(1,k)=NumPixel(1,k)/(height*width*1.0);
end
%3.计算累积分布密度
CumuPixel = zeros(1,256);
CumuPixel(1,1) = ProbPixel(1,1);
for l=2:256
CumuPixel(1,l) = CumuPixel(1,l-1)+ProbPixel(1,l);
end
CumuPixel = uint8(255 .* CumuPixel + 0.5); %取整数
%4.计算灰度值映射
outPiexl = CumuPixel(inPiexl);
end
同样,上面代码只是加深下对均衡化算法流程的理解,实际在OpenCV中也提供了灰度均衡化的函数。
源代码示例:
import cv2 as cv
import numpy as np
def clahe_demo(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
clahe = cv.createCLAHE(clipLimit=5.0, tileGridSize=(8, 8))
dst = clahe.apply(gray)
cv.imshow("clahe_demo", dst)
src = cv.imread("F:/images/rice.png")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
clahe_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
运行结果:
3,直方图比较
对输入的两张图像进行直方图均衡化及直方图计算步骤后,可以对两个图像的直方图进行对比,并通过对比的结果得到一些我们想要的结论。
直方图比较应用
(1)图像相似度比较
如果我们有两张图像,并且这两张图像的直方图一样,或者有极高的相似度,那么在一定程度上,我们可以认为这两幅图是一样的,这就是直方图比较的应用之一。
(2)分析图像之间关系
两张图像的直方图反映了该图像像素的分布情况,可以利用图像的直方图,来分析两张图像的关系。
直方图比较方法
要比较两个直方图(H1 和 H2),首先必须要选择一个衡量直方图相似度的对比标准,我们设为d(H1,H2)
6.API介绍——compareHist
(1)步骤
a.先用cvtColor()把图像从RGB色彩空间转换到HSV色彩空间;
b.计算图像的直方图,然后归一化到[0~1]之间,用到函数 calcHist() 和 normalize() ;
c.使用上述的四种方法之一进行比较,用到函数compareHist()。
(2)API介绍
函数一共有三个参数,一个输入图像,一个输出图像,一个比较方法。比较方法的取值的情况为上面的四种方法,在OpenCV中,每个都有自己的名字:Correlation ( CV_COMP_CORREL );Chi-Square ( CV_COMP_CHISQR );Intersection ( CV_COMP_INTERSECT );Bhattacharyya 距离( CV_COMP_BHATTACHARYYA )。
源代码示例:
import cv2 as cv
import numpy as np
def create_rgb_hist(image):
h, w, c = image.shape
rgbHist = np.zeros([16*16*16, 1], np.float32) #需要是 np.float32类型
bsize = 256 / 1
for row in range(h):
for col in range(w):
b = image[row, col, 0]
g = image[row, col, 1]
r = image[row, col, 2]
index = np.int(b/bsize)*16*16 + np.int(g/bsize)*16 + np.int(r/bsize)
rgbHist[np.int(index), 0] = rgbHist[np.int(index), 0] + 1
return rgbHist
def hist_compare(image1, image2):
hist1 = create_rgb_hist(image1)
hist2 = create_rgb_hist(image2)
match1 = cv.compareHist(hist1, hist2, cv.HISTCMP_BHATTACHARYYA)#巴氏距离,越小越相似
match2 = cv.compareHist(hist1, hist2, cv.HISTCMP_CORREL)#相关性,越大越相似
match3 = cv.compareHist(hist1, hist2, cv.HISTCMP_CHISQR)#卡方,越小越相似
print("巴氏距离: %s, 相关性: %s, 卡方: %s"%(match1, match2, match3))
image1 = cv.imread("F:/images/lena.png")
image2 = cv.imread("F:/images/lenanoise.png")
cv.imshow("image1", image1)
cv.imshow("image2", image2)
hist_compare(image1, image2)
cv.waitKey(0)
cv.destroyAllWindows()
运行结果:
巴氏距离: 0.09072200336618969, 相关性: 0.9788106004024394, 卡方: 164.3082768373199
以上数据说明两张图很相似。如果图像大小不一致需要对直方图进行归一化。