torchmetrics icon indicating copy to clipboard operation
torchmetrics copied to clipboard

More image metrics

Open Borda opened this issue 3 years ago • 24 comments

🚀 Feature

just found this package and seems we have not yet all Image metrics :] some inspiration came from https://github.com/andrewekhalel/sewar but we aim on own implementation with torch

Alternatives

  • ~Mean Squared Error (MSE)~
  • ~Root Mean Sqaured Error (RMSE)~
  • ~Peak Signal-to-Noise Ratio (PSNR)~
  • ~Structural Similarity Index (SSIM)~
  • ~Universal Quality Image Index (UQI)~
  • ~Multi-scale Structural Similarity Index (MS-SSIM)~
  • ~Erreur Relative Globale Adimensionnelle de Synthèse (ERGAS)~
  • [ ] Spatial Correlation Coefficient (SCC) -> #800
  • [ ] Relative Average Spectral Error (RASE) -> #816
  • ~Spectral Angle Mapper (SAM)~
  • ~Spectral Distortion Index (D_lambda)~
  • [ ] Spatial Distortion Index (D_S)
  • [ ] Quality with No Reference (QNR)
  • [ ] Visual Information Fidelity (VIF)
  • [ ] Block Sensitive - Peak Signal-to-Noise Ratio (PSNR-B)

Additional context

any of these metrics would be a separate PR/addition :]

Borda avatar Jan 25 '22 23:01 Borda

i would like to work on this can you assign it to me

nishant42491 avatar Jan 26 '22 06:01 nishant42491

Hi, @Borda

Thanks for creating the issue tracker. Would love to help in this, as I also look forward to contributing to the lightning framework.

I would like to begin with MSE and RMSE, if that's fine with you and everyone else. Please let me know if I can start working on these two metrics.

Thanks!

ankitaS11 avatar Jan 26 '22 07:01 ankitaS11

@ankitaS11 Please note we already have MSE and RMSE in the regression package: https://torchmetrics.readthedocs.io/en/latest/references/modules.html#meansquarederror (this takes an squared argument) So I do not think we should re-implement them in the image domain.

SkafteNicki avatar Jan 26 '22 07:01 SkafteNicki

i would like to work on this can you assign it to me

cool, which one do you want to take? (we have many :zap:)

Borda avatar Jan 26 '22 08:01 Borda

@ankitaS11 Please note we already have MSE and RMSE in the regression package: https://torchmetrics.readthedocs.io/en/latest/references/modules.html#meansquarederror (this takes an squared argument) So I do not think we should re-implement them in the image domain.

Hi, @SkafteNicki - Got it. Thank you for updating the issue as well, I can try Quality with No Reference then, just want to also make sure that it doesn't collide with @nishant42491. Nishant, curious to know if you have any metric in mind to pick up first? I can pick the others then, if that's okay.

ankitaS11 avatar Jan 26 '22 08:01 ankitaS11

@Borda @SkafteNicki are we aiming for an integration or a direct torch reimplementation here?

justusschock avatar Jan 26 '22 09:01 justusschock

@Borda @SkafteNicki are we aiming for an integration or a direct torch reimplementation here?

reimplementation, I put this package just as inspiration for what other image metrics exist...

Borda avatar Jan 26 '22 09:01 Borda

reimplementation, I put this package just as inspiration for what other image metrics exist...

I'd think so as well :)

@ankitaS11 @nishant42491 You can however use this package for testing :)

justusschock avatar Jan 26 '22 09:01 justusschock

@Borda @ankitaS11 may I start with implementing spatial correlation coefficient first?

nishant42491 avatar Jan 26 '22 10:01 nishant42491

@nishant42491 sure, I'll add you to it. Open a draft PR early and ping us there if you need help :)

justusschock avatar Jan 26 '22 10:01 justusschock

@nishant42491 sure, I'll add you to it. Open a draft PR early and ping us there if you need help :)

thank you :)

nishant42491 avatar Jan 26 '22 10:01 nishant42491

I would like to work on Relative Average Spectrul Error, it would be a great way for me to learn new stuff.

Piyush-97 avatar Jan 28 '22 07:01 Piyush-97

Hi, @Borda

