Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

..functional.rotate() arguments broke with 0.5 update #1759

Closed
ghost opened this issue Jan 16, 2020 · 29 comments · Fixed by #1760
Closed

..functional.rotate() arguments broke with 0.5 update #1759

ghost opened this issue Jan 16, 2020 · 29 comments · Fixed by #1760

Comments

@ghost
Copy link

ghost commented Jan 16, 2020

Hey, I just did a fresh install of Pytorch and torchvision on a clean ubuntu 18.04 os.

When I run my code, I get the following Error message
TypeError: function takes exactly 1 argument (3 given)
even though i did not change my code.
torchvision.__version__= 0.5.0
pytorch.__version__=1.4.0

Running the same code on an older machine (0.4.2 and 1.3.1 respectively) works without an issue, I guess it has something to do with the new addition of the fill-colour of the 0.5 update.

How to reproduce:

Img=torch.rand(1,32,32)
Img_PIL=torchvision.transforms.functional.to_pil_image(Img)
Img_PIL_Rot=torchvision.transforms.functional.rotate(Img_PIL, 5)
Img=torchvision.transforms.functional.to_tensor(Img_PIL_Rot)*255

Expeceted result:
rotated Image

Result:

File "/home/.../anaconda3/envs/Python3.7/lib/python3.7/site-packages/PIL/Image.py", line 2544, in new
    return im._new(core.fill(mode, size, color))
TypeError: function takes exactly 1 argument (3 given)
@pmeier
Copy link
Collaborator

pmeier commented Jan 16, 2020

Thanks for the report. Indeed

if isinstance(fill, int):
fill = tuple([fill] * 3)
return img.rotate(angle, resample, expand, center, fillcolor=fill)

causes the error. I will investigate this tomorrow and send a fix.


For your next bug report, please post the full stack trace. That makes it easier for us to investigate where the error might originate from.

@fmassa
Copy link
Member

fmassa commented Jan 16, 2020

Hi,

This error happens because you have an old version of Pillow. If you update pillow, this should work.

@pmeier we should add a compatibility fix for older versions of PIL, and add a test (which was missing before)

@fmassa
Copy link
Member

fmassa commented Jan 16, 2020

FYI, fillcolor is present in Pillow 6, but not in Pillow 5

@pmeier
Copy link
Collaborator

pmeier commented Jan 16, 2020

@fmassa

I don't think this is the issue here. The problem is that we always convert a scalar into a tuple of 3. This works fine for RGB images but fails for greyscale images like in the example given. Actually this will fail for every image with a number of bands / channels different from 3.

From the official documentation:

A new named parameter, fillcolor, has been added to Image.transform. This color specifies the background color to use in the area outside the transformed area in the output image. This parameter takes the same color specifications as used in Image.new.

The documentation of Image.new states:

color - [...] If given, this should be a single integer or floating point value for single-band modes, and a tuple for multi-band modes (one value per band).

Apart from that I agree that we should add a compatibility fix for pillow<5.0.0 and a test for this.


@Jarei002

For a quick fix add fill=(0,) to your call of rotate:

Img=torch.rand(1,32,32)
Img_PIL=torchvision.transforms.functional.to_pil_image(Img)
Img_PIL_Rot=torchvision.transforms.functional.rotate(Img_PIL, 5, fill=(0,))
Img=torchvision.transforms.functional.to_tensor(Img_PIL_Rot)*255

@pmeier pmeier mentioned this issue Jan 16, 2020
4 tasks
@ghost
Copy link
Author

ghost commented Jan 16, 2020

Thanks for the report. Indeed

if isinstance(fill, int):
fill = tuple([fill] * 3)
return img.rotate(angle, resample, expand, center, fillcolor=fill)

causes the error. I will investigate this tomorrow and send a fix.

For your next bug report, please post the full stack trace. That makes it easier for us to investigate where the error might originate from.

Okay, here is the complete stack trace:

Traceback (most recent call last):

  File "<ipython-input-12-db9c93cfce73>", line 4, in <module>
    PermutedImage=torchvision.transforms.functional.rotate(img=PermutedImage,angle=3,fill=(0),)

  File "/home/at-lab/anaconda3/envs/Python3.7/lib/python3.7/site-packages/torchvision/transforms/functional.py", line 729, in rotate
    return img.rotate(angle, resample, expand, center, fillcolor=fill)

  File "/home/at-lab/anaconda3/envs/Python3.7/lib/python3.7/site-packages/PIL/Image.py", line 2023, in rotate
    return self.transform((w, h), AFFINE, matrix, resample, fillcolor=fillcolor)

  File "/home/at-lab/anaconda3/envs/Python3.7/lib/python3.7/site-packages/PIL/Image.py", line 2337, in transform
    im = new(self.mode, size, fillcolor)

  File "/home/at-lab/anaconda3/envs/Python3.7/lib/python3.7/site-packages/PIL/Image.py", line 2544, in new
    return im._new(core.fill(mode, size, color))

