图像质量评估——PSNR:峰值信噪比和SSIM:结构相似性(纯手撸代码)

news2024/12/22 23:56:22

目录

  • PSNR
    • 原理
    • 代码
    • 运行测试结果
  • SSIM
    • 原理
    • 代码
    • 运行测试结果
  • 总结

PSNR

原理

PSNR 是一种衡量图像质量的指标,它是通过比较原始图像和失真图像之间的差异来计算的。具体来说,PSNR 是通过比较两幅图像的每个像素值来计算的。给定一个大小为 m×n 的干净图像 I 和噪声图像 K,均方误差 (MSE) 定义为:

在这里插入图片描述
然后 PSNR (dB) 就定义为:
在这里插入图片描述
其中 MAX_I^2
为图片可能的最大像素值。如果每个像素都由 8 位二进制来表示,那么就为 255。通常,如果像素值由 B 位二进制来表示,那么 MAX_I = 2^B-1。

PSNR 主要比较的是两幅图像的每个像素值的差异,这种差异被称为 “噪声”。如果两幅图像完全相同,那么噪声就为零,PSNR 就为无穷大。如果两幅图像有很大的差异,那么噪声就会很大,PSNR 就会相应地减小。因此,PSNR 越大,表示图像质量越好。

代码

import numpy as np
import cv2

def reorder_image(img, input_order='HWC', output_order='HWC'):
    ''' reorder_image '''
    if input_order not in ['HWC', 'CHW']:
        raise ValueError(f'Wrong input_order {input_order}. Supported input_orders are ' "'HWC' and 'CHW'")
    if output_order not in ['HWC', 'CHW']:
        raise ValueError(f'Wrong output_order {output_order}. Supported output_orders are ' "'HWC' and 'CHW'")
    if len(img.shape) == 2:
        img, input_order = img[..., None], 'CHW'
    if input_order == 'CHW' and output_order == 'HWC':
        img = img.transpose(1, 2, 0)
    elif input_order == 'HWC' and output_order == 'CHW':
        img = img.transpose(2, 0, 1)
    return img

def _convert_input_type_range(img):
    ''' convert input to [0, 1] '''
    img_type = img.dtype
    img = img.astype(np.float32)
    if img_type == np.float32:
        pass
    elif img_type == np.uint8:
        img /= 255.
    else:
        raise TypeError(f'The img type should be np.float32 or np.uint8, but got {img_type}')
    return img
    
def _convert_output_type_range(img, dst_type):
    ''' convert output to dst_type '''
    if dst_type not in (np.uint8, np.float32):
        raise TypeError(f'The dst_type should be np.float32 or np.uint8, but got {dst_type}')
    if dst_type == np.uint8:
        img = img.round()
    else:
        img /= 255.
    return img.astype(dst_type)

def bgr2ycbcr(img, y_only=False):
    ''' bgr space to ycbcr space '''
    img_type = img.dtype
    img = _convert_input_type_range(img)
    if y_only:
        out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0
    else:
        out_img = np.matmul(
            img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], [65.481, -37.797, 112.0]]) + [16, 128, 128]
    out_img = _convert_output_type_range(out_img, img_type)
    return out_img

def calculate_psnr(img, img2, crop_border, input_order='HWC', test_y_channel=False, **_kwargs):
    ''' calculate_psnr '''
    assert img.shape == img2.shape, (f'Image shapes are different: {img.shape}, {img2.shape}.')
    if input_order not in ['HWC', 'CHW']:
        raise ValueError(f'Wrong input_order {input_order}. Supported input_orders are ' '"HWC" and "CHW"')
    if not isinstance(crop_border, (list, tuple)):
        crop_border = (crop_border, crop_border)
    img = reorder_image(img, input_order=input_order).astype(np.float64)
    img2 = reorder_image(img2, input_order=input_order).astype(np.float64)
    if crop_border[0] != 0:
        img = img[crop_border[0]:-crop_border[0], ...]
        img2 = img2[crop_border[0]:-crop_border[0], ...]
    if crop_border[1] != 0:
        img = img[:, crop_border[1]:-crop_border[1], ...]
        img2 = img2[:, crop_border[1]:-crop_border[1], ...]
    if test_y_channel:
        img = bgr2ycbcr(img.astype(np.float32) / 255., y_only=True) * 255
        img2 = bgr2ycbcr(img2.astype(np.float32) / 255., y_only=True) * 255
    mse = np.mean((img - img2)**2)                 # MSE均方误差
    if mse == 0:
        return float('inf')
    PSNR_result = 20. * np.log10(255. / np.sqrt(mse))
    return PSNR_result

