We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Utility to draw Semantic Segmentation Masks
## 🚀 Feature

We recently added utility to draw bounding boxes, which works really well with detection models. #2785 #2556 
It might be nice to draw segmentation masks which we obtain from instance segmentation models.

## Motivation

Same as bounding box utils. It is very useful to have these. It reduces the dependence of users over other plotting libraries.

## Pitch

Our API should be compatible with segmentation models, so we should probably use Tensors.
I think most params remain as same as the previous util. This keeps consistency too.

```
@torch.no_grad()
def draw_segmentation_masks(image: torch.Tensor,
    masks: torch.Tensor,
    labels: Optional[List[str]] = None,
    colors: Optional[List[Union[str, Tuple[int, int, int]]]] = None,
    width: int = 1,
    font: Optional[str] = None,
    font_size: int = 10)
```

We might need to see a method using which we can draw with PIL.
We used `draw.rectangle()` to draw a box in utils, maybe there is some functionality in PIL that can help us draw shapes.

## Alternatives

Let's discuss further, how this API would work with our models.
Also, it would be nice if this works directly for instance segmentation model Mask RCNN.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `torchvision/utils.py`
Content:
```
1 from typing import Union, Optional, List, Tuple, Text, BinaryIO
2 import pathlib
3 import torch
4 import math
5 import warnings
6 import numpy as np
7 from PIL import Image, ImageDraw, ImageFont, ImageColor
8 
9 __all__ = ["make_grid", "save_image", "draw_bounding_boxes"]
10 
11 
12 @torch.no_grad()
13 def make_grid(
14     tensor: Union[torch.Tensor, List[torch.Tensor]],
15     nrow: int = 8,
16     padding: int = 2,
17     normalize: bool = False,
18     value_range: Optional[Tuple[int, int]] = None,
19     scale_each: bool = False,
20     pad_value: int = 0,
21     **kwargs
22 ) -> torch.Tensor:
23     """Make a grid of images.
24 
25     Args:
26         tensor (Tensor or list): 4D mini-batch Tensor of shape (B x C x H x W)
27             or a list of images all of the same size.
28         nrow (int, optional): Number of images displayed in each row of the grid.
29             The final grid size is ``(B / nrow, nrow)``. Default: ``8``.
30         padding (int, optional): amount of padding. Default: ``2``.
31         normalize (bool, optional): If True, shift the image to the range (0, 1),
32             by the min and max values specified by :attr:`range`. Default: ``False``.
33         value_range (tuple, optional): tuple (min, max) where min and max are numbers,
34             then these numbers are used to normalize the image. By default, min and max
35             are computed from the tensor.
36         scale_each (bool, optional): If ``True``, scale each image in the batch of
37             images separately rather than the (min, max) over all images. Default: ``False``.
38         pad_value (float, optional): Value for the padded pixels. Default: ``0``.
39 
40     Example:
41         See this notebook `here <https://gist.github.com/anonymous/bf16430f7750c023141c562f3e9f2a91>`_
42 
43     """
44     if not (torch.is_tensor(tensor) or
45             (isinstance(tensor, list) and all(torch.is_tensor(t) for t in tensor))):
46         raise TypeError(f'tensor or list of tensors expected, got {type(tensor)}')
47 
48     if "range" in kwargs.keys():
49         warning = "range will be deprecated, please use value_range instead."
50         warnings.warn(warning)
51         value_range = kwargs["range"]
52 
53     # if list of tensors, convert to a 4D mini-batch Tensor
54     if isinstance(tensor, list):
55         tensor = torch.stack(tensor, dim=0)
56 
57     if tensor.dim() == 2:  # single image H x W
58         tensor = tensor.unsqueeze(0)
59     if tensor.dim() == 3:  # single image
60         if tensor.size(0) == 1:  # if single-channel, convert to 3-channel
61             tensor = torch.cat((tensor, tensor, tensor), 0)
62         tensor = tensor.unsqueeze(0)
63 
64     if tensor.dim() == 4 and tensor.size(1) == 1:  # single-channel images
65         tensor = torch.cat((tensor, tensor, tensor), 1)
66 
67     if normalize is True:
68         tensor = tensor.clone()  # avoid modifying tensor in-place
69         if value_range is not None:
70             assert isinstance(value_range, tuple), \
71                 "value_range has to be a tuple (min, max) if specified. min and max are numbers"
72 
73         def norm_ip(img, low, high):
74             img.clamp_(min=low, max=high)
75             img.sub_(low).div_(max(high - low, 1e-5))
76 
77         def norm_range(t, value_range):
78             if value_range is not None:
79                 norm_ip(t, value_range[0], value_range[1])
80             else:
81                 norm_ip(t, float(t.min()), float(t.max()))
82 
83         if scale_each is True:
84             for t in tensor:  # loop over mini-batch dimension
85                 norm_range(t, value_range)
86         else:
87             norm_range(tensor, value_range)
88 
89     if tensor.size(0) == 1:
90         return tensor.squeeze(0)
91 
92     # make the mini-batch of images into a grid
93     nmaps = tensor.size(0)
94     xmaps = min(nrow, nmaps)
95     ymaps = int(math.ceil(float(nmaps) / xmaps))
96     height, width = int(tensor.size(2) + padding), int(tensor.size(3) + padding)
97     num_channels = tensor.size(1)
98     grid = tensor.new_full((num_channels, height * ymaps + padding, width * xmaps + padding), pad_value)
99     k = 0
100     for y in range(ymaps):
101         for x in range(xmaps):
102             if k >= nmaps:
103                 break
104             # Tensor.copy_() is a valid method but seems to be missing from the stubs
105             # https://pytorch.org/docs/stable/tensors.html#torch.Tensor.copy_
106             grid.narrow(1, y * height + padding, height - padding).narrow(  # type: ignore[attr-defined]
107                 2, x * width + padding, width - padding
108             ).copy_(tensor[k])
109             k = k + 1
110     return grid
111 
112 
113 @torch.no_grad()
114 def save_image(
115     tensor: Union[torch.Tensor, List[torch.Tensor]],
116     fp: Union[Text, pathlib.Path, BinaryIO],
117     format: Optional[str] = None,
118     **kwargs
119 ) -> None:
120     """Save a given Tensor into an image file.
121 
122     Args:
123         tensor (Tensor or list): Image to be saved. If given a mini-batch tensor,
124             saves the tensor as a grid of images by calling ``make_grid``.
125         fp (string or file object): A filename or a file object
126         format(Optional):  If omitted, the format to use is determined from the filename extension.
127             If a file object was used instead of a filename, this parameter should always be used.
128         **kwargs: Other arguments are documented in ``make_grid``.
129     """
130 
131     grid = make_grid(tensor, **kwargs)
132     # Add 0.5 after unnormalizing to [0, 255] to round to nearest integer
133     ndarr = grid.mul(255).add_(0.5).clamp_(0, 255).permute(1, 2, 0).to('cpu', torch.uint8).numpy()
134     im = Image.fromarray(ndarr)
135     im.save(fp, format=format)
136 
137 
138 @torch.no_grad()
139 def draw_bounding_boxes(
140     image: torch.Tensor,
141     boxes: torch.Tensor,
142     labels: Optional[List[str]] = None,
143     colors: Optional[List[Union[str, Tuple[int, int, int]]]] = None,
144     fill: Optional[bool] = False,
145     width: int = 1,
146     font: Optional[str] = None,
147     font_size: int = 10
148 ) -> torch.Tensor:
149 
150     """
151     Draws bounding boxes on given image.
152     The values of the input image should be uint8 between 0 and 255.
153     If filled, Resulting Tensor should be saved as PNG image.
154 
155     Args:
156         image (Tensor): Tensor of shape (C x H x W)
157         boxes (Tensor): Tensor of size (N, 4) containing bounding boxes in (xmin, ymin, xmax, ymax) format. Note that
158             the boxes are absolute coordinates with respect to the image. In other words: `0 <= xmin < xmax < W` and
159             `0 <= ymin < ymax < H`.
160         labels (List[str]): List containing the labels of bounding boxes.
161         colors (List[Union[str, Tuple[int, int, int]]]): List containing the colors of bounding boxes. The colors can
162             be represented as `str` or `Tuple[int, int, int]`.
163         fill (bool): If `True` fills the bounding box with specified color.
164         width (int): Width of bounding box.
165         font (str): A filename containing a TrueType font. If the file is not found in this filename, the loader may
166             also search in other directories, such as the `fonts/` directory on Windows or `/Library/Fonts/`,
167             `/System/Library/Fonts/` and `~/Library/Fonts/` on macOS.
168         font_size (int): The requested font size in points.
169     """
170 
171     if not isinstance(image, torch.Tensor):
172         raise TypeError(f"Tensor expected, got {type(image)}")
173     elif image.dtype != torch.uint8:
174         raise ValueError(f"Tensor uint8 expected, got {image.dtype}")
175     elif image.dim() != 3:
176         raise ValueError("Pass individual images, not batches")
177 
178     ndarr = image.permute(1, 2, 0).numpy()
179     img_to_draw = Image.fromarray(ndarr)
180 
181     img_boxes = boxes.to(torch.int64).tolist()
182 
183     if fill:
184         draw = ImageDraw.Draw(img_to_draw, "RGBA")
185 
186     else:
187         draw = ImageDraw.Draw(img_to_draw)
188 
189     txt_font = ImageFont.load_default() if font is None else ImageFont.truetype(font=font, size=font_size)
190 
191     for i, bbox in enumerate(img_boxes):
192         if colors is None:
193             color = None
194         else:
195             color = colors[i]
196 
197         if fill:
198             if color is None:
199                 fill_color = (255, 255, 255, 100)
200             elif isinstance(color, str):
201                 # This will automatically raise Error if rgb cannot be parsed.
202                 fill_color = ImageColor.getrgb(color) + (100,)
203             elif isinstance(color, tuple):
204                 fill_color = color + (100,)
205             draw.rectangle(bbox, width=width, outline=color, fill=fill_color)
206         else:
207             draw.rectangle(bbox, width=width, outline=color)
208 
209         if labels is not None:
210             draw.text((bbox[0], bbox[1]), labels[i], fill=color, font=txt_font)
211 
212     return torch.from_numpy(np.array(img_to_draw)).permute(2, 0, 1)
213
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/torchvision/utils.py b/torchvision/utils.py
--- a/torchvision/utils.py
+++ b/torchvision/utils.py
@@ -6,7 +6,7 @@
 import numpy as np
 from PIL import Image, ImageDraw, ImageFont, ImageColor
 
-__all__ = ["make_grid", "save_image", "draw_bounding_boxes"]
+__all__ = ["make_grid", "save_image", "draw_bounding_boxes", "draw_segmentation_masks"]
 
 
 @torch.no_grad()
@@ -153,7 +153,7 @@
     If filled, Resulting Tensor should be saved as PNG image.
 
     Args:
-        image (Tensor): Tensor of shape (C x H x W)
+        image (Tensor): Tensor of shape (C x H x W) and dtype uint8.
         boxes (Tensor): Tensor of size (N, 4) containing bounding boxes in (xmin, ymin, xmax, ymax) format. Note that
             the boxes are absolute coordinates with respect to the image. In other words: `0 <= xmin < xmax < W` and
             `0 <= ymin < ymax < H`.
@@ -210,3 +210,61 @@
             draw.text((bbox[0], bbox[1]), labels[i], fill=color, font=txt_font)
 
     return torch.from_numpy(np.array(img_to_draw)).permute(2, 0, 1)
+
+
+@torch.no_grad()
+def draw_segmentation_masks(
+    image: torch.Tensor,
+    masks: torch.Tensor,
+    alpha: float = 0.2,
+    colors: Optional[List[Union[str, Tuple[int, int, int]]]] = None,
+) -> torch.Tensor:
+
+    """
+    Draws segmentation masks on given RGB image.
+    The values of the input image should be uint8 between 0 and 255.
+
+    Args:
+        image (Tensor): Tensor of shape (3 x H x W) and dtype uint8.
+        masks (Tensor): Tensor of shape (num_masks, H, W). Each containing probability of predicted class.
+        alpha (float): Float number between 0 and 1 denoting factor of transpaerency of masks.
+        colors (List[Union[str, Tuple[int, int, int]]]): List containing the colors of masks. The colors can
+            be represented as `str` or `Tuple[int, int, int]`.
+    """
+
+    if not isinstance(image, torch.Tensor):
+        raise TypeError(f"Tensor expected, got {type(image)}")
+    elif image.dtype != torch.uint8:
+        raise ValueError(f"Tensor uint8 expected, got {image.dtype}")
+    elif image.dim() != 3:
+        raise ValueError("Pass individual images, not batches")
+    elif image.size()[0] != 3:
+        raise ValueError("Pass an RGB image. Other Image formats are not supported")
+
+    num_masks = masks.size()[0]
+    masks = masks.argmax(0)
+
+    if colors is None:
+        palette = torch.tensor([2 ** 25 - 1, 2 ** 15 - 1, 2 ** 21 - 1])
+        colors_t = torch.as_tensor([i for i in range(num_masks)])[:, None] * palette
+        color_arr = (colors_t % 255).numpy().astype("uint8")
+    else:
+        color_list = []
+        for color in colors:
+            if isinstance(color, str):
+                # This will automatically raise Error if rgb cannot be parsed.
+                fill_color = ImageColor.getrgb(color)
+                color_list.append(fill_color)
+            elif isinstance(color, tuple):
+                color_list.append(color)
+
+        color_arr = np.array(color_list).astype("uint8")
+
+    _, h, w = image.size()
+    img_to_draw = Image.fromarray(masks.byte().cpu().numpy()).resize((w, h))
+    img_to_draw.putpalette(color_arr)
+
+    img_to_draw = torch.from_numpy(np.array(img_to_draw.convert('RGB')))
+    img_to_draw = img_to_draw.permute((2, 0, 1))
+
+    return (image.float() * alpha + img_to_draw.float() * (1.0 - alpha)).to(dtype=torch.uint8)

{"golden_diff": "diff --git a/torchvision/utils.py b/torchvision/utils.py\n--- a/torchvision/utils.py\n+++ b/torchvision/utils.py\n@@ -6,7 +6,7 @@\n import numpy as np\n from PIL import Image, ImageDraw, ImageFont, ImageColor\n \n-__all__ = [\"make_grid\", \"save_image\", \"draw_bounding_boxes\"]\n+__all__ = [\"make_grid\", \"save_image\", \"draw_bounding_boxes\", \"draw_segmentation_masks\"]\n \n \n @torch.no_grad()\n@@ -153,7 +153,7 @@\n     If filled, Resulting Tensor should be saved as PNG image.\n \n     Args:\n-        image (Tensor): Tensor of shape (C x H x W)\n+        image (Tensor): Tensor of shape (C x H x W) and dtype uint8.\n         boxes (Tensor): Tensor of size (N, 4) containing bounding boxes in (xmin, ymin, xmax, ymax) format. Note that\n             the boxes are absolute coordinates with respect to the image. In other words: `0 <= xmin < xmax < W` and\n             `0 <= ymin < ymax < H`.\n@@ -210,3 +210,61 @@\n             draw.text((bbox[0], bbox[1]), labels[i], fill=color, font=txt_font)\n \n     return torch.from_numpy(np.array(img_to_draw)).permute(2, 0, 1)\n+\n+\n+@torch.no_grad()\n+def draw_segmentation_masks(\n+    image: torch.Tensor,\n+    masks: torch.Tensor,\n+    alpha: float = 0.2,\n+    colors: Optional[List[Union[str, Tuple[int, int, int]]]] = None,\n+) -> torch.Tensor:\n+\n+    \"\"\"\n+    Draws segmentation masks on given RGB image.\n+    The values of the input image should be uint8 between 0 and 255.\n+\n+    Args:\n+        image (Tensor): Tensor of shape (3 x H x W) and dtype uint8.\n+        masks (Tensor): Tensor of shape (num_masks, H, W). Each containing probability of predicted class.\n+        alpha (float): Float number between 0 and 1 denoting factor of transpaerency of masks.\n+        colors (List[Union[str, Tuple[int, int, int]]]): List containing the colors of masks. The colors can\n+            be represented as `str` or `Tuple[int, int, int]`.\n+    \"\"\"\n+\n+    if not isinstance(image, torch.Tensor):\n+        raise TypeError(f\"Tensor expected, got {type(image)}\")\n+    elif image.dtype != torch.uint8:\n+        raise ValueError(f\"Tensor uint8 expected, got {image.dtype}\")\n+    elif image.dim() != 3:\n+        raise ValueError(\"Pass individual images, not batches\")\n+    elif image.size()[0] != 3:\n+        raise ValueError(\"Pass an RGB image. Other Image formats are not supported\")\n+\n+    num_masks = masks.size()[0]\n+    masks = masks.argmax(0)\n+\n+    if colors is None:\n+        palette = torch.tensor([2 ** 25 - 1, 2 ** 15 - 1, 2 ** 21 - 1])\n+        colors_t = torch.as_tensor([i for i in range(num_masks)])[:, None] * palette\n+        color_arr = (colors_t % 255).numpy().astype(\"uint8\")\n+    else:\n+        color_list = []\n+        for color in colors:\n+            if isinstance(color, str):\n+                # This will automatically raise Error if rgb cannot be parsed.\n+                fill_color = ImageColor.getrgb(color)\n+                color_list.append(fill_color)\n+            elif isinstance(color, tuple):\n+                color_list.append(color)\n+\n+        color_arr = np.array(color_list).astype(\"uint8\")\n+\n+    _, h, w = image.size()\n+    img_to_draw = Image.fromarray(masks.byte().cpu().numpy()).resize((w, h))\n+    img_to_draw.putpalette(color_arr)\n+\n+    img_to_draw = torch.from_numpy(np.array(img_to_draw.convert('RGB')))\n+    img_to_draw = img_to_draw.permute((2, 0, 1))\n+\n+    return (image.float() * alpha + img_to_draw.float() * (1.0 - alpha)).to(dtype=torch.uint8)\n", "issue": "Utility to draw Semantic Segmentation Masks\n## \ud83d\ude80 Feature\r\n\r\nWe recently added utility to draw bounding boxes, which works really well with detection models. #2785 #2556 \r\nIt might be nice to draw segmentation masks which we obtain from instance segmentation models.\r\n\r\n## Motivation\r\n\r\nSame as bounding box utils. It is very useful to have these. It reduces the dependence of users over other plotting libraries.\r\n\r\n## Pitch\r\n\r\nOur API should be compatible with segmentation models, so we should probably use Tensors.\r\nI think most params remain as same as the previous util. This keeps consistency too.\r\n\r\n```\r\n@torch.no_grad()\r\ndef draw_segmentation_masks(image: torch.Tensor,\r\n    masks: torch.Tensor,\r\n    labels: Optional[List[str]] = None,\r\n    colors: Optional[List[Union[str, Tuple[int, int, int]]]] = None,\r\n    width: int = 1,\r\n    font: Optional[str] = None,\r\n    font_size: int = 10)\r\n```\r\n\r\nWe might need to see a method using which we can draw with PIL.\r\nWe used `draw.rectangle()` to draw a box in utils, maybe there is some functionality in PIL that can help us draw shapes.\r\n\r\n## Alternatives\r\n\r\nLet's discuss further, how this API would work with our models.\r\nAlso, it would be nice if this works directly for instance segmentation model Mask RCNN.\r\n\n", "before_files": [{"content": "from typing import Union, Optional, List, Tuple, Text, BinaryIO\nimport pathlib\nimport torch\nimport math\nimport warnings\nimport numpy as np\nfrom PIL import Image, ImageDraw, ImageFont, ImageColor\n\n__all__ = [\"make_grid\", \"save_image\", \"draw_bounding_boxes\"]\n\n\n@torch.no_grad()\ndef make_grid(\n    tensor: Union[torch.Tensor, List[torch.Tensor]],\n    nrow: int = 8,\n    padding: int = 2,\n    normalize: bool = False,\n    value_range: Optional[Tuple[int, int]] = None,\n    scale_each: bool = False,\n    pad_value: int = 0,\n    **kwargs\n) -> torch.Tensor:\n    \"\"\"Make a grid of images.\n\n    Args:\n        tensor (Tensor or list): 4D mini-batch Tensor of shape (B x C x H x W)\n            or a list of images all of the same size.\n        nrow (int, optional): Number of images displayed in each row of the grid.\n            The final grid size is ``(B / nrow, nrow)``. Default: ``8``.\n        padding (int, optional): amount of padding. Default: ``2``.\n        normalize (bool, optional): If True, shift the image to the range (0, 1),\n            by the min and max values specified by :attr:`range`. Default: ``False``.\n        value_range (tuple, optional): tuple (min, max) where min and max are numbers,\n            then these numbers are used to normalize the image. By default, min and max\n            are computed from the tensor.\n        scale_each (bool, optional): If ``True``, scale each image in the batch of\n            images separately rather than the (min, max) over all images. Default: ``False``.\n        pad_value (float, optional): Value for the padded pixels. Default: ``0``.\n\n    Example:\n        See this notebook `here <https://gist.github.com/anonymous/bf16430f7750c023141c562f3e9f2a91>`_\n\n    \"\"\"\n    if not (torch.is_tensor(tensor) or\n            (isinstance(tensor, list) and all(torch.is_tensor(t) for t in tensor))):\n        raise TypeError(f'tensor or list of tensors expected, got {type(tensor)}')\n\n    if \"range\" in kwargs.keys():\n        warning = \"range will be deprecated, please use value_range instead.\"\n        warnings.warn(warning)\n        value_range = kwargs[\"range\"]\n\n    # if list of tensors, convert to a 4D mini-batch Tensor\n    if isinstance(tensor, list):\n        tensor = torch.stack(tensor, dim=0)\n\n    if tensor.dim() == 2:  # single image H x W\n        tensor = tensor.unsqueeze(0)\n    if tensor.dim() == 3:  # single image\n        if tensor.size(0) == 1:  # if single-channel, convert to 3-channel\n            tensor = torch.cat((tensor, tensor, tensor), 0)\n        tensor = tensor.unsqueeze(0)\n\n    if tensor.dim() == 4 and tensor.size(1) == 1:  # single-channel images\n        tensor = torch.cat((tensor, tensor, tensor), 1)\n\n    if normalize is True:\n        tensor = tensor.clone()  # avoid modifying tensor in-place\n        if value_range is not None:\n            assert isinstance(value_range, tuple), \\\n                \"value_range has to be a tuple (min, max) if specified. min and max are numbers\"\n\n        def norm_ip(img, low, high):\n            img.clamp_(min=low, max=high)\n            img.sub_(low).div_(max(high - low, 1e-5))\n\n        def norm_range(t, value_range):\n            if value_range is not None:\n                norm_ip(t, value_range[0], value_range[1])\n            else:\n                norm_ip(t, float(t.min()), float(t.max()))\n\n        if scale_each is True:\n            for t in tensor:  # loop over mini-batch dimension\n                norm_range(t, value_range)\n        else:\n            norm_range(tensor, value_range)\n\n    if tensor.size(0) == 1:\n        return tensor.squeeze(0)\n\n    # make the mini-batch of images into a grid\n    nmaps = tensor.size(0)\n    xmaps = min(nrow, nmaps)\n    ymaps = int(math.ceil(float(nmaps) / xmaps))\n    height, width = int(tensor.size(2) + padding), int(tensor.size(3) + padding)\n    num_channels = tensor.size(1)\n    grid = tensor.new_full((num_channels, height * ymaps + padding, width * xmaps + padding), pad_value)\n    k = 0\n    for y in range(ymaps):\n        for x in range(xmaps):\n            if k >= nmaps:\n                break\n            # Tensor.copy_() is a valid method but seems to be missing from the stubs\n            # https://pytorch.org/docs/stable/tensors.html#torch.Tensor.copy_\n            grid.narrow(1, y * height + padding, height - padding).narrow(  # type: ignore[attr-defined]\n                2, x * width + padding, width - padding\n            ).copy_(tensor[k])\n            k = k + 1\n    return grid\n\n\n@torch.no_grad()\ndef save_image(\n    tensor: Union[torch.Tensor, List[torch.Tensor]],\n    fp: Union[Text, pathlib.Path, BinaryIO],\n    format: Optional[str] = None,\n    **kwargs\n) -> None:\n    \"\"\"Save a given Tensor into an image file.\n\n    Args:\n        tensor (Tensor or list): Image to be saved. If given a mini-batch tensor,\n            saves the tensor as a grid of images by calling ``make_grid``.\n        fp (string or file object): A filename or a file object\n        format(Optional):  If omitted, the format to use is determined from the filename extension.\n            If a file object was used instead of a filename, this parameter should always be used.\n        **kwargs: Other arguments are documented in ``make_grid``.\n    \"\"\"\n\n    grid = make_grid(tensor, **kwargs)\n    # Add 0.5 after unnormalizing to [0, 255] to round to nearest integer\n    ndarr = grid.mul(255).add_(0.5).clamp_(0, 255).permute(1, 2, 0).to('cpu', torch.uint8).numpy()\n    im = Image.fromarray(ndarr)\n    im.save(fp, format=format)\n\n\n@torch.no_grad()\ndef draw_bounding_boxes(\n    image: torch.Tensor,\n    boxes: torch.Tensor,\n    labels: Optional[List[str]] = None,\n    colors: Optional[List[Union[str, Tuple[int, int, int]]]] = None,\n    fill: Optional[bool] = False,\n    width: int = 1,\n    font: Optional[str] = None,\n    font_size: int = 10\n) -> torch.Tensor:\n\n    \"\"\"\n    Draws bounding boxes on given image.\n    The values of the input image should be uint8 between 0 and 255.\n    If filled, Resulting Tensor should be saved as PNG image.\n\n    Args:\n        image (Tensor): Tensor of shape (C x H x W)\n        boxes (Tensor): Tensor of size (N, 4) containing bounding boxes in (xmin, ymin, xmax, ymax) format. Note that\n            the boxes are absolute coordinates with respect to the image. In other words: `0 <= xmin < xmax < W` and\n            `0 <= ymin < ymax < H`.\n        labels (List[str]): List containing the labels of bounding boxes.\n        colors (List[Union[str, Tuple[int, int, int]]]): List containing the colors of bounding boxes. The colors can\n            be represented as `str` or `Tuple[int, int, int]`.\n        fill (bool): If `True` fills the bounding box with specified color.\n        width (int): Width of bounding box.\n        font (str): A filename containing a TrueType font. If the file is not found in this filename, the loader may\n            also search in other directories, such as the `fonts/` directory on Windows or `/Library/Fonts/`,\n            `/System/Library/Fonts/` and `~/Library/Fonts/` on macOS.\n        font_size (int): The requested font size in points.\n    \"\"\"\n\n    if not isinstance(image, torch.Tensor):\n        raise TypeError(f\"Tensor expected, got {type(image)}\")\n    elif image.dtype != torch.uint8:\n        raise ValueError(f\"Tensor uint8 expected, got {image.dtype}\")\n    elif image.dim() != 3:\n        raise ValueError(\"Pass individual images, not batches\")\n\n    ndarr = image.permute(1, 2, 0).numpy()\n    img_to_draw = Image.fromarray(ndarr)\n\n    img_boxes = boxes.to(torch.int64).tolist()\n\n    if fill:\n        draw = ImageDraw.Draw(img_to_draw, \"RGBA\")\n\n    else:\n        draw = ImageDraw.Draw(img_to_draw)\n\n    txt_font = ImageFont.load_default() if font is None else ImageFont.truetype(font=font, size=font_size)\n\n    for i, bbox in enumerate(img_boxes):\n        if colors is None:\n            color = None\n        else:\n            color = colors[i]\n\n        if fill:\n            if color is None:\n                fill_color = (255, 255, 255, 100)\n            elif isinstance(color, str):\n                # This will automatically raise Error if rgb cannot be parsed.\n                fill_color = ImageColor.getrgb(color) + (100,)\n            elif isinstance(color, tuple):\n                fill_color = color + (100,)\n            draw.rectangle(bbox, width=width, outline=color, fill=fill_color)\n        else:\n            draw.rectangle(bbox, width=width, outline=color)\n\n        if labels is not None:\n            draw.text((bbox[0], bbox[1]), labels[i], fill=color, font=txt_font)\n\n    return torch.from_numpy(np.array(img_to_draw)).permute(2, 0, 1)\n", "path": "torchvision/utils.py"}], "after_files": [{"content": "from typing import Union, Optional, List, Tuple, Text, BinaryIO\nimport pathlib\nimport torch\nimport math\nimport warnings\nimport numpy as np\nfrom PIL import Image, ImageDraw, ImageFont, ImageColor\n\n__all__ = [\"make_grid\", \"save_image\", \"draw_bounding_boxes\", \"draw_segmentation_masks\"]\n\n\n@torch.no_grad()\ndef make_grid(\n    tensor: Union[torch.Tensor, List[torch.Tensor]],\n    nrow: int = 8,\n    padding: int = 2,\n    normalize: bool = False,\n    value_range: Optional[Tuple[int, int]] = None,\n    scale_each: bool = False,\n    pad_value: int = 0,\n    **kwargs\n) -> torch.Tensor:\n    \"\"\"Make a grid of images.\n\n    Args:\n        tensor (Tensor or list): 4D mini-batch Tensor of shape (B x C x H x W)\n            or a list of images all of the same size.\n        nrow (int, optional): Number of images displayed in each row of the grid.\n            The final grid size is ``(B / nrow, nrow)``. Default: ``8``.\n        padding (int, optional): amount of padding. Default: ``2``.\n        normalize (bool, optional): If True, shift the image to the range (0, 1),\n            by the min and max values specified by :attr:`range`. Default: ``False``.\n        value_range (tuple, optional): tuple (min, max) where min and max are numbers,\n            then these numbers are used to normalize the image. By default, min and max\n            are computed from the tensor.\n        scale_each (bool, optional): If ``True``, scale each image in the batch of\n            images separately rather than the (min, max) over all images. Default: ``False``.\n        pad_value (float, optional): Value for the padded pixels. Default: ``0``.\n\n    Example:\n        See this notebook `here <https://gist.github.com/anonymous/bf16430f7750c023141c562f3e9f2a91>`_\n\n    \"\"\"\n    if not (torch.is_tensor(tensor) or\n            (isinstance(tensor, list) and all(torch.is_tensor(t) for t in tensor))):\n        raise TypeError(f'tensor or list of tensors expected, got {type(tensor)}')\n\n    if \"range\" in kwargs.keys():\n        warning = \"range will be deprecated, please use value_range instead.\"\n        warnings.warn(warning)\n        value_range = kwargs[\"range\"]\n\n    # if list of tensors, convert to a 4D mini-batch Tensor\n    if isinstance(tensor, list):\n        tensor = torch.stack(tensor, dim=0)\n\n    if tensor.dim() == 2:  # single image H x W\n        tensor = tensor.unsqueeze(0)\n    if tensor.dim() == 3:  # single image\n        if tensor.size(0) == 1:  # if single-channel, convert to 3-channel\n            tensor = torch.cat((tensor, tensor, tensor), 0)\n        tensor = tensor.unsqueeze(0)\n\n    if tensor.dim() == 4 and tensor.size(1) == 1:  # single-channel images\n        tensor = torch.cat((tensor, tensor, tensor), 1)\n\n    if normalize is True:\n        tensor = tensor.clone()  # avoid modifying tensor in-place\n        if value_range is not None:\n            assert isinstance(value_range, tuple), \\\n                \"value_range has to be a tuple (min, max) if specified. min and max are numbers\"\n\n        def norm_ip(img, low, high):\n            img.clamp_(min=low, max=high)\n            img.sub_(low).div_(max(high - low, 1e-5))\n\n        def norm_range(t, value_range):\n            if value_range is not None:\n                norm_ip(t, value_range[0], value_range[1])\n            else:\n                norm_ip(t, float(t.min()), float(t.max()))\n\n        if scale_each is True:\n            for t in tensor:  # loop over mini-batch dimension\n                norm_range(t, value_range)\n        else:\n            norm_range(tensor, value_range)\n\n    if tensor.size(0) == 1:\n        return tensor.squeeze(0)\n\n    # make the mini-batch of images into a grid\n    nmaps = tensor.size(0)\n    xmaps = min(nrow, nmaps)\n    ymaps = int(math.ceil(float(nmaps) / xmaps))\n    height, width = int(tensor.size(2) + padding), int(tensor.size(3) + padding)\n    num_channels = tensor.size(1)\n    grid = tensor.new_full((num_channels, height * ymaps + padding, width * xmaps + padding), pad_value)\n    k = 0\n    for y in range(ymaps):\n        for x in range(xmaps):\n            if k >= nmaps:\n                break\n            # Tensor.copy_() is a valid method but seems to be missing from the stubs\n            # https://pytorch.org/docs/stable/tensors.html#torch.Tensor.copy_\n            grid.narrow(1, y * height + padding, height - padding).narrow(  # type: ignore[attr-defined]\n                2, x * width + padding, width - padding\n            ).copy_(tensor[k])\n            k = k + 1\n    return grid\n\n\n@torch.no_grad()\ndef save_image(\n    tensor: Union[torch.Tensor, List[torch.Tensor]],\n    fp: Union[Text, pathlib.Path, BinaryIO],\n    format: Optional[str] = None,\n    **kwargs\n) -> None:\n    \"\"\"Save a given Tensor into an image file.\n\n    Args:\n        tensor (Tensor or list): Image to be saved. If given a mini-batch tensor,\n            saves the tensor as a grid of images by calling ``make_grid``.\n        fp (string or file object): A filename or a file object\n        format(Optional):  If omitted, the format to use is determined from the filename extension.\n            If a file object was used instead of a filename, this parameter should always be used.\n        **kwargs: Other arguments are documented in ``make_grid``.\n    \"\"\"\n\n    grid = make_grid(tensor, **kwargs)\n    # Add 0.5 after unnormalizing to [0, 255] to round to nearest integer\n    ndarr = grid.mul(255).add_(0.5).clamp_(0, 255).permute(1, 2, 0).to('cpu', torch.uint8).numpy()\n    im = Image.fromarray(ndarr)\n    im.save(fp, format=format)\n\n\n@torch.no_grad()\ndef draw_bounding_boxes(\n    image: torch.Tensor,\n    boxes: torch.Tensor,\n    labels: Optional[List[str]] = None,\n    colors: Optional[List[Union[str, Tuple[int, int, int]]]] = None,\n    fill: Optional[bool] = False,\n    width: int = 1,\n    font: Optional[str] = None,\n    font_size: int = 10\n) -> torch.Tensor:\n\n    \"\"\"\n    Draws bounding boxes on given image.\n    The values of the input image should be uint8 between 0 and 255.\n    If filled, Resulting Tensor should be saved as PNG image.\n\n    Args:\n        image (Tensor): Tensor of shape (C x H x W) and dtype uint8.\n        boxes (Tensor): Tensor of size (N, 4) containing bounding boxes in (xmin, ymin, xmax, ymax) format. Note that\n            the boxes are absolute coordinates with respect to the image. In other words: `0 <= xmin < xmax < W` and\n            `0 <= ymin < ymax < H`.\n        labels (List[str]): List containing the labels of bounding boxes.\n        colors (List[Union[str, Tuple[int, int, int]]]): List containing the colors of bounding boxes. The colors can\n            be represented as `str` or `Tuple[int, int, int]`.\n        fill (bool): If `True` fills the bounding box with specified color.\n        width (int): Width of bounding box.\n        font (str): A filename containing a TrueType font. If the file is not found in this filename, the loader may\n            also search in other directories, such as the `fonts/` directory on Windows or `/Library/Fonts/`,\n            `/System/Library/Fonts/` and `~/Library/Fonts/` on macOS.\n        font_size (int): The requested font size in points.\n    \"\"\"\n\n    if not isinstance(image, torch.Tensor):\n        raise TypeError(f\"Tensor expected, got {type(image)}\")\n    elif image.dtype != torch.uint8:\n        raise ValueError(f\"Tensor uint8 expected, got {image.dtype}\")\n    elif image.dim() != 3:\n        raise ValueError(\"Pass individual images, not batches\")\n\n    ndarr = image.permute(1, 2, 0).numpy()\n    img_to_draw = Image.fromarray(ndarr)\n\n    img_boxes = boxes.to(torch.int64).tolist()\n\n    if fill:\n        draw = ImageDraw.Draw(img_to_draw, \"RGBA\")\n\n    else:\n        draw = ImageDraw.Draw(img_to_draw)\n\n    txt_font = ImageFont.load_default() if font is None else ImageFont.truetype(font=font, size=font_size)\n\n    for i, bbox in enumerate(img_boxes):\n        if colors is None:\n            color = None\n        else:\n            color = colors[i]\n\n        if fill:\n            if color is None:\n                fill_color = (255, 255, 255, 100)\n            elif isinstance(color, str):\n                # This will automatically raise Error if rgb cannot be parsed.\n                fill_color = ImageColor.getrgb(color) + (100,)\n            elif isinstance(color, tuple):\n                fill_color = color + (100,)\n            draw.rectangle(bbox, width=width, outline=color, fill=fill_color)\n        else:\n            draw.rectangle(bbox, width=width, outline=color)\n\n        if labels is not None:\n            draw.text((bbox[0], bbox[1]), labels[i], fill=color, font=txt_font)\n\n    return torch.from_numpy(np.array(img_to_draw)).permute(2, 0, 1)\n\n\n@torch.no_grad()\ndef draw_segmentation_masks(\n    image: torch.Tensor,\n    masks: torch.Tensor,\n    alpha: float = 0.2,\n    colors: Optional[List[Union[str, Tuple[int, int, int]]]] = None,\n) -> torch.Tensor:\n\n    \"\"\"\n    Draws segmentation masks on given RGB image.\n    The values of the input image should be uint8 between 0 and 255.\n\n    Args:\n        image (Tensor): Tensor of shape (3 x H x W) and dtype uint8.\n        masks (Tensor): Tensor of shape (num_masks, H, W). Each containing probability of predicted class.\n        alpha (float): Float number between 0 and 1 denoting factor of transpaerency of masks.\n        colors (List[Union[str, Tuple[int, int, int]]]): List containing the colors of masks. The colors can\n            be represented as `str` or `Tuple[int, int, int]`.\n    \"\"\"\n\n    if not isinstance(image, torch.Tensor):\n        raise TypeError(f\"Tensor expected, got {type(image)}\")\n    elif image.dtype != torch.uint8:\n        raise ValueError(f\"Tensor uint8 expected, got {image.dtype}\")\n    elif image.dim() != 3:\n        raise ValueError(\"Pass individual images, not batches\")\n    elif image.size()[0] != 3:\n        raise ValueError(\"Pass an RGB image. Other Image formats are not supported\")\n\n    num_masks = masks.size()[0]\n    masks = masks.argmax(0)\n\n    if colors is None:\n        palette = torch.tensor([2 ** 25 - 1, 2 ** 15 - 1, 2 ** 21 - 1])\n        colors_t = torch.as_tensor([i for i in range(num_masks)])[:, None] * palette\n        color_arr = (colors_t % 255).numpy().astype(\"uint8\")\n    else:\n        color_list = []\n        for color in colors:\n            if isinstance(color, str):\n                # This will automatically raise Error if rgb cannot be parsed.\n                fill_color = ImageColor.getrgb(color)\n                color_list.append(fill_color)\n            elif isinstance(color, tuple):\n                color_list.append(color)\n\n        color_arr = np.array(color_list).astype(\"uint8\")\n\n    _, h, w = image.size()\n    img_to_draw = Image.fromarray(masks.byte().cpu().numpy()).resize((w, h))\n    img_to_draw.putpalette(color_arr)\n\n    img_to_draw = torch.from_numpy(np.array(img_to_draw.convert('RGB')))\n    img_to_draw = img_to_draw.permute((2, 0, 1))\n\n    return (image.float() * alpha + img_to_draw.float() * (1.0 - alpha)).to(dtype=torch.uint8)\n", "path": "torchvision/utils.py"}]}