TypeError: function takes exactly 1 argument (3 given)

Hi,

This error happens because you have an old version of Pillow. If you update pillow, this should work.

@pmeier we should add a compatibility fix for older versions of PIL, and add a test (which was missing before)

The current PIL version is 7.0.0.

For a quick fix add fill=(0,) to your call of rotate:

As seen from the stack trace, fill=(0), did not fix the issue.

Thanks for the quick responses so far.

@pmeier
Copy link
Collaborator

pmeier commented Jan 16, 2020

You missed a comma there. It should be fill=(0,) and not fill=(0). With that comma you create tuple with a single element. This circumvents the buggy code. Without it the parentheses are just used for priority and simply removed (0 == (0))

@ghost
Copy link
Author

ghost commented Jan 16, 2020

You missed a comma there. It should be fill=(0,) and not fill=(0). With that comma you create tuple with a single element. This circumvents the buggy code. Without it the parentheses are just used for priority and simply removed (0 == (0))

Astonishing that things work when they are done right. Somehow the comma moved outside the parantheses, works now. Thanks again.

As a general question, should I close this issue now, or is this done by someone else at later stages once the actual fix is implemented?

@pmeier
Copy link
Collaborator

pmeier commented Jan 16, 2020

You don't need to close it manually. If the PR I send gets merged, this issue will be closed automatically. Until then its good to be open so others know that this is still worked on.

@IvonaTau
Copy link

I still get the error with transforms.RandomRotation(10, fill=(0,)),:

Traceback (most recent call last):
  File "train-tagan.py", line 423, in <module>
    train(args)
  File "train-tagan.py", line 152, in train
    ranker.update_emb()
  File "/home/itautkute/fashion-iq/scripts/utils.py", line 253, in update_emb
    images, asins = self.get_items(batch_ids)
  File "/home/itautkute/fashion-iq/scripts/utils.py", line 111, in get_items
    i) for i in indexes)
  File "/opt/conda/lib/python3.7/site-packages/joblib/parallel.py", line 1004, in __call__
    if self.dispatch_one_batch(iterator):
  File "/opt/conda/lib/python3.7/site-packages/joblib/parallel.py", line 835, in dispatch_one_batch
    self._dispatch(tasks)
  File "/opt/conda/lib/python3.7/site-packages/joblib/parallel.py", line 754, in _dispatch
    job = self._backend.apply_async(batch, callback=cb)
  File "/opt/conda/lib/python3.7/site-packages/joblib/_parallel_backends.py", line 209, in apply_async
    result = ImmediateResult(func)
  File "/opt/conda/lib/python3.7/site-packages/joblib/_parallel_backends.py", line 590, in __init__
    self.results = batch()
  File "/opt/conda/lib/python3.7/site-packages/joblib/parallel.py", line 256, in __call__
    for func, args, kwargs in self.items]
  File "/opt/conda/lib/python3.7/site-packages/joblib/parallel.py", line 256, in <listcomp>
    for func, args, kwargs in self.items]
  File "/home/itautkute/fashion-iq/scripts/utils.py", line 105, in get_item
    image = self.transform(image)
  File "/opt/conda/lib/python3.7/site-packages/torchvision/transforms/transforms.py", line 70, in __call__
    img = t(img)
  File "/opt/conda/lib/python3.7/site-packages/torchvision/transforms/transforms.py", line 1003, in __call__
    return F.rotate(img, angle, self.resample, self.expand, self.center, self.fill)
  File "/opt/conda/lib/python3.7/site-packages/torchvision/transforms/functional.py", line 729, in rotate
    return img.rotate(angle, resample, expand, center, fillcolor=fill)
  File "/opt/conda/lib/python3.7/site-packages/PIL/Image.py", line 2023, in rotate
    return self.transform((w, h), AFFINE, matrix, resample, fillcolor=fillcolor)
  File "/opt/conda/lib/python3.7/site-packages/PIL/Image.py", line 2337, in transform
    im = new(self.mode, size, fillcolor)
  File "/opt/conda/lib/python3.7/site-packages/PIL/Image.py", line 2544, in new
    return im._new(core.fill(mode, size, color))
TypeError: function takes at least 3 arguments (1 given)

torchvision.__version__ == 0.5.0
PIL.__version__ == 7.0.0

@pmeier
Copy link
Collaborator

pmeier commented Jan 23, 2020

@IvonaTau Could you please post your complete code? A quick test by me run without any error

import numpy as np
from PIL import Image
from torchvision import transforms

img = np.zeros((100, 100), dtype=np.uint8)
img = Image.fromarray(img, mode="L")

transform = transforms.RandomRotation(10, fill=(0,))
transform(img)

@chinglamchoi
Copy link

