Repository: pytorch/vision

Issue:
pil_to_tensor() raises a "NumPy array is not writeable" warning 
### 🐛 Describe the bug

Steps to reproduce:

```python
import torchvision
from PIL import Image
img = Image.open("test/assets/encode_jpeg/grace_hopper_517x606.jpg")
torchvision.transforms.functional.pil_to_tensor(img)
```

Raises:
```
./vision/torchvision/transforms/functional.py:177: UserWarning: The given NumPy array is not writeable, and PyTorch does not support non-writeable tensors. This means you can write to the underlying (supposedly non-writeable) NumPy array using the tensor. You may want to copy the array to protect its data or make it writeable before converting it to a tensor. This type of warning will be suppressed for the rest of this program. (Triggered internally at  ./torch/csrc/utils/tensor_numpy.cpp:187.)
  img = torch.as_tensor(np.asarray(pic))
```

https://github.com/pytorch/vision/blob/f483e71b512e0f9dd5d6eb531fb602581ffb39a6/torchvision/transforms/functional.py#L177 

There are a few options including copying the data, suppressing the warnings etc (see discussion below). The person who picks this up can comment on this issue which solution they are going for.

### Versions

Latest main (f749835057ec16886f16d5533928e269860f1760)

cc @vfdev-5 @datumbox


Discussion:
Do we know what caused this? Was there a change made to PIL recently?
Could also be from PyTorch. It has happened before: https://github.com/pytorch/pytorch/issues/47160
This is a change from PyTorch added in https://github.com/pytorch/pytorch/pull/33615. This will not be a problem for us except if we perform any in-place operations in the returned tensor (which I think almost never happens). So we could probably just silence this warning if possible.
Sounds reasonable. I've updated the ticket to indicate that.
Do you know which PR / diff caused the change @fmassa ?

I agree it shouldn't be a problem in most cases. That being said, if we almost never make inplace operations that means we almost always make a copy, so we might as well use @datumbox earlier suggestion to use `np.array` instead? Essentially this adds one single copy but we do lots of them already anyway
It was added in https://github.com/pytorch/pytorch/pull/33615

Another option is to do something like

```python
nparray.setflags(write=False)
```
but we should ensure that the user doesn't perform any in-place operations, otherwise the underlying PIL image will be modified.

We have the choice here between efficiency (no copy) and being on the safe side (with copy).

I'm ok going for the copy, but we need to keep in mind that we need to make the transforms faster in the future (as they might already be a bottleneck in newer GPUs)
Alternatively we could implement both with a new `force_copy` flag passed to `pil_to_tensor` or something like that.
No strong opinion on my side, since apparently the warning has been there for a while.
I would like to pick up this task
Sounds good Joao, it's yours. :)
I am inclined to add a `force_copy` flag but keep the warning in case the flag is `False`, so it is clear to the caller that the underlying array can be modified. Any thoughts?
@jdsgomes What would be the default value be in this case? Note that modifying the API means that we need to add more tests and update the documentation.
`false`, so the default behaviour wouldn't change.
Sounds good to me. You can implement it like this if you want. Please make sure you pass `True` when you update the code inside the `references` folder because there we want to avoid the warning. :)
As discussed offline with @datumbox I am going to proceed with the copy option for now. 

Resolving patch:
diff --git a/torchvision/transforms/functional.py b/torchvision/transforms/functional.py
--- a/torchvision/transforms/functional.py
+++ b/torchvision/transforms/functional.py
@@ -156,6 +156,10 @@ def pil_to_tensor(pic):
 
     See :class:`~torchvision.transforms.PILToTensor` for more details.
 
+    .. note::
+
+        A deep copy of the underlying array is performed.
+
     Args:
         pic (PIL Image): Image to be converted to tensor.
 
@@ -172,7 +176,7 @@ def pil_to_tensor(pic):
         return torch.as_tensor(nppic)
 
     # handle PIL Image
-    img = torch.as_tensor(np.asarray(pic))
+    img = torch.as_tensor(np.array(pic, copy=True))
     img = img.view(pic.size[1], pic.size[0], len(pic.getbands()))
     # put it from HWC to CHW format
     img = img.permute((2, 0, 1))
diff --git a/torchvision/transforms/transforms.py b/torchvision/transforms/transforms.py
--- a/torchvision/transforms/transforms.py
+++ b/torchvision/transforms/transforms.py
@@ -142,6 +142,10 @@ class PILToTensor:
 
     def __call__(self, pic):
         """
+        .. note::
+
+            A deep copy of the underlying array is performed.
+
         Args:
             pic (PIL Image): Image to be converted to tensor.
 


Test patch: