红外与可见光图像融合评价指标(cddfusion中的代码Evaluator.py)

一、Evaluator.py全部代码(可正常调用)


import numpy as np
import cv2
import sklearn.metrics as skm
from scipy.signal import convolve2d
import math
from skimage.metrics import structural_similarity as ssim

def image_read_cv2(path, mode='RGB'):
    img_BGR = cv2.imread(path).astype('float32')
    assert mode == 'RGB' or mode == 'GRAY' or mode == 'YCrCb', 'mode error'
    if mode == 'RGB':
        img = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2RGB)
    elif mode == 'GRAY':  
        img = np.round(cv2.cvtColor(img_BGR, cv2.COLOR_BGR2GRAY))
    elif mode == 'YCrCb':
        img = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2YCrCb)
    return img

class Evaluator():
    @classmethod
    def input_check(cls, imgF, imgA=None, imgB=None): 
        if imgA is None:
            assert type(imgF) == np.ndarray, 'type error'
            assert len(imgF.shape) == 2, 'dimension error'
        else:
            assert type(imgF) == type(imgA) == type(imgB) == np.ndarray, 'type error'
            assert imgF.shape == imgA.shape == imgB.shape, 'shape error'
            assert len(imgF.shape) == 2, 'dimension error'

    @classmethod
    def EN(cls, img):  # entropy
        cls.input_check(img)
        a = np.uint8(np.round(img)).flatten()
        h = np.bincount(a) / a.shape[0]
        return -sum(h * np.log2(h + (h == 0)))

    @classmethod
    def SD(cls, img):
        cls.input_check(img)
        return np.std(img)

    @classmethod
    def SF(cls, img):
        cls.input_check(img)
        return np.sqrt(np.mean((img[:, 1:] - img[:, :-1]) ** 2) + np.mean((img[1:, :] - img[:-1, :]) ** 2))

    @classmethod
    def AG(cls, img):  # Average gradient
        cls.input_check(img)
        Gx, Gy = np.zeros_like(img), np.zeros_like(img)

        Gx[:, 0] = img[:, 1] - img[:, 0]
        Gx[:, -1] = img[:, -1] - img[:, -2]
        Gx[:, 1:-1] = (img[:, 2:] - img[:, :-2]) / 2

        Gy[0, :] = img[1, :] - img[0, :]
        Gy[-1, :] = img[-1, :] - img[-2, :]
        Gy[1:-1, :] = (img[2:, :] - img[:-2, :]) / 2
        return np.mean(np.sqrt((Gx ** 2 + Gy ** 2) / 2))

    @classmethod
    def MI(cls, image_F, image_A, image_B):
        cls.input_check(image_F, image_A, image_B)
        return skm.mutual_info_score(image_F.flatten(), image_A.flatten()) + skm.mutual_info_score(image_F.flatten(),
                                                                                                   image_B.flatten())

    @classmethod
    def MSE(cls, image_F, image_A, image_B):  # MSE
        cls.input_check(image_F, image_A, image_B)
        return (np.mean((image_A - image_F) ** 2) + np.mean((image_B - image_F) ** 2)) / 2

    @classmethod
    def CC(cls, image_F, image_A, image_B):
        cls.input_check(image_F, image_A, image_B)
        rAF = np.sum((image_A - np.mean(image_A)) * (image_F - np.mean(image_F))) / np.sqrt(
            (np.sum((image_A - np.mean(image_A)) ** 2)) * (np.sum((image_F - np.mean(image_F)) ** 2)))
        rBF = np.sum((image_B - np.mean(image_B)) * (image_F - np.mean(image_F))) / np.sqrt(
            (np.sum((image_B - np.mean(image_B)) ** 2)) * (np.sum((image_F - np.mean(image_F)) ** 2)))
        return (rAF + rBF) / 2

    @classmethod
    def PSNR(cls, image_F, image_A, image_B):
        cls.input_check(image_F, image_A, image_B)
        return 10 * np.log10(np.max(image_F) ** 2 / cls.MSE(image_F, image_A, image_B))

    @classmethod
    def SCD(cls, image_F, image_A, image_B): # The sum of the correlations of differences
        cls.input_check(image_F, image_A, image_B)
        imgF_A = image_F - image_A
        imgF_B = image_F - image_B
        corr1 = np.sum((image_A - np.mean(image_A)) * (imgF_B - np.mean(imgF_B))) / np.sqrt(
            (np.sum((image_A - np.mean(image_A)) ** 2)) * (np.sum((imgF_B - np.mean(imgF_B)) ** 2)))
        corr2 = np.sum((image_B - np.mean(image_B)) * (imgF_A - np.mean(imgF_A))) / np.sqrt(
            (np.sum((image_B - np.mean(image_B)) ** 2)) * (np.sum((imgF_A - np.mean(imgF_A)) ** 2)))
        return corr1 + corr2

    @classmethod
    def VIFF(cls, image_F, image_A, image_B):
        cls.input_check(image_F, image_A, image_B)
        return cls.compare_viff(image_A, image_F)+cls.compare_viff(image_B, image_F)

    @classmethod
    def compare_viff(cls,ref, dist): # viff of a pair of pictures
        sigma_nsq = 2
        eps = 1e-10

        num = 0.0
        den = 0.0
        for scale in range(1, 5):

            N = 2 ** (4 - scale + 1) + 1
            sd = N / 5.0

            # Create a Gaussian kernel as MATLAB's
            m, n = [(ss - 1.) / 2. for ss in (N, N)]
            y, x = np.ogrid[-m:m + 1, -n:n + 1]
            h = np.exp(-(x * x + y * y) / (2. * sd * sd))
            h[h < np.finfo(h.dtype).eps * h.max()] = 0
            sumh = h.sum()
            if sumh != 0:
                win = h / sumh

            if scale > 1:
                ref = convolve2d(ref, np.rot90(win, 2), mode='valid')
                dist = convolve2d(dist, np.rot90(win, 2), mode='valid')
                ref = ref[::2, ::2]
                dist = dist[::2, ::2]

            mu1 = convolve2d(ref, np.rot90(win, 2), mode='valid')
            mu2 = convolve2d(dist, np.rot90(win, 2), mode='valid')
            mu1_sq = mu1 * mu1
            mu2_sq = mu2 * mu2
            mu1_mu2 = mu1 * mu2
            sigma1_sq = convolve2d(ref * ref, np.rot90(win, 2), mode='valid') - mu1_sq
            sigma2_sq = convolve2d(dist * dist, np.rot90(win, 2), mode='valid') - mu2_sq
            sigma12 = convolve2d(ref * dist, np.rot90(win, 2), mode='valid') - mu1_mu2

            sigma1_sq[sigma1_sq < 0] = 0
            sigma2_sq[sigma2_sq < 0] = 0

            g = sigma12 / (sigma1_sq + eps)
            sv_sq = sigma2_sq - g * sigma12

            g[sigma1_sq < eps] = 0
            sv_sq[sigma1_sq < eps] = sigma2_sq[sigma1_sq < eps]
            sigma1_sq[sigma1_sq < eps] = 0

            g[sigma2_sq < eps] = 0
            sv_sq[sigma2_sq < eps] = 0

            sv_sq[g < 0] = sigma2_sq[g < 0]
            g[g < 0] = 0
            sv_sq[sv_sq <= eps] = eps

            num += np.sum(np.log10(1 + g * g * sigma1_sq / (sv_sq + sigma_nsq)))
            den += np.sum(np.log10(1 + sigma1_sq / sigma_nsq))

        vifp = num / den

        if np.isnan(vifp):
            return 1.0
        else:
            return vifp

    @classmethod
    def Qabf(cls, image_F, image_A, image_B):
        cls.input_check(image_F, image_A, image_B)
        gA, aA = cls.Qabf_getArray(image_A)
        gB, aB = cls.Qabf_getArray(image_B)
        gF, aF = cls.Qabf_getArray(image_F)
        QAF = cls.Qabf_getQabf(aA, gA, aF, gF)
        QBF = cls.Qabf_getQabf(aB, gB, aF, gF)

        # 计算QABF
        deno = np.sum(gA + gB)
        nume = np.sum(np.multiply(QAF, gA) + np.multiply(QBF, gB))
        return nume / deno

    @classmethod
    def Qabf_getArray(cls,img):
        # Sobel Operator Sobel
        h1 = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]]).astype(np.float32)
        h2 = np.array([[0, 1, 2], [-1, 0, 1], [-2, -1, 0]]).astype(np.float32)
        h3 = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]).astype(np.float32)

        SAx = convolve2d(img, h3, mode='same')
        SAy = convolve2d(img, h1, mode='same')
        gA = np.sqrt(np.multiply(SAx, SAx) + np.multiply(SAy, SAy))
        aA = np.zeros_like(img)
        aA[SAx == 0] = math.pi / 2
        aA[SAx != 0]=np.arctan(SAy[SAx != 0] / SAx[SAx != 0])
        return gA, aA

    @classmethod
    def Qabf_getQabf(cls,aA, gA, aF, gF):
        L = 1
        Tg = 0.9994
        kg = -15
        Dg = 0.5
        Ta = 0.9879
        ka = -22
        Da = 0.8
        GAF,AAF,QgAF,QaAF,QAF = np.zeros_like(aA),np.zeros_like(aA),np.zeros_like(aA),np.zeros_like(aA),np.zeros_like(aA)
        GAF[gA>gF]=gF[gA>gF]/gA[gA>gF]
        GAF[gA == gF] = gF[gA == gF]
        GAF[gA <gF] = gA[gA<gF]/gF[gA<gF]
        AAF = 1 - np.abs(aA - aF) / (math.pi / 2)
        QgAF = Tg / (1 + np.exp(kg * (GAF - Dg)))
        QaAF = Ta / (1 + np.exp(ka * (AAF - Da)))
        QAF = QgAF* QaAF
        return QAF

    # @classmethod
    # def SSIM(cls, image_F, image_A, image_B):
    #     cls.input_check(image_F, image_A, image_B)
    #     return ssim(image_F,image_A)+ssim(image_F,image_B)
    @classmethod
    def SSIM(cls,image_F, image_A, image_B):
        # 假设图像已经归一化并且在 [0, 1] 范围内
        data_range = 1.0  # 如果您的图像有不同的范围,请更新这个值
        cls.input_check(image_F, image_A, image_B)

        # 计算 image_F 和 image_A,以及 image_F 和 image_B 之间的 SSIM
        ssim_score_A = ssim(image_F, image_A, data_range=data_range)
        ssim_score_B = ssim(image_F, image_B, data_range=data_range)

        # 返回 SSIM 的平均值
        return (ssim_score_A + ssim_score_B) / 2