I had the same problem with torchvision v0.5.0 & PIL v7.0.0. Downgrading to torchvision 0.4.0 & PIL v6.2.0 solved the problem for me.

@pmeier
Copy link
Collaborator

pmeier commented Feb 9, 2020

@chinglamchoi

This is a bug in torchvision==0.5.0. As long as you have pillow>=5.2.0, the actual version is irrelevant.

If you are bound to torchvision==0.5.0 you can add fill=[filler] * num_bands to your rotation parameters. Here filler = 0.0 if img.mode.startswith("F") else 0 and num_bands = len(img.getbands()) where img is an PIL.Image.Image.

If you can use an older version, I suggest you downgrade to torchvision==0.4.2, since this is the last version that does not have this bug.

Alternatively, if you are not bound to a stable version, you can use the nightly version of torchvision, since this bug is already fixed on the master branch (#1760, #1828).

TimoWintz added a commit to romi/romiseg that referenced this issue Feb 21, 2020
@Ge0rges
Copy link

Ge0rges commented Apr 11, 2020

I get this issue as well with torchvision 0.5.0 and Pillow 7.1.1 as well as 7.0.0. Complete trace below:

File "/home/gio/Documents/KNet/src/utils/data_loading.py", line 579, in mnist_class_loader
  traindata[i] = train[i][0].view((28*28))
File "/home/gio/Documents/KNet/venv2/lib/python3.7/site-packages/torchvision/datasets/mnist.py", line 97, in __getitem__
  img = self.transform(img)
File "/home/gio/Documents/KNet/venv2/lib/python3.7/site-packages/torchvision/transforms/transforms.py", line 70, in __call__
  img = t(img)
File "/home/gio/Documents/KNet/venv2/lib/python3.7/site-packages/torchvision/transforms/transforms.py", line 1003, in __call__
  return F.rotate(img, angle, self.resample, self.expand, self.center, self.fill)
File "/home/gio/Documents/KNet/venv2/lib/python3.7/site-packages/torchvision/transforms/functional.py", line 729, in rotate
  return img.rotate(angle, resample, expand, center, fillcolor=fill)
File "/home/gio/Documents/KNet/venv2/lib/python3.7/site-packages/PIL/Image.py", line 2023, in rotate
  return self.transform((w, h), AFFINE, matrix, resample, fillcolor=fillcolor)
File "/home/gio/Documents/KNet/venv2/lib/python3.7/site-packages/PIL/Image.py", line 2337, in transform
  im = new(self.mode, size, fillcolor)
File "/home/gio/Documents/KNet/venv2/lib/python3.7/site-packages/PIL/Image.py", line 2544, in new
  return im._new(core.fill(mode, size, color))
TypeError: function takes exactly 1 argument (3 given)

@pmeier
Copy link
Collaborator

pmeier commented Apr 12, 2020

@Ge0rges As stated above:

This is a bug in torchvision==0.5.0. As long as you have pillow>=5.2.0, the actual version is irrelevant.

I've outlined possible workarounds in that comment. If they do not work for you, please open a new bug report issue.

@Ge0rges
Copy link

Ge0rges commented Apr 12, 2020

I was surprised a fix has been pushed for so long but no updates to torch vision stable have been put out yet.

@pmeier
Copy link
Collaborator

pmeier commented Apr 12, 2020

I think a new release is just around the corner, but I have no precise information about that.

@fmassa
Copy link
Member

fmassa commented Apr 15, 2020

Next release is ready and will be made available early next week

@Interesting6
Copy link

Interesting6 commented Jan 10, 2021

class randomRotate(nn.Module):
    def __init__(self, degrees=[0, 90, 180, 270]):
        super(randomRotate, self).__init__()
        self.degrees = degrees

    def __call__(self, x):
        degree = random.choice(self.degrees)
        return transforms.functional.rotate(x, degree)

__init__:
self.data_cube = segyio.cube(self.data_path)
self.data_cube = torch.from_numpy(self.data_cube)
self.augment = transforms.Compose([
            transforms.ToPILImage(),
            transforms.RandomHorizontalFlip(),
            transforms.RandomVerticalFlip(),
            randomRotate(),
            transforms.ToTensor()
        ])

__getitem__:
image = self.data_cube[i]
image = self.augment(image)

is totally ok.

but I change the data_cube axis:

__init__:
self.data_cube = np.transpose(segyio.cube(self.data_path), (0,2,1))

it runs error,

  File "/home/cym/.conda/envs/action/lib/python3.6/site-packages/PIL/Image.py", line 2072, in rotate
    return self.transform((w, h), AFFINE, matrix, resample, fillcolor=fillcolor)
  File "/home/cym/.conda/envs/action/lib/python3.6/site-packages/PIL/Image.py", line 2406, in transform
    im = new(self.mode, size, fillcolor)
  File "/home/cym/.conda/envs/action/lib/python3.6/site-packages/PIL/Image.py", line 2621, in new
    return im._new(core.fill(mode, size, color))
TypeError: function takes exactly 1 argument (3 given)

add contiguous also doesn't work.

it really strange.

@fmassa
Copy link
Member

fmassa commented Jan 20, 2021

@Interesting6 I believe your image doesn't have the right number of channels, but without further information it's hard to say more

@Interesting6
Copy link

@Interesting6 I believe your image doesn't have the right number of channels, but without further information it's hard to say more

data_cube is a numpy ndarray with dimensions [c=4, h=906, w=463] read from segyio.

@fmassa
Copy link
Member

fmassa commented Jan 20, 2021

@Interesting6 could you please open a new issue with the torchvision version that you are using, and a self-contained example reproducing the issue?

@nightlessbaron
Copy link

@chinglamchoi

This is a bug in torchvision==0.5.0. As long as you have pillow>=5.2.0, the actual version is irrelevant.

If you are bound to torchvision==0.5.0 you can add fill=[filler] * num_bands to your rotation parameters. Here filler = 0.0 if img.mode.startswith("F") else 0 and num_bands = len(img.getbands()) where img is an PIL.Image.Image.

If you can use an older version, I suggest you downgrade to torchvision==0.4.2, since this is the last version that does not have this bug.

Alternatively, if you are not bound to a stable version, you can use the nightly version of torchvision, since this bug is already fixed on the master branch (#1760, #1828).

Thanks, @pmeier, this works. But I don't understand why.
In my case, num_bands was 3, so as you suggested, I replaced fill=(0,) with fill=[0]*3.
But what's exactly happening?

@pmeier
Copy link
Collaborator

pmeier commented Oct 7, 2021

@nightlessbaron

If you already have num_bands==3 you should never hit this bug, as this was the assumed behavior. In any case, are you actually working with torchvision==0.5.0? This version was released more than 1.5 years ago. The bug is fixed in later versions.

@nightlessbaron
Copy link

nightlessbaron commented Oct 7, 2021

@pmeier Thanks for the quick reply.
I am also surprised to see this bug on num_bands==3, as in the discussion above seemed mostly for grayscale images.
I am working on torchvision==0.10.0, thus I realize that this bug was indeed fixed a lot of time earlier.
To be more precise, the error I am getting is

ValueError: The number of elements in 'fill' does not match the number of bands of the image (1 != 3)

Not sure if this helps, but I used this tranform

# I am randomly choosing rotation value
transforms.RandomRotation((rot, rot), fill=(0, )),

P.S. Let me know if I should open a new issue with the exact stack trace :)

@pmeier
Copy link
Collaborator

pmeier commented Oct 7, 2021

The error message is telling you what the problem is: by using fill=(0,) the implementation will only handle single-channel images. If you want all channels to be filled with 0, use fill=0. Otherwise pass a tuple or list with the same number of elements as you have channels, e.g. fill=(0, 0.5, 1.0).

@nightlessbaron
Copy link

nightlessbaron commented Oct 7, 2021

Awesome @pmeier! I am handling multiple datasets, both grayscaled and RGB. I think, in my case, this might do the trick:

try:
    # for RGB images
    transforms.RandomRotation((rot, rot))
except:
    # for grayscaled
    transforms.RandomRotation((rot, rot), fill=(0, )),

Do correct me if I am wrong. Thanks for your help!

@pmeier
Copy link
Collaborator

pmeier commented Oct 7, 2021

Not sure what you are trying to do:

  • RandomRotation takes a range of degrees to randomly sample the angle from. By requesting (rot, rot) there will be no randomness, since the only value that can be sampled is rot. If you want to sample from (-rot, rot), either pass that or use the shortcut RandomRotation(rot).
  • Filling all channels with 0 is the default behavior. If that is what you want, you don't need to pass in anything. Furthermore, if you pass in a Python scalar, say fill=0, it will automatically expanded to handle all available channels.

@nightlessbaron
Copy link

Thanks @pmeier

  • RandomRotation takes a range of degrees to randomly sample the angle from. By requesting (rot, rot) there will be no randomness, since the only value that can be sampled is rot. If you want to sample from (-rot, rot), either pass that or use the shortcut RandomRotation(rot).

Sorry, my bad. rot is a value that is already randomly sampled.

  • Filling all channels with 0 is the default behavior. If that is what you want, you don't need to pass in anything. Furthermore, if you pass in a Python scalar, say fill=0, it will automatically expanded to handle all available channels.

Got it. Thanks :) Just one last question. If fill=0 (that is I keep the default value), then would it throw any error for single-channel images?

@pmeier
Copy link
Collaborator

pmeier commented Oct 7, 2021

No, it shouldn't. If it does, this is a bug and you might file an issue so we can investigate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants