计算两幅图像的相似度(PSNR、SSIM、MSE、余弦相似度、MD5、直方图、互信息、Hash) 代码实现 与举例

news/2024/5/10 15:10:30/文章来源:https://blog.csdn.net/m0_61899108/article/details/127715737

MSE(Mean Squared Error)均方误差

MSE公式 

MSE 计算模型的预测 Ŷ 与真实标签 Y 的接近程度。公式表示为:

 

对于两个m×n的单通道图像I和K,它们的均方误差可定义为:

优点:MSE的函数曲线光滑、连续,处处可导,便于使用梯度下降算法,是一种常用的损失函数。而且,随着误差的减小,梯度也在减小,这有利于收敛,即使使用固定的学习速率,也能较快的收敛到最小值。

缺点:当真实值y和预测值f(x)的差值大于1时,会放大误差;而当差值小于1时,则会缩小误差,这是平方运算决定的。MSE对于较大的误差(>1)给予较大的惩罚,较小的误差(<1)给予较小的惩罚。也就是说,对离群点比较敏感,受其影响较大。

代码实现

# MSE# 方法一:自定义函数import numpy as np
def MSE(img1,img2):mse = np.mean( (img1 - img2) ** 2 )return mse# 方法二:调用库函数
# 老版本的scikit-image,加载SSIM、PSNR、MSE的方式:
from skimage.measure import compare_mse as mse# 新版本的scikit-image,加载方式:
from skimage.metrics import mean_squared_error as mse

PSNR(Peak Signal-to-Noise Ratio)峰值信噪比

 PSNR(Peak Signal to Noise Ratio),峰值信噪比,是一种评价图像的客观标准。,应用场景有很多。它具有局性,PSNR是“Peak Signal to Noise Ratio”的缩写。peak的中文意思是顶点。而ratio的意思是比率或比列的。整个意思就是到达噪音比率的顶点信号,psnr一般是用于最大值信号和背景噪音之间的一个工程项目。通常在经过影像压缩之后,通常输出的影像都会在某种程度与原始影像不同。为了衡量经过处理后的影像品质,通常会参考PSNR值来衡量某个处理程序能否令人满意。它是原图像与被处理图 像之间的均方误差相对于(2n-1)2的对数值(信号最大值的平方,n是每个采样值的比特数),它的单位是dB。

PSNR是最普遍和使用最为广泛的一种图像客观评价指标,然而它是基于对应像素点间的误差,即 基于误差敏感的图像质量评价。由于并未考虑到人眼的视觉特性(人眼对空间频率较低的对比差异敏感度较高,人眼对亮度对比差异的敏感度较色度高,人眼对一个 区域的感知结果会受到其周围邻近区域的影响等),因而经常出现评价结果与人的主观感觉不一致的情况。

PSNR公式

PSNR是通过MSE得出来的,公式如下: 

 其中,MAXI是表示图像点颜色的最大数值,如果每个采样点用 8 位表示,那么就是 255。

 所以MSE越小,则PSNR越大;所以PSNR越大,代表着图像质量越好。一般来说,

  • PSNR高于40dB说明图像质量极好(即非常接近原始图像);
  • 在30—40dB通常表示图像质量是好的(即失真可以察觉但可以接受);
  • 在20—30dB说明图像质量差;
  • 最后,PSNR低于20dB图像不可接受。

代码实现

# PSNR# 方法一:自定义函数 import numpy
import math
def psnr(img1, img2):mse = numpy.mean( (img1 - img2) ** 2 )if mse == 0:return 100PIXEL_MAX = 255.0return 20 * math.log10(PIXEL_MAX / math.sqrt(mse))# 方法二:调用库函数# 老版本的scikit-image,加载SSIM、PSNR、MSE的方式:
from skimage.measure import compare_ssim as ssim
from skimage.measure import compare_psnr as psnr
from skimage.measure import compare_mse as mse# 新版本的scikit-image,加载方式:
from skimage.metrics import structural_similarity as ssim
from skimage.metrics import peak_signal_noise_ratio as psnr
from skimage.metrics import mean_squared_error as mse

SSIM(structural similarity)结构相似性

SSIM(structural similarity),结构相似性,是一种衡量两幅图像相似度的指标。

SSIM算法主要用于检测两张相同尺寸的图像的相似度、或者检测图像的失真程度。

SSIM公式基于样本x和y之间的三个比较衡量:亮度 (luminance)、对比度 (contrast) 和结构 (structure)。

SSIM公式

 常数𝑪𝟏, 𝑪𝟐, 𝑪𝟑是为了避免当分母为 0 时造成的不稳定问题。
𝝁𝒙为均值, 𝝈𝒙 为方差, 𝝈𝒙𝒚 表示协方差。

 

 

SSIM取值范围为[0,1],值越大表示输出图像和无失真图像的差距越小,即图像质量越好。

缺点:结构相似性指标有其限制,对于影像出现位移、缩放、旋转(皆属于非结构性的失真)的情况无法有效的运作。

代码实现

# SSIM# 方法一:自定义函数
# 很奇怪,计算出来和调用函数结果差异很大,建议直接调用函数
import numpy as np
def ssim(y_true , y_pred):u_true = np.mean(y_true)u_pred = np.mean(y_pred)var_true = np.var(y_true)var_pred = np.var(y_pred)std_true = np.sqrt(var_true)std_pred = np.sqrt(var_pred)c1 = np.square(0.01*7)c2 = np.square(0.03*7)ssim = (2 * u_true * u_pred + c1) * (2 * std_pred * std_true + c2)denom = (u_true ** 2 + u_pred ** 2 + c1) * (var_pred + var_true + c2)return ssim / denom# 方法二:调用库函数# 老版本的scikit-image,加载SSIM、PSNR、MSE的方式:
from skimage.measure import compare_ssim as ssim
from skimage.measure import compare_psnr as psnr
from skimage.measure import compare_mse as mse# 新版本的scikit-image,加载方式:
from skimage.metrics import structural_similarity as ssim
from skimage.metrics import peak_signal_noise_ratio as psnr
from skimage.metrics import mean_squared_error as mse