I was going through the referenced repo (sewar) for QNR, and found that it depends on: D_LAMBDA and D_S - and both of them depend on UQI. So, probably it will be easier and more sensible to implement UQI first, and then go to D_LAMBDA, D_S and finally QNR.

Do you have any opinions on this? Just wanted to confirm if I'm moving in a right direction here. In a gist, I would like to implement UQI first, and then other metrics if that's okay with you and everyone.

ankitaS11 avatar Feb 01 '22 08:02 ankitaS11

Hey @ankitaS11 in that case it definitely makes sense to go with your proposed way :) I updated the issue. Just let us know here, when I should assign new metrics to you :)

justusschock avatar Feb 01 '22 08:02 justusschock

Hey @ankitaS11 in that case it definitely makes sense to go with your proposed way :) I updated the issue. Just let us know here, when I should assign new metrics to you :)

Hi @justusschock, I am done with the implementation of UQI metric, now I would like to work on D_Lambda.

ankitaS11 avatar Feb 12 '22 17:02 ankitaS11

@Borda I will work on Spectral Angle Mapper (SAM)

vumichien avatar Mar 10 '22 07:03 vumichien

@vumichien if I'm not mistaken, SAM is just the arccos of the cosine similarity which is implemented here.

Paul-Aime avatar Mar 10 '22 08:03 Paul-Aime

@vumichien My bad, SAM being an image metric and being about spectral characteristics, it is needed to assume NCHW shape, and using torchmetrics.functional.cosine_similarity with those assumptions will require some useless reshaping.

Here is some code as an excuse and to explain myself:

Code
import torch
import torchmetrics
import einops


SPECTRAL_DIM = 1
N, C, H, W = 13, 5, 32, 64


def main():

    x = torch.randn(N, C, H, W)
    y = torch.randn(N, C, H, W)

    sam_index1 = sam_from_torchmetrics_cossim(x, y, reduction="mean")
    sam_index2 = sam_from_scratch(x, y, reduction="mean")

    assert torch.allclose(sam_index1, sam_index2)


def sam_from_torchmetrics_cossim(pred, target, *, reduction="mean"):
    # (..., N, d) required for `torchmetrics.functional.cosine_similarity`
    pred_ = einops.rearrange(pred, "n c h w -> n (h w) c")
    target_ = einops.rearrange(target, "n c h w -> n (h w) c")
    cossim = torchmetrics.functional.cosine_similarity(pred_, target_, reduction="none")
    return reduce(cossim.arccos(), reduction=reduction)


def sam_from_scratch(pred, target, *, reduction="mean"):
    return reduce(_angle(pred, target, dim=SPECTRAL_DIM), reduction=reduction)


def _angle(x1, x2, *, dim):
    return _cosine_similarity(x1, x2, dim=dim).acos()


def _cosine_similarity(x1, x2, *, dim):
    x1 = x1 / torch.norm(x1, p=2, dim=dim, keepdim=True)
    x2 = x2 / torch.norm(x2, p=2, dim=dim, keepdim=True)
    return (x1 * x2).sum(dim=dim).clamp(-1.0, 1.0)


def reduce(x, reduction):
    if reduction == "mean":
        return x.mean()
    if reduction == "sum":
        return x.sum()
    if reduction is None or reduction == "none":
        return x
    raise ValueError(
        f"Expected reduction to be one of `['mean', 'sum', None]` but got {reduction}"
    )


if __name__ == "__main__":
    main()

Paul-Aime avatar Mar 10 '22 10:03 Paul-Aime

@Paul-Aime No problems. SAM is a metric to determine the spectral similarity between two images by the angle between each spectrum, in short, it's the mean arccos of the cosine similarity in each dimension. Thank you for your snippet code.

vumichien avatar Mar 10 '22 11:03 vumichien

@Borda I would like to work on Erreur Relative Globale Adimensionnelle de Synthèse (ERGAS) next after the process of reviewing SAM is completed

vumichien avatar Mar 12 '22 02:03 vumichien

Hi @Borda, I was looking into implementing the Spatial Distortion Index, but there is a problem with the inputs. In this metric, a MS image and the respective PAN image are the targets that will be compared against the MS_low image. There is also a PAN_low image, but this is computed in a predefined way from the PAN image. How will the targets and preds look like? One way would be to use the format target = [ms_1, pan_1, ms_2, pan_2, ...] and preds = [ms_low_1, ms_low_2, ...]. I am asking this, because other metrics seem to have the same shape for targets and preds and I am a bit unsure how to handle this in an elegant way.

