From 3c239ab6e95e891aa8a88b24c48f10cd3c387fdb Mon Sep 17 00:00:00 2001 From: Philip Meier Date: Tue, 31 Jan 2023 15:43:36 +0100 Subject: [PATCH 1/2] move datapoints compat to prototype datasets --- .../prototype/datapoints/_datapoint.py | 19 +------------------ .../prototype/datasets/_builtin/caltech.py | 5 ++--- .../prototype/datasets/_builtin/celeba.py | 5 ++--- .../prototype/datasets/_builtin/coco.py | 7 +++---- .../prototype/datasets/_builtin/cub200.py | 5 ++--- .../prototype/datasets/_builtin/sbd.py | 9 +++++---- .../prototype/datasets/utils/__init__.py | 2 +- .../utils/{_encoded.py => _datapoints.py} | 19 +++++++++++++++++++ 8 files changed, 35 insertions(+), 36 deletions(-) rename torchvision/prototype/datasets/utils/{_encoded.py => _datapoints.py} (74%) diff --git a/torchvision/prototype/datapoints/_datapoint.py b/torchvision/prototype/datapoints/_datapoint.py index fbd19ad86f1..89c08a86477 100644 --- a/torchvision/prototype/datapoints/_datapoint.py +++ b/torchvision/prototype/datapoints/_datapoint.py @@ -29,26 +29,9 @@ def _to_tensor( requires_grad = data.requires_grad if isinstance(data, torch.Tensor) else False return torch.as_tensor(data, dtype=dtype, device=device).requires_grad_(requires_grad) - # FIXME: this is just here for BC with the prototype datasets. Some datasets use the Datapoint directly to have a - # a no-op input for the prototype transforms. For this use case, we can't use plain tensors, since they will be - # interpreted as images. We should decide if we want a public no-op datapoint like `GenericDatapoint` or make this - # one public again. - def __new__( - cls, - data: Any, - dtype: Optional[torch.dtype] = None, - device: Optional[Union[torch.device, str, int]] = None, - requires_grad: Optional[bool] = None, - ) -> Datapoint: - tensor = cls._to_tensor(data, dtype=dtype, device=device, requires_grad=requires_grad) - return tensor.as_subclass(Datapoint) - @classmethod def wrap_like(cls: Type[D], other: D, tensor: torch.Tensor) -> D: - # FIXME: this is just here for BC with the prototype datasets. See __new__ for details. If that is resolved, - # this method should be made abstract - # raise NotImplementedError - return tensor.as_subclass(cls) + raise NotImplementedError _NO_WRAPPING_EXCEPTIONS = { torch.Tensor.clone: lambda cls, input, output: cls.wrap_like(input, output), diff --git a/torchvision/prototype/datasets/_builtin/caltech.py b/torchvision/prototype/datasets/_builtin/caltech.py index 55a77c1a920..f880479ef54 100644 --- a/torchvision/prototype/datasets/_builtin/caltech.py +++ b/torchvision/prototype/datasets/_builtin/caltech.py @@ -5,8 +5,7 @@ import numpy as np from torchdata.datapipes.iter import Filter, IterDataPipe, IterKeyZipper, Mapper from torchvision.prototype.datapoints import BoundingBox, Label -from torchvision.prototype.datapoints._datapoint import Datapoint -from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GDriveResource, OnlineResource +from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GDriveResource, GenericDatapoint, OnlineResource from torchvision.prototype.datasets.utils._internal import ( hint_sharding, hint_shuffling, @@ -115,7 +114,7 @@ def _prepare_sample( format="xyxy", spatial_size=image.spatial_size, ), - contour=Datapoint(ann["obj_contour"].T), + contour=GenericDatapoint(ann["obj_contour"].T), ) def _datapipe(self, resource_dps: List[IterDataPipe]) -> IterDataPipe[Dict[str, Any]]: diff --git a/torchvision/prototype/datasets/_builtin/celeba.py b/torchvision/prototype/datasets/_builtin/celeba.py index 9050cf0b596..3c8b16fafb0 100644 --- a/torchvision/prototype/datasets/_builtin/celeba.py +++ b/torchvision/prototype/datasets/_builtin/celeba.py @@ -4,8 +4,7 @@ from torchdata.datapipes.iter import Filter, IterDataPipe, IterKeyZipper, Mapper, Zipper from torchvision.prototype.datapoints import BoundingBox, Label -from torchvision.prototype.datapoints._datapoint import Datapoint -from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GDriveResource, OnlineResource +from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GDriveResource, GenericDatapoint, OnlineResource from torchvision.prototype.datasets.utils._internal import ( getitem, hint_sharding, @@ -149,7 +148,7 @@ def _prepare_sample( spatial_size=image.spatial_size, ), landmarks={ - landmark: Datapoint((int(landmarks[f"{landmark}_x"]), int(landmarks[f"{landmark}_y"]))) + landmark: GenericDatapoint((int(landmarks[f"{landmark}_x"]), int(landmarks[f"{landmark}_y"]))) for landmark in {key[:-2] for key in landmarks.keys()} }, ) diff --git a/torchvision/prototype/datasets/_builtin/coco.py b/torchvision/prototype/datasets/_builtin/coco.py index fa68bf4dc6f..5d82d1e8a65 100644 --- a/torchvision/prototype/datasets/_builtin/coco.py +++ b/torchvision/prototype/datasets/_builtin/coco.py @@ -15,8 +15,7 @@ UnBatcher, ) from torchvision.prototype.datapoints import BoundingBox, Label, Mask -from torchvision.prototype.datapoints._datapoint import Datapoint -from torchvision.prototype.datasets.utils import Dataset, EncodedImage, HttpResource, OnlineResource +from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GenericDatapoint, HttpResource, OnlineResource from torchvision.prototype.datasets.utils._internal import ( getitem, hint_sharding, @@ -124,8 +123,8 @@ def _decode_instances_anns(self, anns: List[Dict[str, Any]], image_meta: Dict[st ] ) ), - areas=Datapoint([ann["area"] for ann in anns]), - crowds=Datapoint([ann["iscrowd"] for ann in anns], dtype=torch.bool), + areas=GenericDatapoint([ann["area"] for ann in anns]), + crowds=GenericDatapoint([ann["iscrowd"] for ann in anns], dtype=torch.bool), bounding_boxes=BoundingBox( [ann["bbox"] for ann in anns], format="xywh", diff --git a/torchvision/prototype/datasets/_builtin/cub200.py b/torchvision/prototype/datasets/_builtin/cub200.py index ea192baf650..6d39680e8e7 100644 --- a/torchvision/prototype/datasets/_builtin/cub200.py +++ b/torchvision/prototype/datasets/_builtin/cub200.py @@ -15,8 +15,7 @@ ) from torchdata.datapipes.map import IterToMapConverter from torchvision.prototype.datapoints import BoundingBox, Label -from torchvision.prototype.datapoints._datapoint import Datapoint -from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GDriveResource, OnlineResource +from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GDriveResource, GenericDatapoint, OnlineResource from torchvision.prototype.datasets.utils._internal import ( getitem, hint_sharding, @@ -162,7 +161,7 @@ def _2010_prepare_ann( format="xyxy", spatial_size=spatial_size, ), - segmentation=Datapoint(content["seg"]), + segmentation=GenericDatapoint(content["seg"]), ) def _prepare_sample( diff --git a/torchvision/prototype/datasets/_builtin/sbd.py b/torchvision/prototype/datasets/_builtin/sbd.py index c9f054b2c9e..3a4cb9c5a0d 100644 --- a/torchvision/prototype/datasets/_builtin/sbd.py +++ b/torchvision/prototype/datasets/_builtin/sbd.py @@ -4,8 +4,7 @@ import numpy as np from torchdata.datapipes.iter import Demultiplexer, Filter, IterDataPipe, IterKeyZipper, LineReader, Mapper -from torchvision.prototype.datapoints._datapoint import Datapoint -from torchvision.prototype.datasets.utils import Dataset, EncodedImage, HttpResource, OnlineResource +from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GenericDatapoint, HttpResource, OnlineResource from torchvision.prototype.datasets.utils._internal import ( getitem, hint_sharding, @@ -92,8 +91,10 @@ def _prepare_sample(self, data: Tuple[Tuple[Any, Tuple[str, BinaryIO]], Tuple[st image=EncodedImage.from_file(image_buffer), ann_path=ann_path, # the boundaries are stored in sparse CSC format, which is not supported by PyTorch - boundaries=Datapoint(np.stack([raw_boundary.toarray() for raw_boundary in anns["Boundaries"].item()])), - segmentation=Datapoint(anns["Segmentation"].item()), + boundaries=GenericDatapoint( + np.stack([raw_boundary.toarray() for raw_boundary in anns["Boundaries"].item()]) + ), + segmentation=GenericDatapoint(anns["Segmentation"].item()), ) def _datapipe(self, resource_dps: List[IterDataPipe]) -> IterDataPipe[Dict[str, Any]]: diff --git a/torchvision/prototype/datasets/utils/__init__.py b/torchvision/prototype/datasets/utils/__init__.py index 3fdb53eec43..7b94a25a106 100644 --- a/torchvision/prototype/datasets/utils/__init__.py +++ b/torchvision/prototype/datasets/utils/__init__.py @@ -1,4 +1,4 @@ from . import _internal # usort: skip +from ._datapoints import EncodedData, EncodedImage, GenericDatapoint from ._dataset import Dataset -from ._encoded import EncodedData, EncodedImage from ._resource import GDriveResource, HttpResource, KaggleDownloadResource, ManualDownloadResource, OnlineResource diff --git a/torchvision/prototype/datasets/utils/_encoded.py b/torchvision/prototype/datasets/utils/_datapoints.py similarity index 74% rename from torchvision/prototype/datasets/utils/_encoded.py rename to torchvision/prototype/datasets/utils/_datapoints.py index 64cd9f7b951..203fd7b30fe 100644 --- a/torchvision/prototype/datasets/utils/_encoded.py +++ b/torchvision/prototype/datasets/utils/_datapoints.py @@ -10,6 +10,25 @@ from torchvision.prototype.datapoints._datapoint import Datapoint from torchvision.prototype.utils._internal import fromfile, ReadOnlyTensorBuffer + +class GenericDatapoint(Datapoint): + def __new__( + cls, + data: Any, + *, + dtype: Optional[torch.dtype] = None, + device: Optional[Union[torch.device, str, int]] = None, + requires_grad: Optional[bool] = None, + ) -> GenericDatapoint: + tensor = cls._to_tensor(data, dtype=dtype, device=device, requires_grad=requires_grad) + return tensor.as_subclass(cls) + + @classmethod + def wrap_like(cls: Type[GenericDatapoint], other: GenericDatapoint, tensor: torch.Tensor) -> GenericDatapoint: + generic_datapoint = tensor.as_subclass(cls) + return generic_datapoint + + D = TypeVar("D", bound="EncodedData") From b80227e6f8a5a4c4acf3de514fd1fe3ea4cd6c25 Mon Sep 17 00:00:00 2001 From: Philip Meier Date: Fri, 10 Feb 2023 15:24:23 +0100 Subject: [PATCH 2/2] remove GenericDatapoint after introduction of simple tensor heuristic --- test/test_prototype_datasets_builtin.py | 16 ++++++++++------ .../prototype/datasets/_builtin/caltech.py | 6 ++++-- .../prototype/datasets/_builtin/celeba.py | 5 +++-- .../prototype/datasets/_builtin/coco.py | 6 +++--- .../prototype/datasets/_builtin/cub200.py | 5 +++-- .../prototype/datasets/_builtin/sbd.py | 7 ++++--- .../prototype/datasets/utils/__init__.py | 2 +- .../utils/{_datapoints.py => _encoded.py} | 19 ------------------- 8 files changed, 28 insertions(+), 38 deletions(-) rename torchvision/prototype/datasets/utils/{_datapoints.py => _encoded.py} (74%) diff --git a/test/test_prototype_datasets_builtin.py b/test/test_prototype_datasets_builtin.py index 896023fd4b1..7b33dc3e8a0 100644 --- a/test/test_prototype_datasets_builtin.py +++ b/test/test_prototype_datasets_builtin.py @@ -21,6 +21,7 @@ from torchdata.datapipes.utils import StreamWrapper from torchvision._utils import sequence_to_str from torchvision.prototype import datapoints, datasets, transforms +from torchvision.prototype.datasets.utils import EncodedImage from torchvision.prototype.datasets.utils._internal import INFINITE_BUFFER_SIZE @@ -136,18 +137,21 @@ def make_msg_and_close(head): raise AssertionError(make_msg_and_close("The following streams were not closed after a full iteration:")) @parametrize_dataset_mocks(DATASET_MOCKS) - def test_no_simple_tensors(self, dataset_mock, config): + def test_no_unaccompanied_simple_tensors(self, dataset_mock, config): dataset, _ = dataset_mock.load(config) + sample = next_consume(iter(dataset)) simple_tensors = { - key - for key, value in next_consume(iter(dataset)).items() - if torchvision.prototype.transforms.utils.is_simple_tensor(value) + key for key, value in sample.items() if torchvision.prototype.transforms.utils.is_simple_tensor(value) } - if simple_tensors: + + if simple_tensors and not any( + isinstance(item, (datapoints.Image, datapoints.Video, EncodedImage)) for item in sample.values() + ): raise AssertionError( f"The values of key(s) " - f"{sequence_to_str(sorted(simple_tensors), separate_last='and ')} contained simple tensors." + f"{sequence_to_str(sorted(simple_tensors), separate_last='and ')} contained simple tensors, " + f"but didn't find any (encoded) image or video." ) @parametrize_dataset_mocks(DATASET_MOCKS) diff --git a/torchvision/prototype/datasets/_builtin/caltech.py b/torchvision/prototype/datasets/_builtin/caltech.py index f880479ef54..d8f560a36ff 100644 --- a/torchvision/prototype/datasets/_builtin/caltech.py +++ b/torchvision/prototype/datasets/_builtin/caltech.py @@ -3,9 +3,11 @@ from typing import Any, BinaryIO, Dict, List, Tuple, Union import numpy as np + +import torch from torchdata.datapipes.iter import Filter, IterDataPipe, IterKeyZipper, Mapper from torchvision.prototype.datapoints import BoundingBox, Label -from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GDriveResource, GenericDatapoint, OnlineResource +from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GDriveResource, OnlineResource from torchvision.prototype.datasets.utils._internal import ( hint_sharding, hint_shuffling, @@ -114,7 +116,7 @@ def _prepare_sample( format="xyxy", spatial_size=image.spatial_size, ), - contour=GenericDatapoint(ann["obj_contour"].T), + contour=torch.as_tensor(ann["obj_contour"].T), ) def _datapipe(self, resource_dps: List[IterDataPipe]) -> IterDataPipe[Dict[str, Any]]: diff --git a/torchvision/prototype/datasets/_builtin/celeba.py b/torchvision/prototype/datasets/_builtin/celeba.py index 3c8b16fafb0..66999c4c50b 100644 --- a/torchvision/prototype/datasets/_builtin/celeba.py +++ b/torchvision/prototype/datasets/_builtin/celeba.py @@ -2,9 +2,10 @@ import pathlib from typing import Any, BinaryIO, Dict, Iterator, List, Optional, Sequence, Tuple, Union +import torch from torchdata.datapipes.iter import Filter, IterDataPipe, IterKeyZipper, Mapper, Zipper from torchvision.prototype.datapoints import BoundingBox, Label -from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GDriveResource, GenericDatapoint, OnlineResource +from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GDriveResource, OnlineResource from torchvision.prototype.datasets.utils._internal import ( getitem, hint_sharding, @@ -148,7 +149,7 @@ def _prepare_sample( spatial_size=image.spatial_size, ), landmarks={ - landmark: GenericDatapoint((int(landmarks[f"{landmark}_x"]), int(landmarks[f"{landmark}_y"]))) + landmark: torch.tensor((int(landmarks[f"{landmark}_x"]), int(landmarks[f"{landmark}_y"]))) for landmark in {key[:-2] for key in landmarks.keys()} }, ) diff --git a/torchvision/prototype/datasets/_builtin/coco.py b/torchvision/prototype/datasets/_builtin/coco.py index 5d82d1e8a65..e02ca706b1e 100644 --- a/torchvision/prototype/datasets/_builtin/coco.py +++ b/torchvision/prototype/datasets/_builtin/coco.py @@ -15,7 +15,7 @@ UnBatcher, ) from torchvision.prototype.datapoints import BoundingBox, Label, Mask -from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GenericDatapoint, HttpResource, OnlineResource +from torchvision.prototype.datasets.utils import Dataset, EncodedImage, HttpResource, OnlineResource from torchvision.prototype.datasets.utils._internal import ( getitem, hint_sharding, @@ -123,8 +123,8 @@ def _decode_instances_anns(self, anns: List[Dict[str, Any]], image_meta: Dict[st ] ) ), - areas=GenericDatapoint([ann["area"] for ann in anns]), - crowds=GenericDatapoint([ann["iscrowd"] for ann in anns], dtype=torch.bool), + areas=torch.as_tensor([ann["area"] for ann in anns]), + crowds=torch.as_tensor([ann["iscrowd"] for ann in anns], dtype=torch.bool), bounding_boxes=BoundingBox( [ann["bbox"] for ann in anns], format="xywh", diff --git a/torchvision/prototype/datasets/_builtin/cub200.py b/torchvision/prototype/datasets/_builtin/cub200.py index 6d39680e8e7..db561f89ec6 100644 --- a/torchvision/prototype/datasets/_builtin/cub200.py +++ b/torchvision/prototype/datasets/_builtin/cub200.py @@ -3,6 +3,7 @@ import pathlib from typing import Any, BinaryIO, Callable, Dict, List, Optional, Tuple, Union +import torch from torchdata.datapipes.iter import ( CSVDictParser, CSVParser, @@ -15,7 +16,7 @@ ) from torchdata.datapipes.map import IterToMapConverter from torchvision.prototype.datapoints import BoundingBox, Label -from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GDriveResource, GenericDatapoint, OnlineResource +from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GDriveResource, OnlineResource from torchvision.prototype.datasets.utils._internal import ( getitem, hint_sharding, @@ -161,7 +162,7 @@ def _2010_prepare_ann( format="xyxy", spatial_size=spatial_size, ), - segmentation=GenericDatapoint(content["seg"]), + segmentation=torch.as_tensor(content["seg"]), ) def _prepare_sample( diff --git a/torchvision/prototype/datasets/_builtin/sbd.py b/torchvision/prototype/datasets/_builtin/sbd.py index 3a4cb9c5a0d..97986b58b5d 100644 --- a/torchvision/prototype/datasets/_builtin/sbd.py +++ b/torchvision/prototype/datasets/_builtin/sbd.py @@ -3,8 +3,9 @@ from typing import Any, BinaryIO, cast, Dict, List, Optional, Tuple, Union import numpy as np +import torch from torchdata.datapipes.iter import Demultiplexer, Filter, IterDataPipe, IterKeyZipper, LineReader, Mapper -from torchvision.prototype.datasets.utils import Dataset, EncodedImage, GenericDatapoint, HttpResource, OnlineResource +from torchvision.prototype.datasets.utils import Dataset, EncodedImage, HttpResource, OnlineResource from torchvision.prototype.datasets.utils._internal import ( getitem, hint_sharding, @@ -91,10 +92,10 @@ def _prepare_sample(self, data: Tuple[Tuple[Any, Tuple[str, BinaryIO]], Tuple[st image=EncodedImage.from_file(image_buffer), ann_path=ann_path, # the boundaries are stored in sparse CSC format, which is not supported by PyTorch - boundaries=GenericDatapoint( + boundaries=torch.as_tensor( np.stack([raw_boundary.toarray() for raw_boundary in anns["Boundaries"].item()]) ), - segmentation=GenericDatapoint(anns["Segmentation"].item()), + segmentation=torch.as_tensor(anns["Segmentation"].item()), ) def _datapipe(self, resource_dps: List[IterDataPipe]) -> IterDataPipe[Dict[str, Any]]: diff --git a/torchvision/prototype/datasets/utils/__init__.py b/torchvision/prototype/datasets/utils/__init__.py index 7b94a25a106..3fdb53eec43 100644 --- a/torchvision/prototype/datasets/utils/__init__.py +++ b/torchvision/prototype/datasets/utils/__init__.py @@ -1,4 +1,4 @@ from . import _internal # usort: skip -from ._datapoints import EncodedData, EncodedImage, GenericDatapoint from ._dataset import Dataset +from ._encoded import EncodedData, EncodedImage from ._resource import GDriveResource, HttpResource, KaggleDownloadResource, ManualDownloadResource, OnlineResource diff --git a/torchvision/prototype/datasets/utils/_datapoints.py b/torchvision/prototype/datasets/utils/_encoded.py similarity index 74% rename from torchvision/prototype/datasets/utils/_datapoints.py rename to torchvision/prototype/datasets/utils/_encoded.py index 203fd7b30fe..64cd9f7b951 100644 --- a/torchvision/prototype/datasets/utils/_datapoints.py +++ b/torchvision/prototype/datasets/utils/_encoded.py @@ -10,25 +10,6 @@ from torchvision.prototype.datapoints._datapoint import Datapoint from torchvision.prototype.utils._internal import fromfile, ReadOnlyTensorBuffer - -class GenericDatapoint(Datapoint): - def __new__( - cls, - data: Any, - *, - dtype: Optional[torch.dtype] = None, - device: Optional[Union[torch.device, str, int]] = None, - requires_grad: Optional[bool] = None, - ) -> GenericDatapoint: - tensor = cls._to_tensor(data, dtype=dtype, device=device, requires_grad=requires_grad) - return tensor.as_subclass(cls) - - @classmethod - def wrap_like(cls: Type[GenericDatapoint], other: GenericDatapoint, tensor: torch.Tensor) -> GenericDatapoint: - generic_datapoint = tensor.as_subclass(cls) - return generic_datapoint - - D = TypeVar("D", bound="EncodedData")