赞
踩
引言
自然场景统计(NSS)经常被应用于图像的质量评价。人们发现,用高质量设备采集的自然图像(自然场景)有着一定的统计特征(如服从一些类高斯分布),而图像的失真会使这些统计特征发生改变,因此利用图像的一些统计特征作为图像特征,可以完成图像的无参考评价。
BRISQUE来自于论文《No-Reference Image Quality Assessment in the Spatial Domain》,是一个经典的利用NSS进行NR-IQA的模型。观察到自然图像局部归一化亮度系数(MSCN),强烈趋向于单位正态高斯特性,因此作者假设失真会改变MSCN的分布状况,并基于此提取特征。
原理简介
1.局部亮度归一化
如上图左列所示,像素与其周围的像素具有很高的相关性,因为图像函数通常是分段平滑的,我们在左列可以观察到一种对角结构。图像的归一化能够大大减少像素的相关性,如右列所示。
局部亮度归一化过程如下式:
其中:
C=1,是为了防止分母为0。根据上面式子可见这其实是z-score归一化过程。
2.利用广义高斯分布拟合MSCN获得特征
获得的MSCN具有一定的统计特征(如类高斯分布),根据下图可以观察到不同失真类型图像和自然图像具有不同分布特征,文献w使用广义高斯分布(GGD)对图像MSCN的分布进行拟合。
零均值广义高斯分布(GGD)的定义如下:
利用GGD拟合分布获得的形状参数(α, σ^2 )作为特征1和特征2。关于广义高斯分布的参数拟合方法实现问题,我在另一篇博客单独描述,这里主要介绍IQA模型原理,不在叙述。
3.利用非对称广义高斯分布拟合MSCN相邻系数内积
作者也研究了相邻MSCN系数之间的统计特征,虽然自然图像的MSCN系数比较均匀,但是会随着失真的影响而改变。因此作者在四个不同方向计算相邻系数内积,并利用非对称广义高斯分布(AGGD)拟合分布特征。四个方向的内积定义如下:
零均值非对称广义高斯分布(AGGD)的定义如下:
利用AGGD拟合获得的形状参数(η, ν, σ_l^2 , σ_r^2 )将作为特征3-18(4个方向 x 4个特征)。关于非对称广义高斯分布的参数拟合方法实现问题,我亦在另一篇博客单独描述,这里主要介绍IQA模型原理,不在叙述。
至此,对图像的18个特征提取就完成了,具体的特征内容如下表。需要指出的是,文献对图像进行了尺度为2的下采样,然后由提取了18个特征,因此共36个特征。
代码实现
文献的作者是给出MATLAB代码的,地址如下:
http://live.ece.utexas.edu/research/quality/BRISQUE_release.zip
现给出python实现代码:
- import numpy as np
- import cv2
- from scipy.special import gamma
- #产生二维高斯核函数
- #这个函数参考自:https://blog.csdn.net/qq_16013649/article/details/78784791
- def gaussian_2d_kernel(kernel_size, sigma):
- kernel = np.zeros((kernel_size, kernel_size))
- center = kernel_size // 2
- if sigma == 0:
- sigma = ((kernel_size - 1) * 0.5 - 1) * 0.3 + 0.8
- s = 2 * (sigma ** 2)
- sum_val = 0
- for i in range(0, kernel_size):
- for j in range(0, kernel_size):
- x = i - center
- y = j - center
- kernel[i, j] = np.exp(-(x ** 2 + y ** 2) / s)
- sum_val += kernel[i, j]
- sum_val = 1 / sum_val
- return kernel * sum_val
-
- #相关操作
- def correlation(img,kernal):
- kernal_heigh = kernal.shape[0]
- kernal_width = kernal.shape[1]
- h = kernal_heigh // 2
- w = kernal_width // 2
- # 边界补全
- img = np.pad(img, ((h, h), (w, w)), 'constant')
- cor_heigh = img.shape[0] - kernal_heigh + 1
- cor_width = img.shape[1] - kernal_width + 1
- result = np.zeros((cor_heigh, cor_width), dtype=np.float64)
- for i in range(cor_heigh):
- for j in range(cor_width):
- result[i][j] = (img[i:i + kernal_heigh, j:j + kernal_width] * kernal).sum()
- return result
-
- def estimate_GGD_parameters(vec):
- gam =np.arange(0.2,10.0,0.001)#产生候选的γ
- r_gam = (gamma(1/gam)*gamma(3/gam))/((gamma(2/gam))**2)#根据候选的γ计算r(γ)
- sigma_sq=np.mean((vec)**2)
- sigma=np.sqrt(sigma_sq)#方差估计
- E=np.mean(np.abs(vec))
- r=sigma_sq/(E**2)#根据sigma和E计算r(γ)
- diff=np.abs(r-r_gam)
- gamma_param=gam[np.argmin(diff, axis=0)]
- return [gamma_param,sigma_sq]
-
- def estimate_AGGD_parameters(vec):
- alpha =np.arange(0.2,10.0,0.001)#产生候选的α
- r_alpha=((gamma(2/alpha))**2)/(gamma(1/alpha)*gamma(3/alpha))#根据候选的γ计算r(α)
- sigma_l=np.sqrt(np.mean(vec[vec<0]**2))
- sigma_r=np.sqrt(np.mean(vec[vec>0]**2))
- gamma_=sigma_l/sigma_r
- u2=np.mean(vec**2)
- m1=np.mean(np.abs(vec))
- r_=m1**2/u2
- R_=r_*(gamma_**3+1)*(gamma_+1)/((gamma_**2+1)**2)
- diff=(R_-r_alpha)**2
- alpha_param=alpha[np.argmin(diff, axis=0)]
- const1 = np.sqrt(gamma(1 / alpha_param) / gamma(3 / alpha_param))
- const2 = gamma(2 / alpha_param) / gamma(1 / alpha_param)
- eta =(sigma_r-sigma_l)*const1*const2
- return [alpha_param,eta,sigma_l**2,sigma_r**2]
-
-
- def brisque_feature(dis_image):
- dis_image=dis_image.astype(np.float32)#类型转换十分重要
- kernal=gaussian_2d_kernel(7,7/6)
- ux=correlation(dis_image,kernal)
- ux_sq=ux*ux
- sigma=np.sqrt(np.abs(correlation(dis_image**2,kernal)-ux_sq))
- mscn=(dis_image-ux)/(1+sigma)
- f1_2=estimate_GGD_parameters(mscn)
- H=mscn*np.roll(mscn,1,axis=1)
- V=mscn*np.roll(mscn,1,axis=0)
- D1=mscn*np.roll(np.roll(mscn,1,axis=1),1,axis=0)
- D2=mscn*np.roll(np.roll(mscn,-1,axis=1),-1,axis=0)
- f3_6=estimate_AGGD_parameters(H)
- f7_10=estimate_AGGD_parameters(V)
- f11_14=estimate_AGGD_parameters(D1)
- f15_18=estimate_AGGD_parameters(D2)
- return f1_2+f3_6+f7_10+f11_14+f15_18
相关操作和产生高斯核是直接用代码写的,也可以调用python-opencv的相关函数实现。相关操作后的输出图像与输入图像大小相等,因此需要边界补0。
上述为特征f1-f18的提取,提取f1-f36需要进行下采样:
- img=cv2.imread(path+str(img_id)+'.bmp',cv2.IMREAD_GRAYSCALE)
- feat=brisque_feature(img)
- size = img.shape
- #下采样
- img = cv2.resize(img, (int(size[1] / 2), int(size[0]/ 2)), cv2.INTER_NEAREST)
- feat=feat+brisque_feature(img)
与文献w不同的是,这里使用网格搜索寻找SVR的最优参数,SVR使用的sklearn中的模块。
sklearn中的GridSearchCV可以实现网格搜索。
- from sklearn.svm import SVR
- from sklearn.model_selection import GridSearchCV
- #C、γ的搜索范围都是从1e-4到1e4,取9个候选值
- parameters={"kernel": ("linear", 'rbf'), "C": np.logspace(-4, 4, 9), "gamma": np.logspace(-4, 4,9)}
- svr = GridSearchCV(SVR(), param_grid=parameters,cv=4)#4折交叉验证
- svr.fit(feat, score)
- print('The parameters of the best model are: ')
- print(svr.best_params_)
使用网格搜索确定的参数训练的SVR效果略优于文献的结果。评估过程与文献相同,采用80%训练数据,20%测试数据,1000次随机训练-测试,利用1000次的中位数作为结果,本文的结果与文献的结果对比如下表:
————————————————
版权声明:本文为CSDN博主「一头大萝北」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sinat_36438332/article/details/88743536
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。