Repository: pytorch/vision

Issue:
Setting `5.3` and `9` to `bits` argument of `RandomPosterize()` gets an indirect error message
### 🐛 Describe the bug

Setting `5.3` and `9` to `bits` argument of [RandomPosterize()](https://pytorch.org/vision/main/generated/torchvision.transforms.v2.RandomPosterize.html) gets the indirect error message as shown below:

```python
from torchvision.datasets import OxfordIIITPet
from torchvision.transforms.v2 import RandomPosterize

my_data1 = OxfordIIITPet(
    root="data",                 # ↓↓↓
    transform=RandomPosterize(bits=5.3)
)

my_data2 = OxfordIIITPet(
    root="data",                 # ↓
    transform=RandomPosterize(bits=9)
)

my_data1[0] # Error
my_data2[0] # Error
```

> TypeError: bad operand type for unary ~: 'float'

> TypeError: bad operand type for unary ~: 'float'

So, they should be something direct like below respectively:

> TypeError: `bits` must be `int`

> ValueError: `bits` must be [0, 8]



### Versions

```python
import torchvision

torchvision.__version__ # '0.20.1'
```

Hints from issue comments:
@NicolasHug: happy to consider a PR @hyperkai , thanks

Solution patch:
diff --git a/torchvision/transforms/v2/functional/_color.py b/torchvision/transforms/v2/functional/_color.py
index eb75f58cb7..a3f187f84c 100644
--- a/torchvision/transforms/v2/functional/_color.py
+++ b/torchvision/transforms/v2/functional/_color.py
@@ -460,6 +460,9 @@ def posterize(inpt: torch.Tensor, bits: int) -> torch.Tensor:
 @_register_kernel_internal(posterize, torch.Tensor)
 @_register_kernel_internal(posterize, tv_tensors.Image)
 def posterize_image(image: torch.Tensor, bits: int) -> torch.Tensor:
+    if not isinstance(bits, int) or not 0 <= bits <= 8:
+        raise TypeError(f"bits must be a positive integer in the range [0, 8], got {bits} instead.")
+
     if image.is_floating_point():
         levels = 1 << bits
         return image.mul(levels).floor_().clamp_(0, levels - 1).mul_(1.0 / levels)


Test patch:
diff --git a/test/test_transforms_v2.py b/test/test_transforms_v2.py
index a9fd3bc5ec..9f6817bb60 100644
--- a/test/test_transforms_v2.py
+++ b/test/test_transforms_v2.py
@@ -4608,6 +4608,14 @@ class TestPosterize:
 
         assert_equal(actual, expected)
 
+    @pytest.mark.parametrize("bits", [-1, 9, 2.1])
+    def test_error_functional(self, bits):
+        with pytest.raises(
+            TypeError,
+            match=re.escape(f"bits must be a positive integer in the range [0, 8], got {bits} instead."),
+        ):
+            F.posterize(make_image(dtype=torch.uint8), bits=bits)
+
 
 class TestSolarize:
     def _make_threshold(self, input, *, factor=0.5):