if __name__ == '__main__':
	img1 = cv2.imread("datasets/Set5/GTmod12/hh74.png")  # 读入图片1
    img2 = cv2.imread("visualization/Set5/hh74_ETDS_M4C32_x4.png")  # 读入图片2
    PSNR_result = calculate_psnr(img1, img2, 4)

    print("PSNR_result = ",PSNR_result)

运行测试结果

在这里插入图片描述

SSIM

原理

SSIM(结构相似性)是一种衡量两幅图像相似度的指标。相对 PSNR 而言,SSIM 在评价图像质量上更能符合人类的视觉特性。SSIM 使用的两张图像中,一张为未经压缩的无失真图像,另一张为失真后的图像。SSIM 公式基于样本 x 和 y 之间的三个比较衡量:亮度 (luminance)、对比度 (contrast) 和结构 (structure)。具体的计算公式如下所示:

在这里插入图片描述
SSIM 主要比较的是两幅图像的亮度、对比度和结构。这三个因素都是人类视觉系统在评价图像质量时的重要因素。因此,SSIM 能够更好地反映人类视觉系统对图像质量的感知。

代码


import numpy as np
import cv2

def reorder_image(img, input_order='HWC', output_order='HWC'):
    ''' reorder_image '''
    if input_order not in ['HWC', 'CHW']:
        raise ValueError(f'Wrong input_order {input_order}. Supported input_orders are ' "'HWC' and 'CHW'")
    if output_order not in ['HWC', 'CHW']:
        raise ValueError(f'Wrong output_order {output_order}. Supported output_orders are ' "'HWC' and 'CHW'")
    if len(img.shape) == 2:
        img, input_order = img[..., None], 'CHW'
    if input_order == 'CHW' and output_order == 'HWC':
        img = img.transpose(1, 2, 0)
    elif input_order == 'HWC' and output_order == 'CHW':
        img = img.transpose(2, 0, 1)
    return img

def _convert_input_type_range(img):
    ''' convert input to [0, 1] '''
    img_type = img.dtype
    img = img.astype(np.float32)
    if img_type == np.float32:
        pass
    elif img_type == np.uint8:
        img /= 255.
    else:
        raise TypeError(f'The img type should be np.float32 or np.uint8, but got {img_type}')
    return img

def _convert_output_type_range(img, dst_type):
    ''' convert output to dst_type '''
    if dst_type not in (np.uint8, np.float32):
        raise TypeError(f'The dst_type should be np.float32 or np.uint8, but got {dst_type}')
    if dst_type == np.uint8:
        img = img.round()
    else:
        img /= 255.
    return img.astype(dst_type)

def bgr2ycbcr(img, y_only=False):
    ''' bgr space to ycbcr space '''
    img_type = img.dtype
    img = _convert_input_type_range(img)
    if y_only:
        out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0
    else:
        out_img = np.matmul(
            img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], [65.481, -37.797, 112.0]]) + [16, 128, 128]
    out_img = _convert_output_type_range(out_img, img_type)
    return out_img

def _ssim(img, img2):
    ''' ssim '''
    c1, c2 = (0.01 * 255)**2, (0.03 * 255)**2
    img = img.astype(np.float64)
    img2 = img2.astype(np.float64)
    kernel = cv2.getGaussianKernel(11, 1.5)
    window = np.outer(kernel, kernel.transpose())
    mu1 = cv2.filter2D(img, -1, window)[5:-5, 5:-5]
    mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5]
    mu1_sq, mu2_sq = mu1**2, mu2**2
    mu1_mu2 = mu1 * mu2
    sigma1_sq = cv2.filter2D(img**2, -1, window)[5:-5, 5:-5] - mu1_sq
    sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq
    sigma12 = cv2.filter2D(img * img2, -1, window)[5:-5, 5:-5] - mu1_mu2
    ssim_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(img, img2, crop_border, input_order='HWC', test_y_channel=False, **_kwargs):
    ''' calculate_ssim '''
    assert img.shape == img2.shape, (f'Image shapes are different: {img.shape}, {img2.shape}.')
    if input_order not in ['HWC', 'CHW']:
        raise ValueError(f'Wrong input_order {input_order}. Supported input_orders are ' '"HWC" and "CHW"')
    if not isinstance(crop_border, (list, tuple)):
        crop_border = (crop_border, crop_border)
    img = reorder_image(img, input_order=input_order)
    img2 = reorder_image(img2, input_order=input_order)
    img = img.astype(np.float64)
    img2 = img2.astype(np.float64)
    if crop_border[0] != 0:
        img = img[crop_border[0]:-crop_border[0], ...]
        img2 = img2[crop_border[0]:-crop_border[0], ...]
    if crop_border[1] != 0:
        img = img[:, crop_border[1]:-crop_border[1], ...]
        img2 = img2[:, crop_border[1]:-crop_border[1], ...]
    if test_y_channel:
        img = bgr2ycbcr(img.astype(np.float32) / 255., y_only=True)[..., None] * 255
        img2 = bgr2ycbcr(img2.astype(np.float32) / 255., y_only=True)[..., None] * 255
    ssims = []
    for i in range(img.shape[2]):
        ssims.append(_ssim(img[..., i], img2[..., i]))
    ssims_result = np.array(ssims).mean()
    return ssims_result

