Source code for torchjpeg.metrics._psnrb

import numpy as np
import torch
from torch import Tensor


[docs]def blocking_effect_factor(im: Tensor) -> Tensor: r""" Computes the blocking effect factor (BEF) of an image as defined in [1]. Blocking effect factor is used as part of :py:func:`psnrb` but can also be used as an objective measure of "blockiness". Args: im (Tensor): Image of shape :math:`(N, C, H, W)`. Returns: Tensor: The BEF for each image of shape :math:`(N)`. Note: [1] Tadala, Trinadh, and Sri E. Venkata Narayana. "A Novel PSNR-B Approach for Evaluating the Quality of De-blocked Images." (2012). """ # This is a fairly complex formula we're implementing, it would be messy with fewer locals # pylint: disable=too-many-locals block_size = 8 block_horizontal_positions = torch.arange(7, im.shape[3] - 1, 8) block_vertical_positions = torch.arange(7, im.shape[2] - 1, 8) horizontal_block_difference = ((im[:, :, :, block_horizontal_positions] - im[:, :, :, block_horizontal_positions + 1]) ** 2).sum(3).sum(2).sum(1) vertical_block_difference = ((im[:, :, block_vertical_positions, :] - im[:, :, block_vertical_positions + 1, :]) ** 2).sum(3).sum(2).sum(1) nonblock_horizontal_positions = np.setdiff1d(torch.arange(0, im.shape[3] - 1), block_horizontal_positions) nonblock_vertical_positions = np.setdiff1d(torch.arange(0, im.shape[2] - 1), block_vertical_positions) horizontal_nonblock_difference = ((im[:, :, :, nonblock_horizontal_positions] - im[:, :, :, nonblock_horizontal_positions + 1]) ** 2).sum(3).sum(2).sum(1) vertical_nonblock_difference = ((im[:, :, nonblock_vertical_positions, :] - im[:, :, nonblock_vertical_positions + 1, :]) ** 2).sum(3).sum(2).sum(1) n_boundary_horiz = im.shape[2] * (im.shape[3] // block_size - 1) n_boundary_vert = im.shape[3] * (im.shape[2] // block_size - 1) boundary_difference = (horizontal_block_difference + vertical_block_difference) / (n_boundary_horiz + n_boundary_vert) n_nonboundary_horiz = im.shape[2] * (im.shape[3] - 1) - n_boundary_horiz n_nonboundary_vert = im.shape[3] * (im.shape[2] - 1) - n_boundary_vert nonboundary_difference = (horizontal_nonblock_difference + vertical_nonblock_difference) / (n_nonboundary_horiz + n_nonboundary_vert) scaler = np.log2(block_size) / np.log2(min([im.shape[2], im.shape[3]])) bef = scaler * (boundary_difference - nonboundary_difference) bef[boundary_difference <= nonboundary_difference] = 0 return bef
[docs]def psnrb(image: Tensor, target: Tensor) -> Tensor: r""" Computes the peak signal-to-noise ratio with blocking effect factor from [1]. PSNR-B augments the PSNR measure by including the "blockiness" of the degraded image as a way to reduce the PSNR. For multichannel inputs, the PSNR-B is computed separately for each channel and then averaged. Args: image (Tensor): The input images of shape :math:`(N, C, H, W)`. target (Tensor): The target images of shape :math:`(N, C, H, W)`. Returns: Tensor: The PSNR-B of each image in the batch of shape :math:`(N)`. Warning: Unlike most metrics this is not symmetric and the order of the arguents is imporant. Blocking effect factor is only computed for the degraded image, so if the arguments are reversed, there will be very little difference between this and :py:func:`psnr`. Note: PSNR-B is computed as .. math:: P(x, y) = 10 \log_{10}\left(\frac{1}{\text{MSE}(x, y) + \text{BEF}(x)}\right) [1] Tadala, Trinadh, and Sri E. Venkata Narayana. "A Novel PSNR-B Approach for Evaluating the Quality of De-blocked Images." (2012). """ def channel_psnrb(image, target): mse = torch.nn.functional.mse_loss(image, target, reduction="none") bef = blocking_effect_factor(image) mse = mse.view(mse.shape[0], -1).mean(1) return 10 * torch.log10(1 / (mse + bef)) total = torch.stack([channel_psnrb(image[:, c : c + 1, ...], target[:, c : c + 1, ...]) for c in range(image.shape[1])]).sum(0) return total / image.shape[1]