def VIFF(image_F, image_A, image_B):
    refA=image_A
    refB=image_B
    dist=image_F

    sigma_nsq = 2
    eps = 1e-10
    numA = 0.0
    denA = 0.0
    numB = 0.0
    denB = 0.0
    for scale in range(1, 5):
        N = 2 ** (4 - scale + 1) + 1
        sd = N / 5.0
        # Create a Gaussian kernel as MATLAB's
        m, n = [(ss - 1.) / 2. for ss in (N, N)]
        y, x = np.ogrid[-m:m + 1, -n:n + 1]
        h = np.exp(-(x * x + y * y) / (2. * sd * sd))
        h[h < np.finfo(h.dtype).eps * h.max()] = 0
        sumh = h.sum()
        if sumh != 0:
            win = h / sumh

        if scale > 1:
            refA = convolve2d(refA, np.rot90(win, 2), mode='valid')
            refB = convolve2d(refB, np.rot90(win, 2), mode='valid')
            dist = convolve2d(dist, np.rot90(win, 2), mode='valid')
            refA = refA[::2, ::2]
            refB = refB[::2, ::2]
            dist = dist[::2, ::2]

        mu1A = convolve2d(refA, np.rot90(win, 2), mode='valid')
        mu1B = convolve2d(refB, np.rot90(win, 2), mode='valid')
        mu2 = convolve2d(dist, np.rot90(win, 2), mode='valid')
        mu1_sq_A = mu1A * mu1A
        mu1_sq_B = mu1B * mu1B
        mu2_sq = mu2 * mu2
        mu1A_mu2 = mu1A * mu2
        mu1B_mu2 = mu1B * mu2
        sigma1A_sq = convolve2d(refA * refA, np.rot90(win, 2), mode='valid') - mu1_sq_A
        sigma1B_sq = convolve2d(refB * refB, np.rot90(win, 2), mode='valid') - mu1_sq_B
        sigma2_sq = convolve2d(dist * dist, np.rot90(win, 2), mode='valid') - mu2_sq
        sigma12_A = convolve2d(refA * dist, np.rot90(win, 2), mode='valid') - mu1A_mu2
        sigma12_B = convolve2d(refB * dist, np.rot90(win, 2), mode='valid') - mu1B_mu2

        sigma1A_sq[sigma1A_sq < 0] = 0
        sigma1B_sq[sigma1B_sq < 0] = 0
        sigma2_sq[sigma2_sq < 0] = 0

        gA = sigma12_A / (sigma1A_sq + eps)
        gB = sigma12_B / (sigma1B_sq + eps)
        sv_sq_A = sigma2_sq - gA * sigma12_A
        sv_sq_B = sigma2_sq - gB * sigma12_B

        gA[sigma1A_sq < eps] = 0
        gB[sigma1B_sq < eps] = 0
        sv_sq_A[sigma1A_sq < eps] = sigma2_sq[sigma1A_sq < eps]
        sv_sq_B[sigma1B_sq < eps] = sigma2_sq[sigma1B_sq < eps]
        sigma1A_sq[sigma1A_sq < eps] = 0
        sigma1B_sq[sigma1B_sq < eps] = 0

        gA[sigma2_sq < eps] = 0
        gB[sigma2_sq < eps] = 0
        sv_sq_A[sigma2_sq < eps] = 0
        sv_sq_B[sigma2_sq < eps] = 0

        sv_sq_A[gA < 0] = sigma2_sq[gA < 0]
        sv_sq_B[gB < 0] = sigma2_sq[gB < 0]
        gA[gA < 0] = 0
        gB[gB < 0] = 0
        sv_sq_A[sv_sq_A <= eps] = eps
        sv_sq_B[sv_sq_B <= eps] = eps

        numA += np.sum(np.log10(1 + gA * gA * sigma1A_sq / (sv_sq_A + sigma_nsq)))
        numB += np.sum(np.log10(1 + gB * gB * sigma1B_sq / (sv_sq_B + sigma_nsq)))
        denA += np.sum(np.log10(1 + sigma1A_sq / sigma_nsq))
        denB += np.sum(np.log10(1 + sigma1B_sq / sigma_nsq))

    vifpA = numA / denA
    vifpB =numB / denB

    if np.isnan(vifpA):
        vifpA=1
    if np.isnan(vifpB):
        vifpB = 1
    return vifpA+vifpB

1.图像读取函数image_read_cv2(): 

函数image_read_cv2(): 

def image_read_cv2(path, mode='RGB'):
    img_BGR = cv2.imread(path).astype('float32')
    assert mode == 'RGB' or mode == 'GRAY' or mode == 'YCrCb', 'mode error'
    if mode == 'RGB':
        img = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2RGB)
    elif mode == 'GRAY':  
        img = np.round(cv2.cvtColor(img_BGR, cv2.COLOR_BGR2GRAY))
    elif mode == 'YCrCb':
        img = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2YCrCb)
    return img

将读入的图像全部转化为单通道的灰度图像。

2.输入图像格式检查函数input_check(): 

函数input_check():

@classmethod
    def input_check(cls, imgF, imgA=None, imgB=None): 
        if imgA is None:
            assert type(imgF) == np.ndarray, 'type error'
            assert len(imgF.shape) == 2, 'dimension error'
        else:
            assert type(imgF) == type(imgA) == type(imgB) == np.ndarray, 'type error'
            assert imgF.shape == imgA.shape == imgB.shape, 'shape error'
            assert len(imgF.shape) == 2, 'dimension error'

定义了一个类方法(classmethod)input_check,也就是说在类的其他方法中可以通过cls.input_check()直接调用这个方法。该函数主要的作用是检查输入图像的格式。

如果imgANone

  • 确保imgF的类型是numpy.ndarray(NumPy数组),如果不是,抛出一个类型错误的异常。
  • 确认imgF的维度是2(二维数组),如果不是,抛出一个维度错误的异常。

如果imgA不为None

  • 确保imgFimgAimgB的类型都是numpy.ndarray,如果不是,抛出一个类型错误的异常。
  • 确保imgFimgAimgB的形状相同,如果不是,抛出一个形状错误的异常。
  • 再次确认imgF的维度是2。

二、图像评价指标(12种)

1. EN(信息熵 Entropy)

信息熵主要是度量图像包含信息量多少的一个客观评价指标,a表示灰度值,P(a)表示灰度概率分布:

信息熵越高表示融合图像的信息量越丰富,质量越好。

代码如下:(只检查融合图像)
def EN(cls, img):  # entropy
        cls.input_check(img)
        a = np.uint8(np.round(img)).flatten()
        h = np.bincount(a) / a.shape[0]
        return -sum(h * np.log2(h + (h == 0)))

a = np.uint8(np.round(img)).flatten(): np.round():对括号中的内容进行四舍五入的操作。不会改变原数组,而是返回一个新的数组,除非你明确地将结果赋值给原数组的一个元素。然后将新的数组转换为整数(np.uint8 表示无符号8位整数)。最后使用.flatten将其展平为一维数组。

h = np.bincount(a) / a.shape[0]: 使用 np.bincount 函数计算数组 a 中每个整数值出现的次数,并除以总像素数 a.shape[0],得到每个整数值的概率分布 h

return -sum(h * np.log2(h + (h == 0))): 最后,函数计算熵。根据信息论,熵(H)定义为每个可能状态(这里对应每个整数值)的概率(h)乘以以自然对数为底的负对数(-np.log2),然后求和。+ (h == 0) 是为了避免对0取对数,因为自然对数的底数不能为0,所以这里用一个小技巧处理概率为0的情况。

2. SD(标准差 standard Deviation)

是一种衡量数据集中数据分布或离散程度的统计量。在图像处理中,标准差通常用来评价图像的对比度和清晰度,因此也可以用来评价融合图像的质量。

  1. 对比度评价:标准差反映了图像像素值的变化范围,对比度高的图像通常具有较大的标准差。因此,通过比较融合图像和原始图像的标准差,可以评价融合后图像的对比度是否有所提高或降低。

  2. 清晰度评价:清晰的图像通常具有较高的对比度,因此标准差也可以间接反映图像的清晰度。融合图像的标准差较高可能表示图像边缘清晰,细节丰富。

所以融合图像的SD越大越好吗?(NO)

并不是,

具体来说,SD的大小与融合图像的对比度、清晰度和一致性程度存在一定的关联,但并不是越大越好。

  1. 对比度: SD的大小与融合图像的对比度有一定的正相关关系,即SD越大,对比度可能也会越高。但是,过高的对比度可能导致图像过亮或过暗,影响观察体验。因此,在融合图像时需要根据具体应用场景来寻找适合的对比度。

  2. 清晰度: SD的大小与融合图像的清晰度也有一定的正相关关系,即SD越大,图像细节可能也会更加清晰。但是,过高的SD可能导致图像出现过多噪声或失真,影响观察体验。因此,在融合图像时需要根据具体应用场景来寻找适合的清晰度。

  3. 一致性: SD的大小与融合图像的一致性程度没有明确的相关关系,因此SD的大小并不能直接反映融合图像的一致性。

综上所述,SD的大小并不是融合图像的绝对评价指标,而是需要根据具体应用场景和其他图像质量评价指标来进行综合评价。在某些情况下,SD的大小可能会提示融合图像的质量有所提高,但是过高的SD也可能导致图像质量下降。因此,在融合图像时需要谨慎地考虑SD的大小,并结合其他指标进行综合评价。

代码如下:(只检查融合图像)
 def SD(cls, img):
        cls.input_check(img)
        return np.std(img)

3.  SF(空间频率 Spatial Frequency)

空间频率(Spatial Frequency)反映图像灰度的变化率,空间频率越大表示图像越清晰,融合图像质量越好。其计算公式如下:

 

代码如下:(只检查融合图像)
 def SF(cls, img):
        cls.input_check(img)
        return np.sqrt(np.mean((img[:, 1:] - img[:, :-1]) ** 2) + np.mean((img[1:, :] - img[:-1, :]) ** 2))

4.  AG(平均梯度 Average Gradient )

平均梯度(Average Gradient)可以用于衡量融合图像的清晰程度,可以认为平均梯度越大,图像清晰度越好,融合质量越好。计算公式如下:

其中H表示融合图像,M与N分别表示图像的高和宽。

 def AG(cls, img):  # Average gradient
        cls.input_check(img)
        Gx, Gy = np.zeros_like(img), np.zeros_like(img)

        Gx[:, 0] = img[:, 1] - img[:, 0]
        Gx[:, -1] = img[:, -1] - img[:, -2]
        Gx[:, 1:-1] = (img[:, 2:] - img[:, :-2]) / 2

        Gy[0, :] = img[1, :] - img[0, :]
        Gy[-1, :] = img[-1, :] - img[-2, :]
        Gy[1:-1, :] = (img[2:, :] - img[:-2, :]) / 2
        return np.mean(np.sqrt((Gx ** 2 + Gy ** 2) / 2))

5.MI(互信息 Mutual Information)

可度量两幅图像之间的相似程度,即融合图像获取了原图像信息量的多少。互信息越大,表示融合图像保留更多源图像信息,质量越好。互信息是根据图像的信息熵H(A)和联合信息熵H(A,B):

代码如下:(计算三幅图片的)
def MI(cls, image_F, image_A, image_B):
        cls.input_check(image_F, image_A, image_B)
        return skm.mutual_info_score(image_F.flatten(), image_A.flatten()) + skm.mutual_info_score(image_F.flatten(),
                                                                                                   image_B.flatten())

使用了 skm.mutual_info_score 函数来计算互信息,这个函数来自于 sklearn.metrics 模块,用于评估两个离散或连续随机变量之间的互信息。 

最后的结果是融合图像和可见光图像的互信息加上融合图像和红外图像的互信息。

6.MSE(均方误差 Mean Square Error)

反映的是变量间的差异程度,是一种基于像素误差的图像质量客观评价指标,用于衡量融合图像和理想参考图像之间的差异,MSE越小,表示融合图像质量越好。
均方根误差(Root Mean Square Error)是一个翻译空间细节信息的评价指标,公式如下:

归一化均方根误差(normalized root mean square error)就是将RMSE的值变成(0,1)之间。

代码如下:(越小越好,输入三幅图片)
 def MSE(cls, image_F, image_A, image_B):  # MSE
        cls.input_check(image_F, image_A, image_B)
        return (np.mean((image_A - image_F) ** 2) + np.mean((image_B - image_F) ** 2)) / 2

计算了图像 image_A 与图像 image_F 之间的均方误差,以及图像 image_B 与图像 image_F 之间的均方误差,然后将这两个均方误差值相加并除以2取平均,最终返回这个平均值作为结果。这里使用了 np.mean 函数来计算均值,** 表示乘方操作,即计算差的平方。均方误差用于评估两幅图像之间的差异程度,值越小表示两幅图像越相似。 

7.CC(相关系数  Correlation Coefficient)

CC 是一种用于衡量图像之间相关性的指标,通常用于评估图像的相似性或质量。

相关系数用于衡量两个变量之间的线性关系强度,取值范围为[-1, 1],值为1表示完全正相关,值为-1表示完全负相关,值为0表示无相关性。

代码如下:(越接近1越好,输入三幅图片)
  def CC(cls, image_F, image_A, image_B):
        cls.input_check(image_F, image_A, image_B)
        rAF = np.sum((image_A - np.mean(image_A)) * (image_F - np.mean(image_F))) / np.sqrt(
            (np.sum((image_A - np.mean(image_A)) ** 2)) * (np.sum((image_F - np.mean(image_F)) ** 2)))
        rBF = np.sum((image_B - np.mean(image_B)) * (image_F - np.mean(image_F))) / np.sqrt(
            (np.sum((image_B - np.mean(image_B)) ** 2)) * (np.sum((image_F - np.mean(image_F)) ** 2)))
        return (rAF + rBF) / 2

计算了图像 image_A 与图像 image_F 之间的相关系数 rAF。相关系数用于衡量两个变量之间的线性关系强度,取值范围为[-1, 1],值为1表示完全正相关,值为-1表示完全负相关,值为0表示无相关性。这里使用了 np.sum 函数来计算总和,np.mean 函数来计算均值,** 表示乘方操作。

计算了图像 image_B 与图像 image_F 之间的相关系数 rBF,同样使用了类似的计算方法。

最后返回了图像 image_A 与图像 image_F 之间的相关系数 rAF 和图像 image_B 与图像 image_F 之间的相关系数 rBF 的平均值作为最终结果。这里假设两个相关系数具有相同的权重,因此取平均值。

8.PSNR(峰值信噪比  Peak signal to noise ration)

用于衡量图像有效信息与噪声之间的比率,能够反映图像是否失真。

Z表示理想参考图像灰度最大值与最小值之差,通常为255。PSNR的值越大,表示融合图像的质量越好。

代码如下:(越大越好,输入三幅图片)
def PSNR(cls, image_F, image_A, image_B):
        cls.input_check(image_F, image_A, image_B)
        return 10 * np.log10(np.max(image_F) ** 2 / cls.MSE(image_F, image_A, image_B))

直接用img_F的z的平方除以之前就算好的MSE。

9.SCD(差异相关和  Sum of the Correlations of Differences)

代码如下:((-1,1)表示相关性,所以最好是正相关,也就是接近1)
    @classmethod
    def corr2(a, b):
        a = a - np.mean(a)
        b = b - np.mean(b)
        r = np.sum(a * b) / np.sqrt(np.sum(a * a) * np.sum(b * b))
        return r

    @classmethod
    def SCD(cls, image_F, image_A, image_B): # The sum of the correlations of differences
        cls.input_check(image_F, image_A, image_B)
        r = cls.corr2(image_F - image_B, image_A) + cls.corr2(image_F - image_A, image_B)
        return r

这段代码中的SCD方法计算了两幅原始图像image_Aimage_B分别与融合后的图像image_F的差值图像之间的相关性之和。具体来说,它首先调用corr2方法计算了image_F - image_Bimage_A之间的相关性,然后计算了image_F - image_Aimage_B之间的相关性,最后将这两个相关性值相加得到最终的SCD值。

在图像融合领域中,SCD值可以用来评估融合后的图像与原始图像之间的结构相似性,即差异相关和。通过计算差值图像之间的相关性,可以评估融合后图像的质量,以及融合过程中保留了多少原始图像的结构信息。

因此,这段代码中的SCD方法实际上是在计算图像融合质量的一个指标,通过分析差值图像之间的相关性来评估融合效果。

10.VIFF(视觉保真度 The visual information fidelity for fusion)

是基于视觉信息保真度提出的衡量融合图像质量的指标,由于太复杂了,详见A new image fusion performance metric based on visual information fidelity。VIFF的值越大,表示融合图像质量越好。

代码如下:(越接近1越好,输入三张图片)
@classmethod
    def VIFF(cls, image_F, image_A, image_B):
        cls.input_check(image_F, image_A, image_B)
        return cls.compare_viff(image_A, image_F)+cls.compare_viff(image_B, image_F)

    @classmethod
    def compare_viff(cls,ref, dist): # viff of a pair of pictures
        sigma_nsq = 2
        eps = 1e-10

        num = 0.0
        den = 0.0
        for scale in range(1, 5):

            N = 2 ** (4 - scale + 1) + 1
            sd = N / 5.0

            # Create a Gaussian kernel as MATLAB's
            m, n = [(ss - 1.) / 2. for ss in (N, N)]
            y, x = np.ogrid[-m:m + 1, -n:n + 1]
            h = np.exp(-(x * x + y * y) / (2. * sd * sd))
            h[h < np.finfo(h.dtype).eps * h.max()] = 0
            sumh = h.sum()
            if sumh != 0:
                win = h / sumh

            if scale > 1:
                ref = convolve2d(ref, np.rot90(win, 2), mode='valid')
                dist = convolve2d(dist, np.rot90(win, 2), mode='valid')
                ref = ref[::2, ::2]
                dist = dist[::2, ::2]

            mu1 = convolve2d(ref, np.rot90(win, 2), mode='valid')
            mu2 = convolve2d(dist, np.rot90(win, 2), mode='valid')
            mu1_sq = mu1 * mu1
            mu2_sq = mu2 * mu2
            mu1_mu2 = mu1 * mu2
            sigma1_sq = convolve2d(ref * ref, np.rot90(win, 2), mode='valid') - mu1_sq
            sigma2_sq = convolve2d(dist * dist, np.rot90(win, 2), mode='valid') - mu2_sq
            sigma12 = convolve2d(ref * dist, np.rot90(win, 2), mode='valid') - mu1_mu2

            sigma1_sq[sigma1_sq < 0] = 0
            sigma2_sq[sigma2_sq < 0] = 0

            g = sigma12 / (sigma1_sq + eps)
            sv_sq = sigma2_sq - g * sigma12

            g[sigma1_sq < eps] = 0
            sv_sq[sigma1_sq < eps] = sigma2_sq[sigma1_sq < eps]
            sigma1_sq[sigma1_sq < eps] = 0

            g[sigma2_sq < eps] = 0
            sv_sq[sigma2_sq < eps] = 0

            sv_sq[g < 0] = sigma2_sq[g < 0]
            g[g < 0] = 0
            sv_sq[sv_sq <= eps] = eps

            num += np.sum(np.log10(1 + g * g * sigma1_sq / (sv_sq + sigma_nsq)))
            den += np.sum(np.log10(1 + sigma1_sq / sigma_nsq))

        vifp = num / den

        if np.isnan(vifp):
            return 1.0
        else:
            return vifp

这段代码定义了一个类方法VIFF,用于计算两个图像(image_F和image_A或image_B)之间的视觉信息量(Visual Information Fidelity,简称VIFF)。VIFF是一种图像质量评估方法,用于比较两幅图像的相似性。

  1. @classmethod注解表明这是一个类方法,可以直接通过类名调用,而不需要实例化对象。

  2. cls.input_check(image_F, image_A, image_B):是用于检查输入图像的尺寸和类型是否正确,确保可以进行后续计算。

  3. compare_viff(ref, dist)是一个内部函数,计算两个图像(ref和dist)的VIFF值: a. sigma_nsq 和 eps 是常数,分别用于计算中的稳定性和避免除以零。 b. 通过循环(for scale in range(1, 5))对图像进行不同尺度的低通滤波(Gaussian kernel),这有助于减少噪声影响。 c. 计算每个尺度下的均值(mu1, mu2)、均值的平方(mu1_sq, mu2_sq)、协方差(sigma12)、方差(sigma1_sq, sigma2_sq)。 d. 计算相关系数(g),并根据sigma_nsqeps进行调整。 e. 计算VIFF值(vifp),即信息增益(num)除以信息冗余(den)。 f. 如果计算出的VIFF值是NaN,返回1.0(可能是为了处理异常情况),否则返回计算出的VIFF值。

总的来说,这段代码是用于评估两个图像之间在不同尺度下相似性的算法,结果范围在0到1之间,1表示完全相同,0表示完全不同。

11.Qabf(Quality Assessment Based on Fusion)

Qabf是一种新颖的融合图像客观非参考质量评估指标,得到Qabf的算法利用局部度量来估计来自输入的显着信息在融合图像中的表现程度,Qabf的值越高,表示融合图像的质量越好。计算公式如下:

详细可见见论文A new quality metric for image fusion 

