Pillow icon indicating copy to clipboard operation
Pillow copied to clipboard

Resize with LANCZOS

Open richardtml opened this issue 4 years ago • 3 comments

What did you do?

Load the image linked below, convert it to float and resize it with LANCZOS.

What did you expect to happen?

An image with positive pixel values only.

What actually happened?

Return negative pixel values.

What are your OS, Python and Pillow versions?

  • OS: Ubuntu 18.04
  • Python: python-3.6.10 anaconda main
  • Pillow: 7.2.0 anaconda main

alt text

import numpy as np
from PIL import Image
img = Image.open('0a6c60063b4bae4de001caaba306d1_jumbo.jpeg') 
np.array(img.convert('F').resize((50, 50), Image.LANCZOS))
array([[ 8.5948139e-01,  6.6392100e-01,  1.0832241e+00, ...,
         3.5903049e+01,  3.5441189e+01,  3.4633324e+01],
       [-3.8446698e+00, -2.7686529e+00, -4.5956917e+00, ...,
        -6.0899782e-01, -1.5849429e+00, -2.4963107e+00],
       [ 2.4218788e+01,  2.0577053e+01,  3.3584007e+01, ...,
         1.3765612e-01,  3.4020036e-01,  5.1762921e-01],
       ...,
       [ 1.4547446e+02,  1.5638815e+02,  1.6513686e+02, ...,
        -2.9810291e-02, -1.0865730e-02,  2.4887547e+00],
       [ 1.5281839e+02,  1.6102005e+02,  1.6741365e+02, ...,
        -7.9935685e-02,  5.4068506e-01,  4.0174098e+00],
       [ 1.5780322e+02,  1.6550754e+02,  1.7088707e+02, ...,
        -2.7181864e-01,  1.7344187e+00,  7.1707163e+00]], dtype=float32)

Thank you!

richardtml avatar Jul 12 '20 00:07 richardtml

IMHO, this is correct behaviour for float data. Lanzcos (and bicubic) have negative coeffs, and can therefore result in under- and over-shoot around high contrast edges (hence the 'sharper' property of the method). It should be up to the user to handle this in a way they see fit for their application (clipping or re-scaling). For uint data it's a different story: you have to clip during the computation in order to avoid wrap-around.

kmilos avatar Jul 13 '20 08:07 kmilos

If there are negative values in the result, that sounds to me like Pillow has created an invalid image.

radarhere avatar Jul 14 '20 11:07 radarhere

If you do end up "fixing" this, can you at least provide the user the choice to get the untouched filtering result as well please (for float operation only)? Or better yet, please consider keeping the current output for backward compatibility (and parity w/ Matlab, OpenCV, etc...), but maybe add a clipping option (and not only negative values, >1.0 should be treated the same) instead of changing behaviour. See e.g. https://scikit-image.org/docs/stable/api/skimage.transform.html#skimage.transform.resize

kmilos avatar Jul 14 '20 14:07 kmilos

I've created PR #7201 to resolve this by changing the behaviour.

I've left this alone for a while because there are conflicting thoughts here. However, #7184 now also feels this behaviour should be changed. I would introduce an API to allow the behaviour to be toggled on or off, except that F clipping feels very specific.

radarhere avatar Jun 05 '23 13:06 radarhere

For me it also correct and even intended in some situations. Resizing is done separate in directions, with clipping the source for the second pass will be algorithmic incorrect even if you prefer clipped result of the second pass.

@richardtml you can either use LINEAR filter which doesn't have negative lobes, use L mode which makes more sense for jpeg sources or clip the result using numpy.clip.

homm avatar Jun 05 '23 14:06 homm

Closing then, to keep the current behaviour.

radarhere avatar Aug 28 '23 08:08 radarhere