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

42 篇文章 16 订阅
32 篇文章 1 订阅

一、PSNR

1.1 原理

PSNR 是一种衡量图像质量的指标,它是通过比较原始图像和失真图像之间的差异来计算的。具体来说,PSNR 是通过比较两幅图像的每个像素值来计算的。

在这里插入图片描述

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

1.2 代码

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)

1.3 运行测试结果

在这里插入图片描述

二、SSIM

2.1 原理

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

在这里插入图片描述

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

2.2 代码


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("SSIM_result = ", ssims_result)

2.3 运行测试结果

在这里插入图片描述

三、总结

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

  • 15
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

视觉研坊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值