代码如下:(越大越好,输入三幅图片) 
@classmethod
    def Qabf(cls, image_F, image_A, image_B):
        cls.input_check(image_F, image_A, image_B)
        gA, aA = cls.Qabf_getArray(image_A)
        gB, aB = cls.Qabf_getArray(image_B)
        gF, aF = cls.Qabf_getArray(image_F)
        QAF = cls.Qabf_getQabf(aA, gA, aF, gF)
        QBF = cls.Qabf_getQabf(aB, gB, aF, gF)

        # 计算QABF
        deno = np.sum(gA + gB)
        nume = np.sum(np.multiply(QAF, gA) + np.multiply(QBF, gB))
        return nume / deno

    @classmethod
    def Qabf_getArray(cls,img):
        # Sobel Operator Sobel
        h1 = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]]).astype(np.float32)
        h2 = np.array([[0, 1, 2], [-1, 0, 1], [-2, -1, 0]]).astype(np.float32)
        h3 = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]).astype(np.float32)

        SAx = convolve2d(img, h3, mode='same')
        SAy = convolve2d(img, h1, mode='same')
        gA = np.sqrt(np.multiply(SAx, SAx) + np.multiply(SAy, SAy))
        aA = np.zeros_like(img)
        aA[SAx == 0] = math.pi / 2
        aA[SAx != 0]=np.arctan(SAy[SAx != 0] / SAx[SAx != 0])
        return gA, aA

    @classmethod
    def Qabf_getQabf(cls,aA, gA, aF, gF):
        L = 1
        Tg = 0.9994
        kg = -15
        Dg = 0.5
        Ta = 0.9879
        ka = -22
        Da = 0.8
        GAF,AAF,QgAF,QaAF,QAF = np.zeros_like(aA),np.zeros_like(aA),np.zeros_like(aA),np.zeros_like(aA),np.zeros_like(aA)
        GAF[gA>gF]=gF[gA>gF]/gA[gA>gF]
        GAF[gA == gF] = gF[gA == gF]
        GAF[gA <gF] = gA[gA<gF]/gF[gA<gF]
        AAF = 1 - np.abs(aA - aF) / (math.pi / 2)
        QgAF = Tg / (1 + np.exp(kg * (GAF - Dg)))
        QaAF = Ta / (1 + np.exp(ka * (AAF - Da)))
        QAF = QgAF* QaAF
        return QAF
  1. Qabf 方法接收三个输入图像(image_Fimage_Aimage_B),首先进行输入检查,然后分别计算每个图像的梯度(gAgBgF)和方向(aAaBaF)。

  2. Qabf_getArray 方法使用Sobel算子计算图像的梯度和方向。Sobel算子用于检测图像边缘,h1h2, 和 h3 是用于计算x和y方向导数的滤波器。然后计算梯度的模(gA 和 gB),以及方向(aA 和 aB)。

  3. Qabf_getQabf 方法计算Qabf值。它涉及到以下几个关键计算:

    • LTgkgDgTaka, 和 Da 是预设的参数,用于调整Qabf的计算。
    • GAF 是融合梯度的调整,根据gAgF的大小关系进行调整,以确保融合后的梯度不会过大或过小。
    • AAF 是角度差的调整,考虑了输入图像A和融合图像F的方向差异。
    • QgAF 和 QaAF 分别是基于梯度和角度调整的Q值。
    • 最终通过 QAF = QgAF * QaAF 计算出融合图像的Qabf值。
  4. QAF 是返回的结果,表示输入图像A在融合图像F中的表现程度,值越高,表明融合质量越好。

12.SSIM(结构相似性指标 Structural Similarity Index Measure, SSIM )

是一种用于比较两幅图像相似度的指标,它考虑了亮度、对比度和结构三个方面的信息。

SSIM值的范围在[-1, 1]之间,值越接近1表示两幅图像越相似,值越接近-1表示两幅图像越不相似,值为0表示两幅图像完全不同。

SSIM指标的计算基于以下三个因素:

  1. 亮度相似性(Luminance Similarity):衡量图像的亮度信息是否一致。
  2. 对比度相似性(Contrast Similarity):衡量图像的对比度信息是否一致。
  3. 结构相似性(Structure Similarity):衡量图像的结构信息是否一致。

其次就是MSSIM,这个很容易理解就是源图像A与融合图像F的SSIM与源图像B与融合图像F的SSIM的平均值。

其次是多层级结构相似性(Multiscale structural similarity),简写为MS-SSIM。MS-SSIM能更好地与人眼视觉系统的视觉感知相一致,并且在一定的尺度下,评价效果优于SSIM,公式如下:

 代码如下:(越接近1越好,输入三幅图像)
    @classmethod
    def SSIM(cls, image_F, image_A, image_B):
        cls.input_check(image_F, image_A, image_B)
        return ssim(image_F,image_A)+ssim(image_F,image_B)

参考:图像融合质量评价方法MSSIM、MS-SSIM、FS、Qmi、Qabf与VIFF(三)-CSDN博客

图像融合质量评价方法AG、SF、STD、MI与NMI(二)_nmi图像-CSDN博客 

图像融合评估指标Python版_图像融合评价指标python-CSDN博客 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/600795.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

minio上传文件失败如何解决

1. 做了什么操作 通过接口上传excel文件&#xff0c;返回响应值 2. 错误如图 2. 如何解决 根据错误描述定位到了部署minio的地方minio通过docker部署&#xff0c;找到docker - compose发现配置文件中minio有两个端口&#xff0c;一个是用于api的&#xff0c;一个是用于管理界面…

AI模型:windows本地运行下载安装ollama运行Google CodeGemma可离线运行数据模型【自留记录】

AI模型&#xff1a;windows本地运行下载安装ollama运行Google CodeGemma可离线运行数据模型【自留记录】 CodeGemma 没法直接运行&#xff0c;需要中间软件。下载安装ollama后&#xff0c;使用ollama运行CodeGemma。 类似 前端本地需要安装 node.js 才可能跑vue、react项目 1…

QX-mini51学习---(2)点亮LED

目录 1什么是ed 2led工作参数 3本节相关原理图分析 4本节相关c 5实践 1什么是ed 半导体发光二极管&#xff0c;将电能转化为光能&#xff0c;耗电低&#xff0c;寿命长&#xff0c;抗震动 长正短负&#xff0c;贴片是绿点处是负极 2led工作参数 3本节相关原理图分析 当…

工业网关设备的种类、功能及其在各种工业场景中的应用-天拓四方

在快速发展的工业信息化时代&#xff0c;工业网关设备作为连接工业设备与云平台的桥梁&#xff0c;发挥着至关重要的作用。本文将详细介绍工业网关设备的种类、功能以及其在各种工业场景中的应用&#xff0c;帮助广大读者更深入地了解这一重要设备。 一、工业网关设备的种类 …

【Linux 基础 IO】文件系统

文章目录 1.初步理解文件2.C语言环境下的文件操作2.1 C库中 fopen、fwrite 的讲解2.2 C文件操作的实例 3.系统调用接口的讲解 1.初步理解文件 &#x1f427;① 打开文件&#xff1a; 本质是进程打开文件&#xff0c;只有程序运行起来文件才被打开&#xff1b; &#x1f427;②文…