if __name__ == '__main__':
    img1 = cv2.imread("datasets/Set5/GTmod12/hh74.png")  # 读入图片1
    img2 = cv2.imread("visualization/Set5/hh74_ETDS_M4C32_x4.png")  # 读入图片2
    ssims_result = calculate_ssim(img1, img2, 4)

    print("PSNR_result = ", ssims_result)

运行测试结果

在这里插入图片描述

总结

以上就是图像质量评估——PSNR:峰值信噪比和SSIM:结构相似性的原理及详细代码,希望能帮到你,总结不易,撸码不易,三连多多支持,谢谢!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

董事长孙进任职资格获批,盛京银行坎坷向前

11月6日,国家金融监管总局行政许可信息显示,盛京银行(HK:02066)董事长孙进的任职资格已于近日获准。 作为东北地区成立最早、规模最大的总部银行,盛京银行近年来的发展之路颇为坎坷,在经历了大规模的管理层…

最近的总结(2023.11.8)

菜鸟本来是不打算写文章的,奈何1500的曝光券让我心痒难耐 菜鸟主要是想把这篇博客(平凡人的一生的意义是什么?)推出去,看看大家的看法! 不过既然写,菜鸟自然要好好写,就来聊聊最近…

实现财务自由的十大步骤

一、明确实现财务自由的意义 很多人都希望实现财务自由,但是只有很少人真正想过:我为什么要实现财务自由? 不喜欢干活,不喜欢工作不想让工作束缚自己,不想靠工资收入来维持生活想自由自在,无拘无束环游世界 所以就…

基于袋獾算法的无人机航迹规划-附代码

基于袋獾算法的无人机航迹规划 文章目录 基于袋獾算法的无人机航迹规划1.袋獾搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要:本文主要介绍利用袋獾算法来优化无人机航迹规划。 1.袋獾搜索算法 …

AI全栈大模型工程师(十九)Semantic Kernel

文章目录 Semantic KernelSK 的开发进展SK 的生态位SK 基础架构后记 Semantic Kernel 先比较下 Semantic Kernel 和 LangChain。 Semantic KernelLangChain出品公司微软LangChain AI支持语言Python、C#、Java、TypeScriptPython、TypeScript开源协议MITMIT被应用在Microsoft …

第三章:人工智能深度学习教程-基础神经网络(第四节-从头开始的具有前向和反向传播的深度神经网络 – Python)

本文旨在从头开始实现深度神经网络。我们将实现一个深度神经网络,其中包含一个具有四个单元的隐藏层和一个输出层。实施将从头开始,并实施以下步骤。算法: 1. 可视化输入数据 2. 确定权重和偏置矩阵的形状 3. 初始化矩阵、要使用的函数 4. 前…

各省市90米分辨率DEM数据,多图可下载

之前给大家推了30米分辨率dem数据,有些小伙伴反应也需要90米的,于是今天就给大家推荐一个新数据 —— 各省市90米分辨率DEM数据! 各省市90米分辨率DEM数据广泛应用于国土资源调查、水利水电工程、地质灾害预警、城市规划等领域,对…

JavaFX入门和网格布局面板的使用,Dao层交互,舞台与场景切换以及其他控件的使用

网格布局 将整个面板划分为若干个格子 , 每个格子的大小是一样的 , 每个格子中可以放置一个控件(布局) , 类似于表格的方式。在网格布局 中放入控件的时候 , 还需要指定位置。 GridPane gridPane new GridPane(); 我们将要排出这个布局 , 也就是登陆页…

