Giter VIP home page Giter VIP logo

piqa's Introduction

PyTorch Image Quality Assessment

PIQA is a collection of PyTorch metrics for image quality assessment in various image processing tasks such as generation, denoising, super-resolution, interpolation, etc. It focuses on the efficiency, conciseness and understandability of its (sub-)modules, such that anyone can easily reuse and/or adapt them to its needs.

PIQA should be pronounced pika (like Pikachu ⚡️)

Installation

The piqa package is available on PyPI, which means it is installable via pip.

pip install piqa

Alternatively, if you need the latest features, you can install it from the repository.

pip install git+https://github.com/francois-rozet/piqa

Getting started

In piqa, each metric is associated to a class, child of torch.nn.Module, which has to be instantiated to evaluate the metric. All metrics are differentiable and support CPU and GPU (CUDA).

import torch
import piqa

# PSNR
x = torch.rand(5, 3, 256, 256)
y = torch.rand(5, 3, 256, 256)

psnr = piqa.PSNR()
l = psnr(x, y)

# SSIM
x = torch.rand(5, 3, 256, 256, requires_grad=True).cuda()
y = torch.rand(5, 3, 256, 256).cuda()

ssim = piqa.SSIM().cuda()
l = 1 - ssim(x, y)
l.backward()

Like torch.nn built-in components, these classes are based on functional definitions of the metrics, which are less user-friendly, but more versatile.

from piqa.ssim import ssim
from piqa.utils.functional import gaussian_kernel

kernel = gaussian_kernel(11, sigma=1.5).repeat(3, 1, 1)
ss, cs = ssim(x, y, kernel=kernel)

For more information, check out the documentation at piqa.readthedocs.io.

Available metrics

Class Range Objective Year Metric
TV [0, ∞] / 1937 Total Variation
PSNR [0, ∞] max / Peak Signal-to-Noise Ratio
SSIM [0, 1] max 2004 Structural Similarity
MS_SSIM [0, 1] max 2004 Multi-Scale Structural Similarity
LPIPS [0, ∞] min 2018 Learned Perceptual Image Patch Similarity
GMSD [0, ∞] min 2013 Gradient Magnitude Similarity Deviation
MS_GMSD [0, ∞] min 2017 Multi-Scale Gradient Magnitude Similarity Deviation
MDSI [0, ∞] min 2016 Mean Deviation Similarity Index
HaarPSI [0, 1] max 2018 Haar Perceptual Similarity Index
VSI [0, 1] max 2014 Visual Saliency-based Index
FSIM [0, 1] max 2011 Feature Similarity
FID [0, ∞] min 2017 Fréchet Inception Distance

Tracing

All metrics of piqa support PyTorch's tracing, which optimizes their execution, especially on GPU.

ssim = piqa.SSIM().cuda()
ssim_traced = torch.jit.trace(ssim, (x, y))

l = 1 - ssim_traced(x, y)  # should be faster ¯\_(ツ)_/¯

Assert

PIQA uses type assertions to raise meaningful messages when a metric doesn't receive an input of the expected type. This feature eases a lot early prototyping and debugging, but it might hurt a little the performances. If you need the absolute best performances, the assertions can be disabled with the Python flag -O. For example,

python -O your_awesome_code_using_piqa.py

Alternatively, you can disable PIQA's type assertions within your code with

piqa.utils.set_debug(False)

Contributing

If you have a question, an issue or would like to contribute, please read our contributing guidelines.

piqa's People

Contributors

francois-rozet avatar ilpoli avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

piqa's Issues

RuntimeError with piqa.MS_SSIM

Description

RuntimeError using MS_SSIM with updated PyTorch.

Reproduce

[trougnouf@uryzen:~]$ python                                                                                                                                                  (03-03 14:58)
Python 3.10.2 (main, Jan 15 2022, 19:56:27) [GCC 11.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> import piqa
>>> piqa.MS_SSIM()(torch.rand(1,3,256,256), torch.rand(1,3,256,256))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1110, in _call_impl
    return forward_call(*input, **kwargs)
  File "/usr/lib/python3.10/site-packages/piqa/ssim.py", line 324, in forward
    l = ms_ssim(
  File "/usr/lib/python3.10/site-packages/piqa/ssim.py", line 180, in ms_ssim
    ss, cs = ssim(
  File "/usr/lib/python3.10/site-packages/piqa/ssim.py", line 100, in ssim
    mu_x = channel_convs(x, window, pad)
  File "/usr/lib/python3.10/site-packages/piqa/utils/functional.py", line 75, in channel_convs
    x = channel_conv(x, k)
  File "/usr/lib/python3.10/site-packages/piqa/utils/functional.py", line 39, in channel_conv
    return F.conv1d(x, kernel, padding=padding, groups=x.size(1))
RuntimeError: Expected 2D (unbatched) or 3D (batched) input to conv1d, but got input of size: [1, 3, 256, 256]

Environment

  • PIQA version: 1.1.7+4+g48ec8c4-1
  • PyTorch version: 1.11.0rc3-1
  • Cuda version: 11.6.0-1
  • Python version: 3.10.2
  • OS: Arch Linux

Option to disable type assertions

✨ Feature

Description

An argument to disable the use of value_range, the code that checks if the input tensors don't exceed 0-1 or 0-255. Or just disabling the entirety of _assert_type, I'm going to realize very quickly if my tensors are the wrong shape even without these and the only assert that's prone to go off suddenly is the value_range one.

The motivation is to enable the use of piqa for optimization, which often leads to values exceeding this range.

I recognize that metrics may cease to offer useful information if the values exceed the intended ranges, but other losses present during training may work well enough to bring it back into appropriate levels.

I've tried disabling asserts in Python via !export PYTHONOPTIMIZE="True" and variations thereof but it doesn't on Colab. I've tried %env PYTHONOPTIMIZE="True" and this allows me to run !python -c "assert False" without an assertion error, but piqa still gives assertion errors.

Implementation

An arg/kwarg like self.skip_assert_value = kwargs.get('skip_assert_value', False)
And in the forward pass for modules with asserts:

        _assert_type(
            [input, target],
            device=self.kernel.device,
            dim_range=(4, 4),
            n_channels=3,
            if self.skip_assert_value == False:
                value_range=(0., self.value_range),
        )

Alternatives

An alternate solution I've considered is just copying piqa and removing the asserts entirely so the asserts don't halt my training, but I can't be the only person with an interest in using piqa for training and this impacts anyone using piqa for training. I've also re-implemented SSIM on my own without the asserts and it seemed to work fine.

Make downsampling optional in quality metrics

✨ Feature

Description

Right now, several metrics, like MDSI, HaarPSI, VSI etc., first downsample the image before calculating the distance (The downsampling is done by a fixed factor of 2 in HaarPSI, and to a size of at most 256 in MDSI and VSI. I didn't check the other modules, if they have something like this too).

This can cause problems if these are used as target losses for neural networks: for instance, if the network learns to add a chessboard-like-pattern (-1 if x+y is odd and 1 if it's even) to the image, the metric will not be changed at all, since the downsampled image hasn't been changed.

It's not just a theoretical problem, such patterns do arise in practice if the last layer is ConvTranspose2d.

Implementation

Add a kwarg downsample=True to these metrics (in both class and functional formats). It can have the default value of True to preserve backward compatibility. The images won't be downsampled if it is False.

Alternatives

None.

RuntimeError with non-contiguous tensors in PSNR

Description

Applying PSNR to non-contiguous tensors leads to the following RuntimeError:

RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.

See #28 for the original bug report.

Reproduce

import torch
from piqa import PSNR

x = torch.rand(1, 3, 32, 32).transpose(-1, -2)
y = torch.rand(1, 3, 32, 32).transpose(-1, -2)

PSNR()(x, y)  # RuntimeError

Causes and solution

In piqa.psnr.mse, the squared error does not necessarily make the error term (x - y) ** 2 contiguous if both x and y are non-contiguous, which is problematic for the subsequent view. As the error message suggests, using reshape instead fixes the problem.

Environment

  • PIQA version: 1.2.2
  • PyTorch version: 1.8.0
  • Python version: 3.8.10
  • OS: Ubuntu 20.04

JIT TypeError: Object of type 'module' is not an instance of 'function' with Python 3.9.1

🐛 Bug

Description

I cannot build the package on Arch Linux with PyTorch 1.7.1 and Python 3.9 (using aur , PKGBUILD)

Traceback (most recent call last):
  File "/home/trougnouf/.cache/pacaur/python-pytorch-piqa-git/src/piqa/setup.py", line 5, in <module>
    import piqa
  File "/home/trougnouf/.cache/pacaur/python-pytorch-piqa-git/src/piqa/piqa/__init__.py", line 10, in <module>
    from .tv import TV
  File "/home/trougnouf/.cache/pacaur/python-pytorch-piqa-git/src/piqa/piqa/tv.py", line 16, in <module>
    def tv(x: torch.Tensor, norm: str = 'L1') -> torch.Tensor:
  File "/usr/lib/python3.9/site-packages/torch/jit/_script.py", line 939, in script
    fn = torch._C._jit_script_compile(
TypeError: Object of type 'module' is not an instance of 'function'

This may be a bug in PyTorch JIT, incompatibility with newer Numpy, or other, but I'm reporting here since this is the only place I've encountered the bug so far and I haven't seen another report.

Reproduce

On an up to date system:

git clone [email protected]:francois-rozet/piqa.git
cd piqa
python setup.py build

alternatively, with piqa succesfully built with older libraries and the system then updated, the same error is triggered with "import piqa"

Expected behavior

No error

Causes and solution

Workaround: set PIQA_JIT=0

Environment

  • PIQA version: 5994e34
  • Python version: 3.9.1
  • OS: Arch Linux
  • Numpy 1.20
  • PyTorch 1.7.1

Extension to images with number of channels > 3 ?

Hi,
Some metrics (at least SSIM) could be extended to 2D tensors (images) with more than 3 channels as the metrics are computed on a channel basis and then averaged over the channels, correct ?
Best regards,
Laurent

RuntimeError in README example

Description

I have tried to run given SSIM example in README but it gives below error.

Reproduce

A minimal working example demonstrating the current behavior.

import torch
from piqa.ssim import ssim
from piqa.utils.functional import gaussian_kernel

x = torch.rand(5, 3, 256, 256, requires_grad=True).cuda()
y = torch.rand(5, 3, 256, 256).cuda()

kernel = gaussian_kernel(11, sigma=1.5).expand(3, 11, 11)

l = 1 - ssim(x, y, kernel=kernel)

Expected behavior

Correct output for l with SSIM loss, which can be obtained with below code.

import torch
import piqa

# SSIM
x = torch.rand(5, 3, 256, 256, requires_grad=True).cuda()
y = torch.rand(5, 3, 256, 256).cuda()

ssim = piqa.SSIM(window_size=11, sigma=1.5, n_channels = 3, reduction = 'mean').cuda()
l = 1 - ssim(x, y)
l.backward()

print(l)

output : tensor(0.9923, device='cuda:0', grad_fn=<RsubBackward1>)

Possible issue

Error in the example code, possibly with number of channels and Gaussian kernel mapping.

Environment

  • PIQA version: 1.3.1
  • PyTorch version: 2.1.0+cu118
  • Python version: 3.9.18
  • OS: Windows10

Error

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Cell In[1], line 10
      6 y = torch.rand(5, 3, 256, 256).cuda()
      8 kernel = gaussian_kernel(11, sigma=1.5).expand(3, 11, 11)
---> 10 l = 1 - ssim(x, y, kernel=kernel)

File k:\Miniconda3\envs\pytorch\lib\site-packages\torch\jit\_trace.py:1230, in _script_if_tracing.<locals>.wrapper(*args, **kwargs)
   1226 @functools.wraps(fn)
   1227 def wrapper(*args, **kwargs):
   1228     if not is_tracing():
   1229         # Not tracing, don't do anything
-> 1230         return fn(*args, **kwargs)
   1232     compiled_fn = script(wrapper.__original_fn)  # type: ignore[attr-defined]
   1233     return compiled_fn(*args, **kwargs)

File k:\Miniconda3\envs\pytorch\lib\site-packages\piqa\ssim.py:102, in ssim(x, y, kernel, channel_avg, padding, value_range, k1, k2)
     99     pad = 0
    101 # Mean (mu)
--> 102 mu_x = channel_convs(x, window, pad)
    103 mu_y = channel_convs(y, window, pad)
    105 mu_xx = mu_x ** 2

File k:\Miniconda3\envs\pytorch\lib\site-packages\piqa\utils\functional.py:86, in channel_convs(x, kernels, padding)
     83     x = F.pad(x, pad=pad)
     85 for k in kernels:
---> 86     x = channel_conv(x, k)
     88 return x

File k:\Miniconda3\envs\pytorch\lib\site-packages\piqa\utils\functional.py:46, in channel_conv(x, kernel, padding)
     44     return F.conv3d(x, kernel, padding=padding, groups=x.shape[-4])
     45 elif D == 2:
---> 46     return F.conv2d(x, kernel, padding=padding, groups=x.shape[-3])
     47 elif D == 1:
     48     return F.conv1d(x, kernel, padding=padding, groups=x.shape[-2])

RuntimeError: Given groups=3, weight of size [3, 11, 11, 1], expected input[5, 3, 256, 256] to have 33 channels, but got 3 channels instead

SSIM not working for negative numbers

Hi!

SSIM module isn't accepting negative numbers, as well as numbers greater than 1. This isn't an issue when working with photographic data, but when working with numerical data it's a hindrance. E.g.from skimage.metrics import structural_similarity handles negative numbers well. Could you please implement this? Or is it enough for me to edit the lines in ssim.py?
Cheers!

AssertionError using piqa.SSIM

🐛 Bug

Getting AssertionError for computing SSIM value between a model predticted image and ground truth image

Traceback (most recent call last):  
File "train.py", line 133, in <module>    
    train(opt)  
File "train.py", line 69, in train    
    loss = criterion(labels, preds)  
File "/opt/ml/code/loss.py", line 90, in sr_loss
   l3 = SSIMLoss(y_true[:,0:3,:,:], y_pred[:,0:3,:,:]) 
 File "/opt/ml/code/loss.py", line 35, in SSIMLoss    
    return (1 - ssim(y_true, y_pred))  
File "/opt/conda/lib/python3.6/site-packages/torch/nn/modules/module.py", line 1102, in _call_impl    
    return forward_call(*input, **kwargs)  
File "/opt/conda/lib/python3.6/site-packages/piqa/ssim.py", line 259, in forward    
    value_range=(0., self.value_range),  
File "/opt/conda/lib/python3.6/site-packages/piqa/utils/__init__.py", line 83, in _assert_type    
    'Expected values to be lower or equal to'
--
AssertionError: Expected values to be lower or equal to 1.0, got 1.242782473564148

Reproduce

A minimal working example demonstrating the current behavior.

from piqa import SSIM

class SSIMLoss(SSIM):
    def forward(self, x, y):
        return 1. - super().forward(x, y)

criterion = SSIMLoss() # .cuda() if you need GPU support

...
loss = criterion(x, y)
...

Expected behavior

I have two doubts:

  1. Why is the module calculating SSIM > 1.0
  2. If it is, why is it getting caught as an assertion

Causes and solution

NA

Environment

  • PIQA version: 1.1.7
  • Python version: 3.6
  • OS: MacOS Monterey 12.1

Performance benchmarks and unification suggestion

Hey there.
I have a question regarding your claim on efficiency boost compared to kornia, piq and IQA-pytorch. Prevously, 4x speed up was claimed. Currently, I see up to 2x speed up mentioned in the README.md. Could you provide any benchmarks that would support these statements? It would also be great to see if this performance boost holds in case of invalid or partically valid user data.
Another question is whether you considered to join the piq project. Your contribution to already well established library would be appreciated and could potentially bring more impact rather than development of a very similar product in parallel. If there are some reasons for not to join other than a wish to develop your own library, could you share them?

Different SSIM between piqa and skimage

Description

Piqa and skimage give different ssim using save input.

Reproduce

import torch
import numpy as np
import imageio.v2 as iio

from piqa import SSIM, PSNR
from skimage.metrics import structural_similarity
from skimage.metrics import peak_signal_noise_ratio


if __name__ == '__main__':

    # random input
    r = np.random.RandomState(42)
    image1 = r.rand(256, 256, 3)
    image2 = r.rand(256, 256, 3)

    # images in same shape
    # image1 = iio.imread('image1.png') / 255.0
    # image2 = iio.imread('image2.png') / 255.0

    image1_tensor = torch.from_numpy(image1.transpose(2, 0, 1)[None]).float().contiguous()
    image2_tensor = torch.from_numpy(image2.transpose(2, 0, 1)[None]).float().contiguous()

    sk_psnr = peak_signal_noise_ratio(image1, image2)
    sk_ssim = structural_similarity(
        image1, image2,
        win_size=7, sigma=1.5, multichannel=True,
        data_range=1.0, K1=0.01, K2=0.03
    )

    piqa_psnr = PSNR()(image1_tensor, image2_tensor)
    piqa_ssim = SSIM(
        window_size=7, sigma=1.5, n_channels=3,
        channel_avg=True, value_range=1.0,
        padding=False, k1=0.01, k2=0.03
    )(image1_tensor, image2_tensor)

    print(f'SKImage PSNR: {sk_psnr}')
    print(f'SKImage SSIM: {sk_ssim}')
    print(f'PIQA SSIM:    {piqa_psnr.item()}')
    print(f'PIQA SSIM:    {piqa_ssim.item()}')

  # output
  # SKImage PSNR: 7.778893543723596
  # SKImage SSIM: 0.0045113893857824895
  # PIQA SSIM:    7.77889347076416
  # PIQA SSIM:    0.004785331431776285

Expected behavior

SSIMs provided by piqa and skimage are more different than other metrics.
It is more obvious when using real images.
Could piqa be more consistent with skimage?
Thanks.

Environment

  • piqa version: 1.2.2
  • pytorch version: 1.12.0+cu113
  • python version: 3.8.13
  • numpy version: 1.23.0
  • scikit-image version: 0.18.3
  • imageio version: 2.19.3
  • os: Ubuntu 20.04

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.