victor1cea avatar Aug 25 '22 02:08 victor1cea

Hi @victor1cea are ms_1 and ms_2 of the same shape? Are pan_1 and pan_2 of the same shape? Do they have the same size as the ms images?

justusschock avatar Aug 25 '22 05:08 justusschock

The distortions indices were introduced in https://doi.org/10.14358/PERS.74.2.193

formula

The reference implementation used in pansharpening (task for which the metric was introduced) can be found at liangjiandeng/DLPan-Toolbox/.../D_lambda.m (MATLAB).

The reference implementation gives 2 ways of computing the spatial distortion (translated in PyTorch):

torch.mean( (uiqi(hrms, pan) - uiqi(ms,    pan_lr_decimated) ) ** p, axis=-1) ** (1 / p)  # As original paper
# or
torch.mean( (uiqi(hrms, pan) - uiqi(ms_up, pan_lr          ) ) ** p, axis=-1) ** (1 / p)  # As done by default in reference implementation

This is with the classical pansharpening setting as follows:

r = 4  # ratio PAN / MS (pixel size)

# Raw inputs
ms.shape = (N, C, H, W)  # MS
pan.shape = (N, 1, H * r, W * r)  # PAN

# Modified inputs
ms_up.shape = (N, C, H * r, W * r)  # classicaly bicubic interpolation
pan_lr.shape = (N, 1, H * r, W * r)  # same shape as PAN, but degraded (blurred) to the resolution of MS
pan_lr_decimated.shape = (N, 1, H, W)  # same as pan_lr, but then decimated to MS size

# Prediction
hrms.shape = (N, C, H * r, W * r)  # the fused image prediction

Actually, it might be better to let the user choose its implementation by providing both arguments of the reference UIQI (ms and pan_lr below):

torch.mean( (uiqi(hrms, pan) - uiqi(ms, pan_lr) ** p, axis=-1) ** (1 / p)

This way pan_lr can also be optional, and if not given the assumption can be made that the ms provided is of shape (N, C, H, W), as in the original paper, and then the pan_lr will default to torch.interp the pan to ms size, as in the original paper.

Moreover, the interpolation method used to compute pan_lr has quite an impact on the resulting metric, so this is an additional benefit of letting the user choose its method.

Anyway, regarding preds and targets, I think that the best would then be:

targets = [ms, pan] # or [ms, pan, pan_lr], as the user want
preds = [hrms]

ms, pan, pan_lr and hrms being batched, with shapes as described above.

Paul-Aime avatar Aug 25 '22 09:08 Paul-Aime

Thank you! :smile:

victor1cea avatar Aug 25 '22 17:08 victor1cea

@Borda It would be great if you can give me some reference regarding Block Sensitive - Peak Signal-to-Noise Ratio (PSNR-B). Then I would like to work on adding it as a metrics. Thanks!

soma2000-lang avatar Nov 08 '22 07:11 soma2000-lang

@SkafteNicki could you pls link some good resources? :)

Borda avatar Nov 08 '22 11:11 Borda

I can take a stab at Visual Information Fidelity VIF if it hasn't been taken yet @Borda

rschireman avatar Nov 08 '22 18:11 rschireman

@Borda It would be great if you can give me some reference regarding Block Sensitive - Peak Signal-to-Noise Ratio (PSNR-B). Then I would like to work on adding it as a metrics. Thanks!

Hi @soma2000-lang, Here is the reference paper: https://ieeexplore.ieee.org/abstract/document/5535179 and here is a reference implementation in numpy: https://github.com/andrewekhalel/sewar/blob/ac76e7bc75732fde40bb0d3908f4b6863400cc27/sewar/full_ref.py#L371-L395

SkafteNicki avatar Nov 09 '22 09:11 SkafteNicki

Hi @Borda, please assign any metric that needs to be implemented to me. I see a lot of people taking a stab at most of them, so any metric which doesn't conflict with others would be great. Looking forward to hearing back from you.

theja-vanka avatar Dec 04 '22 11:12 theja-vanka