diff --git a/.circleci/config.yml b/.circleci/config.yml index 254e758ade2..86aa08d65b6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -174,6 +174,26 @@ commands: - store_test_results: path: test-results + download_model_weights: + parameters: + extract_roots: + type: string + default: "torchvision/models" + background: + type: boolean + default: true + steps: + - apt_install: + args: parallel wget + descr: Install download utilitites + - run: + name: Download model weights + background: << parameters.background >> + command: | + mkdir -p ~/.cache/torch/hub/checkpoints + python scripts/collect_model_urls.py << parameters.extract_roots >> \ + | parallel -j0 'wget --no-verbose -O ~/.cache/torch/hub/checkpoints/`basename {}` {}\?source=ci' + binary_common: &binary_common parameters: # Edit these defaults to do a release @@ -354,14 +374,7 @@ jobs: resource_class: xlarge steps: - checkout - - run: - name: Download model weights - background: true - command: | - sudo apt update -qy && sudo apt install -qy parallel wget - mkdir -p ~/.cache/torch/hub/checkpoints - python scripts/collect_model_urls.py torchvision/models \ - | parallel -j0 'wget --no-verbose -O ~/.cache/torch/hub/checkpoints/`basename {}` {}\?source=ci' + - download_model_weights - install_torchvision - run: name: Enable extended tests @@ -1021,12 +1034,13 @@ jobs: build_docs: <<: *binary_common docker: - - image: "pytorch/manylinux-cuda100" + - image: circleci/python:3.7 resource_class: 2xlarge+ steps: - attach_workspace: at: ~/workspace - checkout + - download_model_weights - run: name: Setup command: .circleci/unittest/linux/scripts/setup_env.sh diff --git a/.circleci/config.yml.in b/.circleci/config.yml.in index 6b5b31a3e6b..df4ad5cb310 100644 --- a/.circleci/config.yml.in +++ b/.circleci/config.yml.in @@ -174,6 +174,26 @@ commands: - store_test_results: path: test-results + download_model_weights: + parameters: + extract_roots: + type: string + default: "torchvision/models" + background: + type: boolean + default: true + steps: + - apt_install: + args: parallel wget + descr: Install download utilitites + - run: + name: Download model weights + background: << parameters.background >> + command: | + mkdir -p ~/.cache/torch/hub/checkpoints + python scripts/collect_model_urls.py << parameters.extract_roots >> \ + | parallel -j0 'wget --no-verbose -O ~/.cache/torch/hub/checkpoints/`basename {}` {}\?source=ci' + binary_common: &binary_common parameters: # Edit these defaults to do a release @@ -354,14 +374,7 @@ jobs: resource_class: xlarge steps: - checkout - - run: - name: Download model weights - background: true - command: | - sudo apt update -qy && sudo apt install -qy parallel wget - mkdir -p ~/.cache/torch/hub/checkpoints - python scripts/collect_model_urls.py torchvision/models \ - | parallel -j0 'wget --no-verbose -O ~/.cache/torch/hub/checkpoints/`basename {}` {}\?source=ci' + - download_model_weights - install_torchvision - run: name: Enable extended tests @@ -1021,12 +1034,13 @@ jobs: build_docs: <<: *binary_common docker: - - image: "pytorch/manylinux-cuda100" + - image: circleci/python:3.7 resource_class: 2xlarge+ steps: - attach_workspace: at: ~/workspace - checkout + - download_model_weights - run: name: Setup command: .circleci/unittest/linux/scripts/setup_env.sh diff --git a/ios/LibTorchvision.podspec b/ios/LibTorchvision.podspec index 2260bc5fcfc..3e760b77edd 100644 --- a/ios/LibTorchvision.podspec +++ b/ios/LibTorchvision.podspec @@ -1,8 +1,8 @@ -pytorch_version = '1.10.0' +pytorch_version = '1.11.0' Pod::Spec.new do |s| s.name = 'LibTorchvision' - s.version = '0.11.1' + s.version = '0.12.0' s.authors = 'PyTorch Team' s.license = { :type => 'BSD' } s.homepage = 'https://github.com/pytorch/vision' diff --git a/scripts/collect_model_urls.py b/scripts/collect_model_urls.py index 3554e80b1ed..2acba6cbbda 100644 --- a/scripts/collect_model_urls.py +++ b/scripts/collect_model_urls.py @@ -2,21 +2,19 @@ import re import sys -MODEL_URL_PATTERN = re.compile(r"https://download[.]pytorch[.]org/models/.*?[.]pth") +MODEL_URL_PATTERN = re.compile(r"https://download[.]pytorch[.]org/models/.+?[.]pth") -def main(root): +def main(*roots): model_urls = set() - for path in pathlib.Path(root).glob("**/*"): - if path.name.startswith("_") or not path.suffix == ".py": - continue - - with open(path, "r") as file: - for line in file: - model_urls.update(MODEL_URL_PATTERN.findall(line)) + for root in roots: + for path in pathlib.Path(root).rglob("*.py"): + with open(path, "r") as file: + for line in file: + model_urls.update(MODEL_URL_PATTERN.findall(line)) print("\n".join(sorted(model_urls))) if __name__ == "__main__": - main(sys.argv[1]) + main(*sys.argv[1:]) diff --git a/test/test_prototype_transforms.py b/test/test_prototype_transforms.py index 5b0693a2e78..b6085bb1c71 100644 --- a/test/test_prototype_transforms.py +++ b/test/test_prototype_transforms.py @@ -71,6 +71,7 @@ class TestSmoke: transforms.CenterCrop([16, 16]), transforms.ConvertImageDtype(), transforms.RandomHorizontalFlip(), + transforms.Pad(5), ) def test_common(self, transform, input): transform(input) diff --git a/torchvision/prototype/transforms/__init__.py b/torchvision/prototype/transforms/__init__.py index 48581a23930..be192fa3f5d 100644 --- a/torchvision/prototype/transforms/__init__.py +++ b/torchvision/prototype/transforms/__init__.py @@ -13,6 +13,7 @@ TenCrop, BatchMultiCrop, RandomHorizontalFlip, + Pad, RandomZoomOut, ) from ._meta import ConvertBoundingBoxFormat, ConvertImageDtype, ConvertImageColorSpace diff --git a/torchvision/prototype/transforms/_geometry.py b/torchvision/prototype/transforms/_geometry.py index 3276a8b2ab2..6a05a8895f7 100644 --- a/torchvision/prototype/transforms/_geometry.py +++ b/torchvision/prototype/transforms/_geometry.py @@ -1,5 +1,6 @@ import collections.abc import math +import numbers import warnings from typing import Any, Dict, List, Union, Sequence, Tuple, cast @@ -9,6 +10,7 @@ from torchvision.prototype.transforms import Transform, functional as F from torchvision.transforms.functional import pil_to_tensor, InterpolationMode from torchvision.transforms.transforms import _setup_size, _interpolation_modes_from_int +from typing_extensions import Literal from ._utils import query_image, get_image_dimensions, has_any, is_simple_tensor @@ -272,42 +274,31 @@ def apply_recursively(obj: Any) -> Any: return apply_recursively(inputs if len(inputs) > 1 else inputs[0]) -class RandomZoomOut(Transform): +class Pad(Transform): def __init__( - self, fill: Union[float, Sequence[float]] = 0.0, side_range: Tuple[float, float] = (1.0, 4.0), p: float = 0.5 + self, + padding: Union[int, Sequence[int]], + fill: Union[float, Sequence[float]] = 0.0, + padding_mode: Literal["constant", "edge", "reflect", "symmetric"] = "constant", ) -> None: super().__init__() + if not isinstance(padding, (numbers.Number, tuple, list)): + raise TypeError("Got inappropriate padding arg") - if fill is None: - fill = 0.0 - self.fill = fill - - self.side_range = side_range - if side_range[0] < 1.0 or side_range[0] > side_range[1]: - raise ValueError(f"Invalid canvas side range provided {side_range}.") - - self.p = p - - def _get_params(self, sample: Any) -> Dict[str, Any]: - image = query_image(sample) - orig_c, orig_h, orig_w = get_image_dimensions(image) - - r = self.side_range[0] + torch.rand(1) * (self.side_range[1] - self.side_range[0]) - canvas_width = int(orig_w * r) - canvas_height = int(orig_h * r) + if not isinstance(fill, (numbers.Number, str, tuple, list)): + raise TypeError("Got inappropriate fill arg") - r = torch.rand(2) - left = int((canvas_width - orig_w) * r[0]) - top = int((canvas_height - orig_h) * r[1]) - right = canvas_width - (left + orig_w) - bottom = canvas_height - (top + orig_h) - padding = [left, top, right, bottom] + if padding_mode not in ["constant", "edge", "reflect", "symmetric"]: + raise ValueError("Padding mode should be either constant, edge, reflect or symmetric") - fill = self.fill - if not isinstance(fill, collections.abc.Sequence): - fill = [fill] * orig_c + if isinstance(padding, Sequence) and len(padding) not in [1, 2, 4]: + raise ValueError( + f"Padding must be an int or a 1, 2, or 4 element tuple, not a {len(padding)} element tuple" + ) - return dict(padding=padding, fill=fill) + self.padding = padding + self.fill = fill + self.padding_mode = padding_mode def _transform(self, input: Any, params: Dict[str, Any]) -> Any: if isinstance(input, features.Image) or is_simple_tensor(input): @@ -349,6 +340,48 @@ def _transform(self, input: Any, params: Dict[str, Any]) -> Any: else: return input + +class RandomZoomOut(Transform): + def __init__( + self, fill: Union[float, Sequence[float]] = 0.0, side_range: Tuple[float, float] = (1.0, 4.0), p: float = 0.5 + ) -> None: + super().__init__() + + if fill is None: + fill = 0.0 + self.fill = fill + + self.side_range = side_range + if side_range[0] < 1.0 or side_range[0] > side_range[1]: + raise ValueError(f"Invalid canvas side range provided {side_range}.") + + self.p = p + + def _get_params(self, sample: Any) -> Dict[str, Any]: + image = query_image(sample) + orig_c, orig_h, orig_w = get_image_dimensions(image) + + r = self.side_range[0] + torch.rand(1) * (self.side_range[1] - self.side_range[0]) + canvas_width = int(orig_w * r) + canvas_height = int(orig_h * r) + + r = torch.rand(2) + left = int((canvas_width - orig_w) * r[0]) + top = int((canvas_height - orig_h) * r[1]) + right = canvas_width - (left + orig_w) + bottom = canvas_height - (top + orig_h) + padding = [left, top, right, bottom] + + fill = self.fill + if not isinstance(fill, collections.abc.Sequence): + fill = [fill] * orig_c + + return dict(padding=padding, fill=fill) + + def _transform(self, input: Any, params: Dict[str, Any]) -> Any: + transform = Pad(**params, padding_mode="constant") + return transform(input) + def forward(self, *inputs: Any) -> Any: sample = inputs if len(inputs) > 1 else inputs[0] if torch.rand(1) >= self.p: