Spaces:
Build error
Build error
Download transforms.py from jcarnero/birds-classification: direct link, hf CLI and curl.
- Browser
- Download file 4.15 kB
-
https://huggingface.co/spaces/jcarnero/birds-classification/resolve/main/transforms.py
- Command line
-
hf download hf://spaces/jcarnero/birds-classification/transforms.py
-
curl -L -o transforms.py https://huggingface.co/spaces/jcarnero/birds-classification/resolve/main/transforms.py
4.15 kB
| import math | |
| from typing import Union, Tuple | |
| import torch | |
| import torch.nn.functional as F | |
| import torchvision.transforms.functional as tvf | |
| # import torchvision.transforms as tvtfms | |
| # # import operator as op | |
| from PIL import Image | |
| # # from torch import nn | |
| # # from timm import create_model | |
| def crop(image: Union[Image.Image, torch.tensor], size: Tuple[int, int]) -> Image: | |
| """ | |
| Takes a `PIL.Image` and crops it `size` unless one | |
| dimension is larger than the actual image. Padding | |
| must be performed afterwards if so. | |
| Args: | |
| image (`PIL.Image`): | |
| An image to perform cropping on | |
| size (`tuple` of integers): | |
| A size to crop to, should be in the form | |
| of (width, height) | |
| Returns: | |
| An augmented `PIL.Image` | |
| """ | |
| top = (image.size[-2] - size[0]) // 2 | |
| left = (image.size[-1] - size[1]) // 2 | |
| top = max(top, 0) | |
| left = max(left, 0) | |
| height = min(top + size[0], image.size[-2]) | |
| width = min(left + size[1], image.size[-1]) | |
| return image.crop((top, left, height, width)) | |
| def pad(image, size: Tuple[int, int]) -> Image: | |
| """ | |
| Takes a `PIL.Image` and pads it to `size` with | |
| zeros. | |
| Args: | |
| image (`PIL.Image`): | |
| An image to perform padding on | |
| size (`tuple` of integers): | |
| A size to pad to, should be in the form | |
| of (width, height) | |
| Returns: | |
| An augmented `PIL.Image` | |
| """ | |
| top = (image.size[-2] - size[0]) // 2 | |
| left = (image.size[-1] - size[1]) // 2 | |
| pad_top = max(-top, 0) | |
| pad_left = max(-left, 0) | |
| height, width = ( | |
| max(size[1] - image.size[-2] + top, 0), | |
| max(size[0] - image.size[-1] + left, 0), | |
| ) | |
| return tvf.pad(image, [pad_top, pad_left, height, width], padding_mode="constant") | |
| def resized_crop_pad( | |
| image: Union[Image.Image, torch.tensor], | |
| size: Tuple[int, int], | |
| extra_crop_ratio: float = 0.14, | |
| ) -> Image: | |
| """ | |
| Takes a `PIL.Image`, resize it according to the | |
| `extra_crop_ratio`, and then crops and pads | |
| it to `size`. | |
| Args: | |
| image (`PIL.Image`): | |
| An image to perform padding on | |
| size (`tuple` of integers): | |
| A size to crop and pad to, should be in the form | |
| of (width, height) | |
| extra_crop_ratio (float): | |
| The ratio of size at the edge cropped out. Default 0.14 | |
| """ | |
| maximum_space = max(size[0], size[1]) | |
| extra_space = maximum_space * extra_crop_ratio | |
| extra_space = math.ceil(extra_space / 8) * 8 | |
| extended_size = (size[0] + extra_space, size[1] + extra_space) | |
| resized_image = image.resize(extended_size, resample=Image.Resampling.BILINEAR) | |
| if extended_size != size: | |
| resized_image = pad(crop(resized_image, size), size) | |
| return resized_image | |
| def gpu_crop(batch: torch.tensor, size: Tuple[int, int]): | |
| """ | |
| Crops each image in `batch` to a particular `size`. | |
| Args: | |
| batch (array of `torch.Tensor`): | |
| A batch of images, should be of shape `NxCxWxH` | |
| size (`tuple` of integers): | |
| A size to pad to, should be in the form | |
| of (width, height) | |
| Returns: | |
| A batch of cropped images | |
| """ | |
| # Split into multiple lines for clarity | |
| affine_matrix = torch.eye(3, device=batch.device).float() | |
| affine_matrix = affine_matrix.unsqueeze(0) | |
| affine_matrix = affine_matrix.expand(batch.size(0), 3, 3) | |
| affine_matrix = affine_matrix.contiguous()[:, :2] | |
| coords = F.affine_grid(affine_matrix, batch.shape[:2] + size, align_corners=True) | |
| top_range, bottom_range = coords.min(), coords.max() | |
| zoom = 1 / (bottom_range - top_range).item() * 2 | |
| resizing_limit = ( | |
| min(batch.shape[-2] / coords.shape[-2], batch.shape[-1] / coords.shape[-1]) / 2 | |
| ) | |
| if resizing_limit > 1 and resizing_limit > zoom: | |
| batch = F.interpolate( | |
| batch, | |
| scale_factor=1 / resizing_limit, | |
| mode="area", | |
| recompute_scale_factor=True, | |
| ) | |
| return F.grid_sample( | |
| batch, coords, mode="bilinear", padding_mode="reflection", align_corners=True | |
| ) | |