时间序列预测模型实战案例(十)(CNN-GRU-LSTM)通过堆叠CNN、GRU、LSTM实现多元预测和单元预测

本文介绍 本篇博客为大家讲解的是通过组堆叠CNN、GRU、LSTM个数,建立多元预测和单元预测的时间序列预测模型,其效果要比单用GRU、LSTM效果好的多,其结合了CNN的特征提取功能、GRU和LSTM用于处理数据中的时间依赖关系的功能。通过将它们组合在…

快速构建高质量中文APP登录注册页面Figma源文件

在这个数字化时代,移动应用程序(APP)已经成为我们日常生活中不可或缺的一部分。如果您正在为您的中文APP开发登录注册页面,并寻找高质量的UI设计素材,那么您来对地方了!我们为您提供了一个完整的Figma源文件…

Java语言级别8不支持本地枚举和语言级别 ‘8‘ 不支持 内部类中的 static 声明

Java语言级别8不支持本地枚举和语言级别 8 不支持 内部类中的 static 声明 具体报错情况总结 具体报错情况 今天笔者准备在Test下的测试方法创建枚举类的时候,发现出现了报错Java”语言级别8不支持本地枚举“。 然后又试试创建一个类中包含一个枚举类时,…

JavaFX进阶:学生管理系统结构讲解,复合布局集成,表格数据显示

系统介绍 我们会通过一个学生管理系统来学习 其中 , 分为两个角色 老师 Teacher public class Teacher { private Integer id; private String name; private String password; private String gender; } 学生 Student public class Student { private Integer id; priva…

【3D 图像分割】基于 Pytorch 的 VNet 3D 图像分割10(测试推理篇)

对于直接将裁剪的patch,一个个的放到训练好的模型中进行预测,这部分代码可以直接参考前面的训练部分就行了。其实说白了,就是验证部分。不使用dataloader的方法,也只需要修改少部分代码即可。 但是,这种方法是不end t…

Busco-真核生物为主基因组质量评估

文章目录 简介Install必须参数谱系数据集输出结果自动谱系选择结果解读完整片段化缺失 自动选择:多domain和污染匹配注意BUSCO报告常用脚本真核Ref 简介 Busco评估基因组质量的核心原理在于通过计算基因组的通用单拷贝标记基因的比例来估计基因组的完整性。其中两个…

Javascript知识点详解:对象的继承、原型对象、原型链

目录 对象的继承 原型对象概述 构造函数的缺点 prototype 属性的作用 原型链 constructor 属性 instanceof 运算符 构造函数的继承 多重继承 对象的继承 面向对象编程很重要的一个方面,就是对象的继承。A 对象通过继承 B 对象,就能直接拥有 B …

C++之函数中实现类、调用总结(二百五十四)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…

MySQL之表的增删改查

目录 表的增删改查1.Create1.1 单行数据 全列插入1.2 多行数据 指定列插入1.3 插入否则更新1.4 替换 2.Retrieve1.1 SELECT 列全列查询指定列查询查询字段为表达式为查询结果指定别名结果去重 1.2 WHERE 条件普遍使用NULL 的查询结果排序筛选分页结果 3.Update对查询到的结果…

vue3+setup 解决:this.$refs引用子组件报错 is not a function

一、如果在父组件中以下四步都没问题的话&#xff0c;再看下面步骤 二、如果父组件引用的是index页面 请在 头部加上以下代码 &#xff08;如果是form页面请忽略这一步&#xff09; <template> <a-modalv-model:visible"visible"title"头部名称&…

MySQL库的库操作指南

1.创建数据库 一般格式&#xff1a;create database (if not exists) database1_name,database2_name...... 特殊形式&#xff1a; create database charset harset_name collate collate_name 解释&#xff1a; 红色字是用户自己设置的名称charset&#xff1a;指定数据…

照片处理软件 DxO FilmPack 7 mac中文版软件介绍

DxO FilmPack 7 mac是一款照片处理软件&#xff0c;专为摄影后期制作而设计。该软件来自法国的DXO公司&#xff0c;它可以在数码影像上模拟胶卷的颜色、对比度、颗粒感等。DxO FilmPack 7提供了多种胶卷颜色效果&#xff0c;包括7种正片胶卷颜色、9种单色照片胶卷颜色、5种负片…