函数 structural_similarity

def structural_similarity(*, im1, im2,win_size=None, gradient=False, data_range=None,multichannel=False, gaussian_weights=False,full=False, **kwargs)

处理RGB等多通道图像或划分为多块时

上述原始计算方式仅法针对单通道灰度图,

求RGB图或多通道图时,PSNR有三种方式,其他类似:

  • 分别计算 RGB 三个通道的 PSNR,然后取平均值。
  • 计算 RGB 三通道的 MSE ,然后再除以 3 。
  • 将图片转化为 YCbCr 格式,然后只计算 Y 分量也就是亮度分量的 PSNR。

分为多块时,在实际应用中,可以利用滑动窗将图像分块,令分块总数为N,考虑到窗口形状对分块的影响,采用高斯加权计算每一窗口的均值、方差以及协方差,然后计算对应块的结构相似度SSIM,最后将平均值作为两图像的结构相似性度量,即平均结构相似性SSIM。  

例:计算两个RGB图像的MSE(均方误差): 

  1. ‎从红色通道开始‎;
  2. ‎计算两个图像的红色通道中每个像素的灰度值之间的差异(所有像素位置的(redA(0,0)-redB(0,0)等);
  3. ‎对每个像素的差异进行平方  (redA(0,0)-redB(0,0)^2;
  4. ‎计算红色通道中所有像素的平方差之和‎;
  5. ‎对绿色和蓝色通道重复上述操作‎;
  6. ‎将 3 个通道的总和相加并除以 3,即 (红色通道和+绿色通道和+蓝色通道和)/ 3‎;
  7. ‎除以图像面积(宽度高度)‎‎以形成平均值或平均值,即(红和+绿和+蓝和)/(3* 宽度*高度)‎‎= MSE‎。
     

余弦相似度

把图片表示成一个向量,通过计算向量之间的余弦距离来表征两张图片的相似度。

余弦相似度算法:一个向量空间中两个向量夹角间的余弦值作为衡量两个个体之间差异的大小,余弦值接近1,夹角趋于0,表明两个向量越相似,余弦值接近于0,夹角趋于90度,表明两个向量越不相似。 

余弦公式 

二维空间余弦函数

多维空间余弦函数

代码实现

图片相似度计算方法总结 - 知乎 (zhihu.com)

# -*- coding: utf-8 -*-
# !/usr/bin/env python
# 余弦相似度计算
from PIL import Image
from numpy import average, dot, linalg
# 对图片进行统一化处理
def get_thum(image, size=(64, 64), greyscale=False):# 利用image对图像大小重新设置, Image.ANTIALIAS为高质量的image = image.resize(size, Image.ANTIALIAS)if greyscale:# 将图片转换为L模式,其为灰度图,其每个像素用8个bit表示image = image.convert('L')return image
# 计算图片的余弦距离
def image_similarity_vectors_via_numpy(image1, image2):image1 = get_thum(image1)image2 = get_thum(image2)images = [image1, image2]vectors = []norms = []for image in images:vector = []for pixel_tuple in image.getdata():vector.append(average(pixel_tuple))vectors.append(vector)# linalg=linear(线性)+algebra(代数),norm则表示范数# 求图片的范数norms.append(linalg.norm(vector, 2))a, b = vectorsa_norm, b_norm = norms# dot返回的是点积,对二维数组(矩阵)进行计算res = dot(a / a_norm, b / b_norm)return res
image1 = Image.open('010.jpg')
image2 = Image.open('011.jpg')
cosin = image_similarity_vectors_via_numpy(image1, image2)
print('图片余弦相似度', cosin)

哈希相似度 Hash

图片相相似度计算(Hash、SSIM、compareHist)_南苏月的博客-CSDN博客_图像相似度计算公式

哈希算法-图片相似度计算_chenghaoy的博客-CSDN博客_均值哈希算法相似度

实现图片相似度比较的hash算法有三种:均值哈希算法(AHash),差值哈希算法(DHash),感知哈希算法 (PHash)。

  • aHash:平均值哈希。速度比较快,但是常常不太精确。
  • pHash:感知哈希。精确度比较高,但是速度方面较差一些。
  • dHash:差异值哈希。精确度较高,且速度也非常快。

哈希不是以严格的方式计算Hash值,而是以更加相对的方式计算哈希值,因为“相似”与否,就是一种相对的判定。值哈希算法差值哈希算法感知哈希算法都是值越小,相似度越高,取值为0-64,即汉明距离中,64位的hash值有多少不同。三直方图单通道直方图的值为0-1,值越大,相似度越高。

均值哈希算法(AHash)

一张图片就是一个二维信号,它包含了不同频率的成分。亮度变化小的区域是低频成分,它描述大范围的信息。而亮度变化剧烈的区域(比如物体的边缘)就是高频的成分,它描述具体的细节。或者说高频可以提供图片详细的信息,而低频可以提供一个框架。 而一张大的,详细的图片有很高的频率,而小图片缺乏图像细节,所以都是低频的。所以我们平时的下采样,也就是缩小图片的过程,实际上是损失高频信息的过程。均值哈希算法就是利用图片的低频信息。
具体步骤

  1. 缩小尺寸:将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。
  2. 简化色彩:将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。
  3. 计算平均值:计算所有64个像素的灰度平均值
  4. 比较像素的灰度:将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
  5. 计算哈希值:将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。

最后得到两张图片的指纹信息后,计算两组64位数据的汉明距离,即对比数据不同的位数,不同位数越少,表明图片的相似度越大。
分析: 均值哈希算法计算速度快,不受图片尺寸大小的影响,但是缺点就是对均值敏感,例如对图像进行伽马校正或直方图均衡就会影响均值,从而影响最终的hash值。

感知哈希算法 (PHash)

感知哈希算法是一个比均值哈希算法更为健壮的一种算法,与均值哈希算法的区别在于感知哈希算法是通过DCT(离散余弦变换)来获取图片的低频信息。
离散余弦变换(DCT)是种图像压缩算法,它将图像从像素域变换到频率域。然后一般图像都存在很多冗余和相关性的,所以转换到频率域之后,只有很少的一部分频率分量的系数才不为0,大部分系数都为0(或者说接近于0)。经过DCT变换后的系数矩阵从左上角到右下角频率越来越高,因此图片的能量主要保留在左上角的低频系数上了。
具体步骤

  1. 缩小尺寸:pHash以小图片开始,但图片大于8x8,32x32是最好的。这样做的目的是简化了DCT的计算,而不是减小频率。
  2. 简化色彩:将图片转化成灰度图像,进一步简化计算量。
  3. 计算DCT:计算图片的DCT变换,得到32x32的DCT系数矩阵。
  4. 缩小DCT:虽然DCT的结果是32x32大小的矩阵,但我们只要保留左上角的8x8的矩阵,这部分呈现了图片中的最低频率。
  5. 计算平均值:如同均值哈希一样,计算DCT的均值。
  6. 计算hash值:这是最主要的一步,根据8x8的DCT矩阵,设置0或1的64位的hash值,大于等于DCT均值的设为”1”,小于DCT均值的设为“0”。组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。

分析: 结果并不能告诉我们真实性的低频率,只能粗略地告诉我们相对于平均值频率的相对比例。只要图片的整体结构保持不变,hash结果值就不变。能够避免伽马校正或颜色直方图被调整带来的影响。对于变形程度在25%以内的图片也能精准识别。

差值哈希算法(DHash)

比pHash,dHash的速度要快的多,相比aHash,dHash在效率几乎相同的情况下的效果要更好,它是基于渐变实现的。
主要步骤:

  1. 缩小尺寸:收缩到8x9(高x宽)的大小,一遍它有72的像素点
  2. 转化为灰度图:把缩放后的图片转化为256阶的灰度图。
  3. 计算差异值:dHash算法工作在相邻像素之间,这样每行9个像素之间产生了8个不同的差异,一共8行,则产生了64个差异值
  4. 获得指纹:如果左边的像素比右边的更亮,则记录为1,否则为0.

代码实现

import cv2
import numpy as np
#感知哈希算法
def pHash(image): image = cv2.resize(image,(32,32), interpolation=cv2.INTER_CUBIC) image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) 
#     cv2.imshow('image', image)
#     cv2.waitKey(0)
#     cv2.destroyAllWindows()# 将灰度图转为浮点型,再进行dct变换 dct = cv2.dct(np.float32(image))
#     print(dct)# 取左上角的8*8,这些代表图片的最低频率 # 这个操作等价于c++中利用opencv实现的掩码操作 # 在python中进行掩码操作,可以直接这样取出图像矩阵的某一部分 dct_roi = dct[0:8,0:8]  avreage = np.mean(dct_roi) hash = [] for i in range(dct_roi.shape[0]): for j in range(dct_roi.shape[1]): if dct_roi[i,j] > avreage: hash.append(1) else: hash.append(0) return hash#均值哈希算法
def aHash(image):#缩放为8*8image=cv2.resize(image,(8,8),interpolation=cv2.INTER_CUBIC)#转换为灰度图image=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)avreage = np.mean(image) hash = [] for i in range(image.shape[0]): for j in range(image.shape[1]): if image[i,j] > avreage: hash.append(1) else: hash.append(0) return hash#差值感知算法
def dHash(image):#缩放9*8image=cv2.resize(image,(9,8),interpolation=cv2.INTER_CUBIC)#转换灰度图image=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
#     print(image.shape)hash=[]#每行前一个像素大于后一个像素为1,相反为0,生成哈希for i in range(8):for j in range(8):if image[i,j]>image[i,j+1]:hash.append(1)else:hash.append(0)return hash#计算汉明距离
def Hamming_distance(hash1,hash2): num = 0for index in range(len(hash1)): if hash1[index] != hash2[index]: num += 1return num
if __name__ == "__main__":image_file1 = './data/cartoon1.jpg'image_file2 = './data/cartoon3.jpg'img1 = cv2.imread(image_file1)img2 = cv2.imread(image_file2)hash1 = pHash(img1)hash2 = pHash(img2)dist = Hamming_distance(hash1, hash2)#将距离转化为相似度similarity = 1 - dist * 1.0 / 64 print(dist)print(similarity)

直方图距离

图片的相似度--直方图距离 - 知乎 (zhihu.com)

直方图距离描述

方法描述:按照某种距离度量的标准对两幅图像的直方图进行相似度的测量。

图像直方图丰富的图像细节信息,反映了图像像素点的概率分布情况,统计每一个像素点强度值具有的像素个数。

  • 优点:计算量比较小。
  • 缺点: 直方图反应的是图像灰度值得概率分布,并没有图像的空间位置信息在里面,因此,会出现误判;比如纹理结构相同,但明暗不同的图像,应该相似度很高,但实际结果是相似度很低,而纹理结构不同,但明暗相近的图像,相似度却很高。

计算步骤:

  1. 将图片resize,得到相同大小的图片;
  2. 将图片灰度,灰度后图片的像素在[0-255]之间;
  3. 计算图片的直方图数据,统计相同像素点的概率分布;
  4. 根据相关性计算公式,计算两个图片直方图的相关性。

代码实现

import cv2
def calculate(image1, image2):# 灰度直方图算法# 计算单通道的直方图的相似值hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0])hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0])# 计算直方图的重合度degree = 0for i in range(len(hist1)):if hist1[i] != hist2[i]:degree = degree + (1 - abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i]))else:degree = degree + 1degree = degree / len(hist1)return degreedef classify_hist_with_split(image1, image2, size=(256, 256)):# RGB每个通道的直方图相似度# 将图像resize后,分离为RGB三个通道,再计算每个通道的相似值image1 = cv2.resize(image1, size)image2 = cv2.resize(image2, size)sub_image1 = cv2.split(image1)sub_image2 = cv2.split(image2)sub_data = 0for im1, im2 in zip(sub_image1, sub_image2):sub_data += calculate(im1, im2)sub_data = sub_data / 3return sub_data

互信息(Mutual Information)

归一化互信息(NMI)评价指标_易_的博客-CSDN博客_nmi指标

信息熵、相对熵、互信息 

信息熵:对信息进行量化度量。可以理解为某种特定信息的出现概率。

相对熵:(relative entropy),又被称为Kullback-Leibler散度(Kullback-Leibler divergence,KL散度)或信息散度(information divergence),是两个概率分布(probability distribution)间差异的非对称性度量 。在在信息理论中,相对熵等价于两个概率分布的信息熵(Shannon entropy)的差值。

互信息:(Mutual Information)是信息论里一种有用的信息度量,它可以看成是一个随机变量中包含的关于另一个随机变量的信息量,或者说是一个随机变量由于已知另一个随机变量而减少的不肯定性。

归一化互信息:将互信息放在[0,1]之间,容易评价算法的好坏。 

 代码实现

from sklearn import metrics as mrimg1 = cv2.imread('1.png')
img2 = cv2.imread('2.png')
img2 = cv2.resize(img2, (img1.shape[1], img1.shape[0])
nmi = mr.normalized_mutual_info_score(img1.reshape(-1), img2.reshape(-1))

像素匹配 pixelmatch

GitHub - whtsky/pixelmatch-py: A fast pixel-level image comparison library, originally created to compare screenshots in tests.

利用像素之间的匹配来计算相似度 

# https://github.com/whtsky/pixelmatch-py# 第一步:pip install pixelmatch# 第二步:from PIL import Image
from pixelmatch.contrib.PIL import pixelmatchimg_a = Image.open("a.png")
img_b = Image.open("b.png")
img_diff = Image.new("RGBA", img_a.size)
# note how there is no need to specify dimensions
mismatch = pixelmatch(img_a, img_b, img_diff, includeAA=True)
img_diff.save("diff.png")

举例1——使用四种方法计算图片相似度:MD5、直方图、PSNR、SSIM

有史以来最全的图像相似度算法_小殊小殊的博客-CSDN博客_图片相似度

使用四种方法计算图片相似度:MD5(粗暴的md5比较,判断是否完全相同)、直方图、PSNR、SSIM。

import numpy as np
from PIL import Image
from skimage.metrics import structural_similarity
import cv2
import os
import hashlib
import math'''粗暴的md5比较 返回是否完全相同
'''
def md5_similarity(img1_path, img2_path):file1 = open(img1_path, "rb")file2 = open(img2_path, "rb")md = hashlib.md5()md.update(file1.read())res1 = md.hexdigest()md = hashlib.md5()md.update(file2.read())res2 = md.hexdigest()return res1 == res2def normalize(data):return data / np.sum(data)'''直方图相似度相关性比较 cv2.HISTCMP_CORREL:值越大,相似度越高相交性比较 cv2.HISTCMP_INTERSECT:值越大,相似度越高卡方比较 cv2.HISTCMP_CHISQR:值越小,相似度越高巴氏距离比较 cv2.HISTCMP_BHATTACHARYYA:值越小,相似度越高
'''
def hist_similarity(img1, img2, hist_size=256):imghistb1 = cv2.calcHist([img1], [0], None, [hist_size], [0, 256])imghistg1 = cv2.calcHist([img1], [1], None, [hist_size], [0, 256])imghistr1 = cv2.calcHist([img1], [2], None, [hist_size], [0, 256])imghistb2 = cv2.calcHist([img2], [0], None, [hist_size], [0, 256])imghistg2 = cv2.calcHist([img2], [1], None, [hist_size], [0, 256])imghistr2 = cv2.calcHist([img2], [2], None, [hist_size], [0, 256])distanceb = cv2.compareHist(normalize(imghistb1), normalize(imghistb2), cv2.HISTCMP_CORREL)distanceg = cv2.compareHist(normalize(imghistg1), normalize(imghistg2), cv2.HISTCMP_CORREL)distancer = cv2.compareHist(normalize(imghistr1), normalize(imghistr2), cv2.HISTCMP_CORREL)meandistance = np.mean([distanceb, distanceg, distancer])return meandistancedef PSNR(img1, img2):mse = np.mean((img1/255. - img2/255.) ** 2)if mse == 0:return 100PIXEL_MAX = 1return 20 * math.log10(PIXEL_MAX / math.sqrt(mse))def SSIM(img1, img2):gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)# 计算两个灰度图像之间的结构相似度score, diff = structural_similarity(gray1, gray2, win_size=101, full=True)# diff = (diff * 255).astype("uint8")# print("SSIM:{}".format(score))return score, diffdef ssim(img1, img2):u_true = np.mean(img1)u_pred = np.mean(img2)var_true = np.var(img1)var_pred = np.var(img2)std_true = np.sqrt(var_true)std_pred = np.sqrt(var_pred)c1 = np.square(0.01*7)c2 = np.square(0.03*7)ssim = (2 * u_true * u_pred + c1) * (2 * std_pred * std_true + c2)denom = (u_true ** 2 + u_pred ** 2 + c1) * (var_pred + var_true + c2)return ssim/denomdef MSE(img1,img2):mse = np.mean( (img1 - img2) ** 2 )return mseif __name__ == '__main__':img1_path = '1_08.jpg'img2_path = '2_08.jpg'img1 = cv2.imread(img1_path)img2 = cv2.imread(img2_path)# 1.粗暴的md5比较 返回是否完全相同print('md5_similarity:', md5_similarity(img1_path, img2_path))# 2.直方图相似度print('hist_similarity:', hist_similarity(img1, img2))# 3.PSNRprint('PSNR:', PSNR(img1, img2))# 4.SSIMprint('灰度图SSIM:', SSIM(img1, img2)[0])print('RGB图_ssim:', ssim(img1, img2))print('RGB图_structural_similarity:', structural_similarity(img1,img2, multichannel=True))# 5.MSEprint('MSE:', MSE(img1, img2))

效果展示:

两图
md5_similarity: False
hist_similarity: 0.1344447074377748
PSNR: 12.980832701995697
灰度图SSIM: 0.5966066785284522
RGB图_ssim: 0.9917382398395064
RGB图_structural_similarity: 0.525279441002218
MSE: 86.05895699395074

举例2——批量计算PSNR、SSIM、MSE

import os
import numpy as np
from glob import glob
import cv2
from skimage.measure import compare_mse,compare_ssim,compare_psnrdef read_img(path):return cv2.imread(path,cv2.IMREAD_GRAYSCALE)def mse(tf_img1, tf_img2):return compare_mse(tf_img1,tf_img2)def psnr(tf_img1, tf_img2):return compare_psnr(tf_img1,tf_img2)def ssim(tf_img1, tf_img2):return compare_ssim(tf_img1,tf_img2)def main():WSI_MASK_PATH1 = 'E:/test/A/'WSI_MASK_PATH2 = 'E:/test/B/'path_real = glob(os.path.join(WSI_MASK_PATH1, '*.jpg'))path_fake = glob(os.path.join(WSI_MASK_PATH2, '*.jpg'))list_psnr = []list_ssim = []list_mse = []for i in range(len(path_real)):t1 = read_img(path_real[i])t2 = read_img(path_fake[i])result1 = np.zeros(t1.shape,dtype=np.float32)result2 = np.zeros(t2.shape,dtype=np.float32)cv2.normalize(t1,result1,alpha=0,beta=1,norm_type=cv2.NORM_MINMAX,dtype=cv2.CV_32F)cv2.normalize(t2,result2,alpha=0,beta=1,norm_type=cv2.NORM_MINMAX,dtype=cv2.CV_32F)mse_num = mse(result1, result2)psnr_num = psnr(result1, result2)ssim_num = ssim(result1, result2)list_psnr.append(psnr_num)list_ssim.append(ssim_num)list_mse.append(mse_num)#输出每张图像的指标:print("{}/".format(i+1)+"{}:".format(len(path_real)))str = "\\"print("image:"+path_real[i][(path_real[i].index(str)+1):])print("PSNR:", psnr_num)print("SSIM:", ssim_num)print("MSE:",mse_num)#输出平均指标:print("平均PSNR:", np.mean(list_psnr))  # ,list_psnr)print("平均SSIM:", np.mean(list_ssim))  # ,list_ssim)print("平均MSE:", np.mean(list_mse))  # ,list_mse)if __name__ == '__main__':main()

举例3——计算Y通道或RGB通道的PSNR和SSIM

 Python计算Y通道或者RGB通道的PSNR_SSIM_未知量0520的博客-CSDN博客_folder_gt 

  • 添加:PSNR/SSIM计算结果,保存文件(txt),便于后期导入excel列表分析。
  • 代码介绍:PSNR_SSIM效果和matlab计算的结果一致(本人没有验证)。
  • 代码功能,能够计算图像的Y_channel 或者RGB_channel状态下的PSNR_SSIM结果
  • 用法:将真实图片,生成图片分别放入两个文件夹,代码中有选择两种方式(only_Y, RGB),切换注释相应的代码行即可。
  • 注意:安装相应的代码库。
'''
calculate the PSNR and SSIM.
same as MATLAB's results
https://blog.csdn.net/qq_45041702/article/details/120997743'''
import os
import math
import numpy as np
import cv2
import glob
import osdef main():# Configurations# GT - Ground-truth;# Gen: Generated / Restored / Recovered imagesfolder_GT = '/content/drive/MyDrive/Experiment/codes/26_PSNR_SSIM/All/groud_truth_png'folder_Gen = '/content/drive/MyDrive/Experiment/codes/26_PSNR_SSIM/All/1_ESPCN_png'#save the psnr and ssim score by txtPS_path = '/content/drive/MyDrive/Experiment/codes/26_PSNR_SSIM/Score'if not os.path.exists(PS_path):print('NO_path to sava the NIQE_Score,Making....')os.makedirs(PS_path)else:print("The NIQE_Score_path has existed")PS_txt = open(PS_path +'/score1.txt', 'a' )crop_border = 4  # same with scalesuffix = ''  # suffix for Gen imagestest_Y = False  # True: test Y channel only; False: test RGB channelsPSNR_all = []SSIM_all = []img_list = sorted(glob.glob(folder_GT + '/*'))if test_Y:print('Testing Y channel.')else:print('Testing RGB channels.')for i, img_path in enumerate(img_list):base_name = os.path.splitext(os.path.basename(img_path))[0]print(base_name)im_GT = cv2.imread(img_path) / 255.#不同格式图像# im_Gen = cv2.imread(os.path.join(folder_Gen, base_name + suffix + '.tif')) / 255.im_Gen = cv2.imread(os.path.join(folder_Gen, base_name + suffix + '.png')) / 255.if test_Y and im_GT.shape[2] == 3:  # evaluate on Y channel in YCbCr color spaceim_GT_in = bgr2ycbcr(im_GT)im_Gen_in = bgr2ycbcr(im_Gen)else:im_GT_in = im_GTim_Gen_in = im_Gen# crop bordersif crop_border == 0:cropped_GT = im_GT_incropped_Gen = im_Gen_inelse:if im_GT_in.ndim == 3:cropped_GT = im_GT_in[crop_border:-crop_border, crop_border:-crop_border, :]cropped_Gen = im_Gen_in[crop_border:-crop_border, crop_border:-crop_border, :]elif im_GT_in.ndim == 2:cropped_GT = im_GT_in[crop_border:-crop_border, crop_border:-crop_border]cropped_Gen = im_Gen_in[crop_border:-crop_border, crop_border:-crop_border]else:raise ValueError('Wrong image dimension: {}. Should be 2 or 3.'.format(im_GT_in.ndim))#不同通道数(Y通道和RGB三个通道),需要更改# calculate PSNR and SSIM# PSNR = calculate_psnr(cropped_GT * 255, cropped_Gen * 255)PSNR = calculate_rgb_psnr(cropped_GT * 255, cropped_Gen * 255)SSIM = calculate_ssim(cropped_GT * 255, cropped_Gen * 255)print('{:3d} - {:25}. \tPSNR: {:.4f} dB, \tSSIM: {:.4f}'.format(i + 1, base_name, PSNR, SSIM))PSNR_all.append(PSNR)SSIM_all.append(SSIM)single_info = '[{}],PSNR(dB),{:.4f}, SSIM,{:.4f}'PS_txt.write(single_info.format(base_name,PSNR,SSIM))PS_txt.write("\n")Mean_format = 'Mean_PSNR: {:.4f}, Mean_SSIM: {:.4f}'Mean_PSNR =  sum(PSNR_all) / len(PSNR_all)Mean_SSIM =  sum(SSIM_all) / len(SSIM_all)print(Mean_format.format(Mean_PSNR,Mean_SSIM))PS_txt.write(Mean_format.format(Mean_PSNR,Mean_SSIM))PS_txt.write('\n')def calculate_psnr(img1, img2):# img1 and img2 have range [0, 255]img1 = img1.astype(np.float64)img2 = img2.astype(np.float64)mse = np.mean((img1 - img2)**2)if mse == 0:return float('inf')return 20 * math.log10(255.0 / math.sqrt(mse))def calculate_rgb_psnr(img1, img2):"""calculate psnr among rgb channel, img1 and img2 have range [0, 255]"""n_channels = np.ndim(img1)sum_psnr = 0for i in range(n_channels):this_psnr = calculate_psnr(img1[:,:,i], img2[:,:,i])sum_psnr += this_psnrreturn sum_psnr/n_channelsdef ssim(img1, img2):C1 = (0.01 * 255)**2C2 = (0.03 * 255)**2img1 = img1.astype(np.float64)img2 = img2.astype(np.float64)kernel = cv2.getGaussianKernel(11, 1.5)window = np.outer(kernel, kernel.transpose())mu1 = cv2.filter2D(img1, -1, window)[5:-5, 5:-5]  # validmu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5]mu1_sq = mu1**2mu2_sq = mu2**2mu1_mu2 = mu1 * mu2sigma1_sq = cv2.filter2D(img1**2, -1, window)[5:-5, 5:-5] - mu1_sqsigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sqsigma12 = cv2.filter2D(img1 * img2, -1, window)[5:-5, 5:-5] - mu1_mu2ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) *(sigma1_sq + sigma2_sq + C2))return ssim_map.mean()def calculate_ssim(img1, img2):'''calculate SSIMthe same outputs as MATLAB'simg1, img2: [0, 255]'''if not img1.shape == img2.shape:raise ValueError('Input images must have the same dimensions.')if img1.ndim == 2:return ssim(img1, img2)elif img1.ndim == 3:if img1.shape[2] == 3:ssims = []for i in range(img1.shape[2]):ssims.append(ssim(img1[..., i], img2[..., i]))return np.array(ssims).mean()elif img1.shape[2] == 1:return ssim(np.squeeze(img1), np.squeeze(img2))else:raise ValueError('Wrong input image dimensions.')def bgr2ycbcr(img, only_y=True):'''same as matlab rgb2ycbcronly_y: only return Y channelInput:uint8, [0, 255]float, [0, 1]'''in_img_type = img.dtypeimg.astype(np.float32)if in_img_type != np.uint8:img *= 255.# convertif only_y:rlt = np.dot(img, [24.966, 128.553, 65.481]) / 255.0 + 16.0else:rlt = np.matmul(img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786],[65.481, -37.797, 112.0]]) / 255.0 + [16, 128, 128]if in_img_type == np.uint8:rlt = rlt.round()else:rlt /= 255.return rlt.astype(in_img_type)if __name__ == '__main__':main()

结果展示: 

举例4——调用SSIM算法,结合opencv阈值分割和轮廓提取,找两图差异

SSIM---结构相似性算法(OpenCV+Python)_hedgehog__的博客-CSDN博客_python 结构相似性 通过调用skimage.metrics包下的SSIM算法,结合OpenCV的阈值分割及轮廓提取算法,找出两幅图像的差异。

# https://blog.csdn.net/hedgehog__/article/details/107257755import cv2
import imutils
from skimage.metrics import structural_similarity
import time
from skimage import filters, img_as_ubyte
import numpy as npstart = time.time()
# 读入图像,转为灰度图像
src = cv2.imread('C:/Users/Hedgehog/Desktop/right.jpg')
img = cv2.imread('C:/Users/Hedgehog/Desktop/left.jpg')
grayA = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 计算两个灰度图像之间的结构相似度
(score, diff) = structural_similarity(grayA, grayB, win_size=101, full=True)
diff = (diff * 255).astype("uint8")
cv2.namedWindow("diff", cv2.WINDOW_NORMAL)
cv2.imshow("diff", diff)
print("SSIM:{}".format(score))# 找到不同的轮廓以致于可以在表示为 '不同'的区域放置矩形
# 全局自适应阈值分割(二值化),返回值有两个,第一个是阈值,第二个是二值图像
dst = cv2.threshold(diff, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
cv2.namedWindow("threshold", cv2.WINDOW_NORMAL)
cv2.imshow('threshold', dst)
# findContours找轮廓,返回值有两个,第一个是轮廓信息,第二个是轮廓的层次信息(“树”状拓扑结构)
# cv2.RETR_EXTERNAL:只检测最外层轮廓
# cv2.CHAIN_APPROX_SIMPLE:压缩水平方向、垂直方向和对角线方向的元素,保留该方向的终点坐标,如矩形的轮廓可用4个角点表示
contours, hierarchy = cv2.findContours(dst.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print(contours)
newimg = np.zeros(dst.shape, np.uint8)  # 定义一个和图像分割处理后相同大小的黑色图
# drawContours画轮廓,将找到的轮廓信息画出来
cv2.drawContours(newimg, contours, -1, (255, 255, 255), 1)
cv2.namedWindow("contours", cv2.WINDOW_NORMAL)
cv2.imshow('contours', newimg)
# cnts = cnts[0] if imutils.is_cv3() else cnts[0]    取findContours函数的第一个返回值,即取轮廓信息# 找到一系列区域,在区域周围放置矩形
for c in contours:(x, y, w, h) = cv2.boundingRect(c)  # boundingRect函数:计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的cv2.rectangle(src, (x, y), (x + w, y + h), (0, 255, 0), 2)  # rectangle函数:使用对角线的两点pt1,pt2画一个矩形轮廓cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)  # 画矩形的图, pt1, pt2,(对角线两点的坐标), 矩形边框的颜色,矩形边框的粗细end = time.time()
print(end - start)
# 用cv2.imshow 展现最终对比之后的图片
cv2.namedWindow("right", cv2.WINDOW_NORMAL)
cv2.imshow('right', src)
cv2.namedWindow("left", cv2.WINDOW_NORMAL)
cv2.imshow('left', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

效果展示:

参考博客

计算两幅图像的相似度总结_JerrySing的博客-CSDN博客_compare_ssim

Python计算图片之间的相似度_~小疯子~的博客-CSDN博客_python 识别图片相似度

有史以来最全的图像相似度算法_小殊小殊的博客-CSDN博客_图片相似度

(超详细)实现计算图片相似度MSE和PSNR_Natuki丶的博客-CSDN博客_计算psnr和mse

Python批量计算PSNR、SSIM、MSE_杨杨杨Garrick的博客-CSDN博客

图像质量评价指标MSE/PSNR/SSIM_秋天的从从的博客-CSDN博客_图像mse

图片相相似度计算(Hash、SSIM、compareHist)_南苏月的博客-CSDN博客_图像相似度计算公式

【OpenCV-Python】:图像PSNR、SSIM、MSE计算_米开朗琪罗~的博客-CSDN博客_图像的mse计算

Python计算Y通道或者RGB通道的PSNR_SSIM_未知量0520的博客-CSDN博客_folder_gt

scikit-image 0.18.0版本计算PSNR、SSIM、MSE(Python代码)_YZBshanshan的博客-CSDN博客_scikit-image版本

图像质量评估中的PSNR和SSIM的定义,公式和含义_·如烟·的博客-CSDN博客_psnr定义

【基础知识】图像去噪评估—PSNR和SSIM - 梁君牧 - 博客园 (cnblogs.com)

SSIM---结构相似性算法(OpenCV+Python)_hedgehog__的博客-CSDN博客_python 结构相似性

图片相似度计算方法总结 - 知乎 (zhihu.com)

相似度计算方法(三) 余弦相似度_潘永青的博客-CSDN博客_余弦相似度

图片的相似度--直方图距离 - 知乎 (zhihu.com)

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

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

相关文章

什么是HTML?

关于每篇博文的浪漫主义 【4k】镰仓的灰色与青 | irkyar | sonya74【【4k】镰仓的灰色与青 | irkyar | sonya74】 https://www.bilibili.com/video/BV14G411A7eX/?share_sourcecopy_web&vd_source385ba0043075be7c24c4aeb4aaa73352 网页 1.1什么是网页 网站是指在因特网…

排列(全排,前一个,下一个)

目录 一&#xff0c; 前一个排列&#xff08;字典序更大的最近的一个排列&#xff09; 1.1 底层实现 1.2 prev_permulation() 二&#xff0c;下一个排列&#xff08;字典序更小的最近的一个排列&#xff09; 1.1 底层实现 1.2 next_permulation() 三&#xff0c;全排列 …

C++基础——函数重载

目录 一.函数 重载的三种形式&#xff1a; 1.参数个数不同的重载函数 2. 类型上的不同重载函数 3. 类型顺序上的不同重载函数 二.函数重载缺省参数 返回值不同&#xff0c;能不能构成函数重载&#xff1f; 重载&#xff1a; 重新赋予意义&#xff0c;可以认为是一词多义&…

AssetBundle检测服务使用指南

熟悉UWA的老朋友都知道&#xff0c;UWA有个经典的免费SaaS服务 - 「在线AssetBundle检测」&#xff0c;可以检测包括纹理、网格、动画片段、材质等项目资源使用情况和具体冗余程度。通过快速查看特定的AssetBundle、资源文件和脚本信息&#xff0c;以及AssetBundle和资源之间的…

JAVA线程池 -clt设计与分析

1. 前言 ctl 是线程池源码中常常用到的一个变量。它的主要作用是记录线程池的生命周期状态和当前工作的线程数。作者通过巧妙的设计&#xff0c;将一个整型变量按二进制位分成两部分&#xff0c;分别表示两个信息。 2. 源码解析 源码部分 /*** The main pool control state,…

Pytorch学习笔记6——时间序列RNN

Pytorch学习笔记6——时间序列 判断态度的二分类问题&#xff1a; 对于长句子不适合&#xff0c;因此需要权值共享&#xff1a; 对于某一层&#xff1a; 上一步输出作为下一步输入&#xff0c;存储语境信息&#xff1a; 自我更新语境信息ht ht是最后一次送入得到的m…

提出创意的方法

提出创意的方法 “我不擅长想出创意。” 你是否经常对自己说这样的话? 在公司的会议和学校的集体工作中总是提不出好点子。 即使提出了很多创意&#xff0c;也会偏向于相同的创意&#xff0c;无法得出有趣的提案。 为了打破这样的状况&#xff0c;这次的博客介绍创造性想法…

排序算法:插入排序、冒泡排序、选择排序、希尔排序、堆排序、快速排序

排序算法相关总结&#xff0c;涉及的排序算法有&#xff1a;插入排序、冒泡排序、选择排序、希尔排序、堆排序、快速排序。 这里写目录标题1.插入排序2.冒泡排序3.选择排序4.希尔排序5.堆排序6.快速排序总结稳定性概念: 假定在待排序的记录序列中&#xff0c;存在多个具有相同的…

缓冲区的管理

文章目录什么是缓冲区&#xff1f;有什么作用&#xff1f;单缓冲单缓冲和双缓冲通信时的区别循环缓冲区缓冲池什么是缓冲区&#xff1f;有什么作用&#xff1f; 缓冲区是一个存取区域&#xff0c;可以由专门的硬件寄存器组成&#xff0c;也可以用内存作为缓冲区&#xff0c;本节…

Python中的几种推导式用法,先收藏再说......

嗨害大家好鸭&#xff01;我是小熊猫❤ 今天我们来整点非常干的干货&#xff1a; 源码、资料电子书点击此处 Python 推导式是一种独特的数据处理方式&#xff0c; 可以从一个数据序列构建另一个新的数据序列的结构体。 Python 支持各种数据结构的推导式&#xff1a; 列表(li…

D. Maximum Sum on Even Positions(最大连续字段和)

Problem - 1373D - Codeforces 题意: 给你一个由n个整数组成的数组a。数组的索引从零开始&#xff08;即第一个元素是a0&#xff0c;第二个是a1&#xff0c;以此类推&#xff09;。 你最多可以扭转这个数组的一个子数组&#xff08;连续子段&#xff09;。回顾一下&#xff0c…

[附源码]计算机毕业设计JAVAjsp不回头药店药品管理系统

[附源码]计算机毕业设计JAVAjsp不回头药店药品管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SS…

【Prometheus】什么是prometheus?prometheus简介

本文目录 一、什么是prometheus&#xff1f; 二、体系结构概述 三、适用场景 四、不适用的场景 一、什么是prometheus&#xff1f; Prometheus官网 Prometheus开源代码 From metrics to insight Power your metrics and alerting with the leading open-source monitoring…

随想录一刷Day48——动态规划

文章目录Day48_动态规划29. 打家劫舍30. 打家劫舍 II31. 打家劫舍 IIIDay48_动态规划 29. 打家劫舍 198. 打家劫舍 思路&#xff1a; 题目的关键是&#xff0c;不能偷相邻的两个屋子&#xff0c;即只能偷上一个屋子不偷当前屋子&#xff0c;或者不偷上一个屋子偷当前屋子。 d…

LeetCode刷题(python版)——Topic57插入区间

一、题设 给你一个 无重叠的 &#xff0c;按照区间起始端点排序的区间列表。 在列表中插入一个新的区间&#xff0c;你需要确保列表中的区间仍然有序且不重叠&#xff08;如果有必要的话&#xff0c;可以合并区间&#xff09;。 示例 1&#xff1a; 输入&#xff1a;interva…

ServletConfig和ServletContext接口

一、ServletConfig接口详解 1、简介 Servlet 容器初始化 Servlet 时&#xff0c;会为这个 Servlet 创建一个 ServletConfig 对象&#xff0c;并将 ServletConfig 对象作为参数传递给 Servlet 。通过 ServletConfig 对象即可获得当前 Servlet 的初始化参数信息。一个 Web 应用中…

【仿牛客网笔记】 Redis,一站式高性能存储方案——Redis入门

Redis可以开发对性能要求较高的功能。还可以利用Redis重构我们现有的功能。 NoSQL关系型数据库之外的统称。 快照有称为RDB 以快照的形式 不适合实时的去做&#xff0c;适合一段时间做一次。 日志又称AOF 以日志的形式每执行一次就存入到硬盘中&#xff0c;可以做到实时的存储以…

【python的静态方法,classmethod方法和__call___魔法方法】

classmethod魔法方法和staticmethodstaticmethod&#xff0c;静态方法classmethod&#xff0c;绑定类方法__call__&#xff0c;可调用类类方法staticmethod&#xff0c;静态方法 在python中&#xff0c;使用静态方法可以实现不需要实例化对象的绑定就可以直接调用的函数&#…

【Unity3D】游戏物体操作 ④ ( 选中多个游戏物体操作 | 复制选中物体 | 聚焦选中物体 | 激活、禁用选中物体 | 对齐选中物体 )

文章目录一、选中多个游戏物体操作1、Scene 场景窗口选中多个物体2、Hierarchy 层级窗口选中多个物体二、复制选中物体1、使用 " Ctrl D " 快捷键复制物体2、使用 右键菜单 | Duplicate 选项复制三、聚焦选中物体四、激活、禁用选中物体五、对齐选中物体一、选中多个…

计算机组成原理浮点数表示

浮点数表示 浮点数的表示分为阶码和尾数&#xff1b; 比如3.026*1011;阶码是11;尾数是3.026&#xff1b; 对于阶码&#xff1a; 阶符为正&#xff0c;小数点向后移n位&#xff08;n表示阶的大小&#xff09;; 阶符为负&#xff0c;小数点向前移n位&#xff08;n表示阶的大小&a…