fuse/utils/Evaluator.py

306 lines
11 KiB
Python
Raw Permalink Normal View History

2023-05-04 17:34:24 +08:00
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)
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