Fizzler库+C#:从微博抓取热点的最简单方法

概述 在这篇技术文章中&#xff0c;我们将深入研究如何利用Fizzler库结合C#语言&#xff0c;以实现从微博平台抓取热点信息的功能。微博作为中国乃至全球范围内具有重要影响力的社交媒体平台之一&#xff0c;在互联网信息传播中扮演着举足轻重的角色。通过Fizzler这一强大的.N…

【探索Java编程:从入门到入狱】Day4

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

电费自动抄表是什么?什么叫电费自动抄表?

1.电费自动抄表&#xff1a;简述 电费自动抄表是一种现代化电力工程管理方法&#xff0c;根据远程系统收集解决电度表数据&#xff0c;取代了传统的人工抄水表方法。这项技术提高了效率&#xff0c;降低了不正确&#xff0c;并且为消费者和电力公司提供了更多服务项目概率。 …

基于51单片机ESP8266wifi控制机器人—送餐、快递

基于51单片机wifi控制机器人 &#xff08;程序&#xff0b;原理图&#xff0b;PCB&#xff0b;设计报告&#xff09; ​功能介绍 具体功能&#xff1a; 1.L298N驱动电机&#xff0c;机器人行走&#xff1b; 2.装备红外线感应检测到周围环境&#xff0c;进行行程判断&#xf…

Windows环境编译 VVenC 源码生成 Visual Studio 工程

VVenC介绍 Fraunhofer通用视频编码器(VVenC)的开发是为了提供一种公开可用的、快速和有效的VVC编码器实现。VVenC软件基于VTM&#xff0c;其优化包括软件重新设计以减轻性能瓶颈、广泛的SIMD优化、改进的编码器搜索算法和基本的多线程支持以利用并行。此外&#xff0c;VVenC支…

124.反转链表(力扣)

题目描述 代码解决&#xff08;思路1&#xff1a;双指针&#xff09; class Solution { public:ListNode* reverseList(ListNode* head) {ListNode*temp;//保存cur下一个节点ListNode*curhead;ListNode*preNULL;while(cur){tempcur->next;// 保存一下 cur的下一个节点&#…

uniapp 监听APP切换前台、后台插件 Ba-Lifecycle

监听APP切换前台、后台 Ba-Lifecycle 简介&#xff08;下载地址&#xff09; Ba-Lifecycle 是一款uniapp监听APP切换前台、后台的插件&#xff0c;简单易用。 截图展示 也可关注博客&#xff0c;实时更新最新插件&#xff1a; uniapp 常用原生插件大全 使用方法 在 script…

Spring事件

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;Spring⛺️稳中求进&#xff0c;晒太阳 Spring事件 简洁 Spring Event&#xff08;Application Event&#xff09;就是一个观察者模式&#xff0c;一个bean处理完任务后希望通知其他Bean的…

数据交换和异步请求(JSONAjax))

目录 一.JSON介绍1.JSON的特点2.JSON的结构3.JSON的值JSON示例4.JSON与字符串对象转换5.注意事项 二.JSON在Java中的使用1.Javabean to json2.List to json3.Map to JSONTypeToken底层解析 三.Ajax介绍1.介绍2.Ajax经典应用场景 四.Ajax原理示意图1. 传统web应用2.Ajax方法 五.…

突然断电,瀚高数据库启动失败

服务器临时断电后&#xff0c;数据库启动不起来 ps -ef|grep postgres 进到数据库的data目录下看下ls 看下 查看临时文件&#xff1a; ls -la /tmp 把这两个5866的文件改个名字张老师 加个bak就行 改完了pg_ctl start起一下

618挑选家用洗地机,需要注意哪些事项?有哪些家用洗地机值得买?

近年来&#xff0c;智能清洁家电越来越受到消费者的欢迎&#xff0c;洗地机作为清洁家电的新宠&#xff0c;凭借其集扫地、拖地、杀菌清洗于一体的强大功能&#xff0c;成为市场上的热销产品。那么&#xff0c;这类洗地机真的好用吗&#xff1f;怎么挑选到好用的家用的洗地机呢…

风电厂数字孪生3D数据可视化交互展示构筑智慧化电厂管理体系

随着智慧电厂成为未来电力企业发展的必然趋势&#xff0c;深圳华锐视点紧跟时代步伐&#xff0c;引领技术革新&#xff0c;推出了能源3D可视化智慧管理系统。该系统以企业现有的数字化、信息化建设为基础&#xff0c;融合云平台、大数据、物联网、移动互联、机器人、VR虚拟现实…

BUUCTF [极客大挑战 2019]EasySQL 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; [极客大挑战 2019]EasySQL 1 密文&#xff1a; 解题思路&#xff1a; 1、根据题目提示&#xff0c;并且网站也存在输入框&#xff0c;尝试进行SQL注入。 首先&#xff0c;判断提交方式&#xff0c;随机输入数据…

EtherCAT开发_4_分布时钟知识点摘抄笔记1

分布时钟 (DC&#xff0c;Distributed Cl ock) 可以使所有EtherCAT设备使用相同的系统时间&#xff0c;从而控制各设备任务的同步执行。从站设备可以根据同步的系统时间产生同步信号&#xff0c;用于中断控制或触发数字量输入输出。支持分布式时钟的从站称为 DC 从站。分布时钟…

常见的容器技术有哪些

容器技术是一种轻量级的软件封装方式&#xff0c;它将软件代码及其依赖项打包在一起&#xff0c;这样应用可以在任何支持容器的系统上无缝运行。它允许应用程序及其依赖项在一个隔离的环境中运行&#xff0c;这个环境被称为容器。容器技术有助于提高应用程序的可移植性、一致性…