Skip to content

migrate caltech prototype datasets #5749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 25 additions & 16 deletions test/builtin_dataset_mocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,8 @@ def cifar100(root, config):
return len(train_files if config["split"] == "train" else test_files)


# @register_mock
def caltech101(info, root, config):
@register_mock(configs=[dict()])
def caltech101(root, config):
def create_ann_file(root, name):
import scipy.io

Expand All @@ -390,15 +390,17 @@ def create_ann_folder(root, name, file_name_fn, num_examples):
images_root = root / "101_ObjectCategories"
anns_root = root / "Annotations"

ann_category_map = {
"Faces_2": "Faces",
"Faces_3": "Faces_easy",
"Motorbikes_16": "Motorbikes",
"Airplanes_Side_2": "airplanes",
image_category_map = {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The map was inversed before and did nothing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the annotation folder had the same name as the image folder. I'm wondering why the test didn't fail then. Is it because the dataset code doesn't actually care about the annotation folder names?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To give some context: Caltech101 uses folders to separate the categories. The folder names match between the images and annotations except for four exceptions. The implementation matches the annotations folder names with this map

_ANNS_CATEGORY_MAP = {
"Faces_2": "Faces",
"Faces_3": "Faces_easy",
"Motorbikes_16": "Motorbikes",
"Airplanes_Side_2": "airplanes",
}

and this logic

if category in self._ANNS_CATEGORY_MAP:
category = self._ANNS_CATEGORY_MAP[category]

The old mock data used the already matched names for the annotations so the dataset didn't need to do any matching. This means that the test were passing fine, although the implementation might be broken (it wasn't), since the matching was never tested.

"Faces": "Faces_2",
"Faces_easy": "Faces_3",
"Motorbikes": "Motorbikes_16",
"airplanes": "Airplanes_Side_2",
}

categories = ["Faces", "Faces_easy", "Motorbikes", "airplanes", "yin_yang"]

num_images_per_category = 2
for category in info.categories:
for category in categories:
create_image_folder(
root=images_root,
name=category,
Expand All @@ -407,7 +409,7 @@ def create_ann_folder(root, name, file_name_fn, num_examples):
)
create_ann_folder(
root=anns_root,
name=ann_category_map.get(category, category),
name=image_category_map.get(category, category),
file_name_fn=lambda idx: f"annotation_{idx + 1:04d}.mat",
num_examples=num_images_per_category,
)
Expand All @@ -417,27 +419,34 @@ def create_ann_folder(root, name, file_name_fn, num_examples):

make_tar(root, f"{anns_root.name}.tar", anns_root)

return num_images_per_category * len(info.categories)
return num_images_per_category * len(categories)


# @register_mock
def caltech256(info, root, config):
@register_mock(configs=[dict()])
def caltech256(root, config):
dir = root / "256_ObjectCategories"
num_images_per_category = 2

for idx, category in enumerate(info.categories, 1):
categories = [
(1, "ak47"),
(127, "laptop-101"),
(198, "spider"),
(257, "clutter"),
]

for category_idx, category in categories:
files = create_image_folder(
dir,
name=f"{idx:03d}.{category}",
file_name_fn=lambda image_idx: f"{idx:03d}_{image_idx + 1:04d}.jpg",
name=f"{category_idx:03d}.{category}",
file_name_fn=lambda image_idx: f"{category_idx:03d}_{image_idx + 1:04d}.jpg",
num_examples=num_images_per_category,
)
if category == "spider":
open(files[0].parent / "RENAME2", "w").close()

make_tar(root, f"{dir.name}.tar", dir)

return num_images_per_category * len(info.categories)
return num_images_per_category * len(categories)


@register_mock(configs=combinations_grid(split=("train", "val", "test")))
Expand Down
117 changes: 76 additions & 41 deletions torchvision/prototype/datasets/_builtin/caltech.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pathlib
import re
from typing import Any, Dict, List, Tuple, BinaryIO
from typing import Any, Dict, List, Tuple, BinaryIO, Union

import numpy as np
from torchdata.datapipes.iter import (
Expand All @@ -9,26 +9,49 @@
Filter,
IterKeyZipper,
)
from torchvision.prototype.datasets.utils import (
Dataset,
DatasetConfig,
DatasetInfo,
HttpResource,
OnlineResource,
from torchvision.prototype.datasets.utils import Dataset2, DatasetInfo, HttpResource, OnlineResource
from torchvision.prototype.datasets.utils._internal import (
INFINITE_BUFFER_SIZE,
read_mat,
hint_sharding,
hint_shuffling,
BUILTIN_DIR,
)
from torchvision.prototype.datasets.utils._internal import INFINITE_BUFFER_SIZE, read_mat, hint_sharding, hint_shuffling
from torchvision.prototype.features import Label, BoundingBox, _Feature, EncodedImage

from .._api import register_dataset, register_info

class Caltech101(Dataset):
def _make_info(self) -> DatasetInfo:
return DatasetInfo(
"caltech101",

CALTECH101_CATEGORIES, *_ = zip(*DatasetInfo.read_categories_file(BUILTIN_DIR / "caltech101.categories"))


@register_info("caltech101")
def _caltech101_info() -> Dict[str, Any]:
return dict(categories=CALTECH101_CATEGORIES)


@register_dataset("caltech101")
class Caltech101(Dataset2):
"""
- **homepage**: http://www.vision.caltech.edu/Image_Datasets/Caltech101
- **dependencies**:
- <scipy `https://scipy.org/`>_
"""

def __init__(
self,
root: Union[str, pathlib.Path],
skip_integrity_check: bool = False,
) -> None:
self._categories = _caltech101_info()["categories"]

super().__init__(
root,
dependencies=("scipy",),
homepage="http://www.vision.caltech.edu/Image_Datasets/Caltech101",
skip_integrity_check=skip_integrity_check,
)

def resources(self, config: DatasetConfig) -> List[OnlineResource]:
def _resources(self) -> List[OnlineResource]:
images = HttpResource(
"http://www.vision.caltech.edu/Image_Datasets/Caltech101/101_ObjectCategories.tar.gz",
sha256="af6ece2f339791ca20f855943d8b55dd60892c0a25105fcd631ee3d6430f9926",
Expand Down Expand Up @@ -88,7 +111,7 @@ def _prepare_sample(
ann = read_mat(ann_buffer)

return dict(
label=Label.from_category(category, categories=self.categories),
label=Label.from_category(category, categories=self._categories),
image_path=image_path,
image=image,
ann_path=ann_path,
Expand All @@ -98,12 +121,7 @@ def _prepare_sample(
contour=_Feature(ann["obj_contour"].T),
)

def _make_datapipe(
self,
resource_dps: List[IterDataPipe],
*,
config: DatasetConfig,
) -> IterDataPipe[Dict[str, Any]]:
def _datapipe(self, resource_dps: List[IterDataPipe]) -> IterDataPipe[Dict[str, Any]]:
images_dp, anns_dp = resource_dps

images_dp = Filter(images_dp, self._is_not_background_image)
Expand All @@ -122,23 +140,42 @@ def _make_datapipe(
)
return Mapper(dp, self._prepare_sample)

def _generate_categories(self, root: pathlib.Path) -> List[str]:
resources = self.resources(self.default_config)
def __len__(self) -> int:
return 8677

def _generate_categories(self) -> List[str]:
resources = self._resources()

dp = resources[0].load(root)
dp = resources[0].load(self._root)
dp = Filter(dp, self._is_not_background_image)

return sorted({pathlib.Path(path).parent.name for path, _ in dp})


class Caltech256(Dataset):
def _make_info(self) -> DatasetInfo:
return DatasetInfo(
"caltech256",
homepage="http://www.vision.caltech.edu/Image_Datasets/Caltech256",
)
CALTECH256_CATEGORIES, *_ = zip(*DatasetInfo.read_categories_file(BUILTIN_DIR / "caltech256.categories"))


@register_info("caltech256")
def _caltech256_info() -> Dict[str, Any]:
return dict(categories=CALTECH256_CATEGORIES)


@register_dataset("caltech256")
class Caltech256(Dataset2):
"""
- **homepage**: http://www.vision.caltech.edu/Image_Datasets/Caltech256
"""

def resources(self, config: DatasetConfig) -> List[OnlineResource]:
def __init__(
self,
root: Union[str, pathlib.Path],
skip_integrity_check: bool = False,
) -> None:
self._categories = _caltech256_info()["categories"]

super().__init__(root, skip_integrity_check=skip_integrity_check)

def _resources(self) -> List[OnlineResource]:
return [
HttpResource(
"http://www.vision.caltech.edu/Image_Datasets/Caltech256/256_ObjectCategories.tar",
Expand All @@ -156,25 +193,23 @@ def _prepare_sample(self, data: Tuple[str, BinaryIO]) -> Dict[str, Any]:
return dict(
path=path,
image=EncodedImage.from_file(buffer),
label=Label(int(pathlib.Path(path).parent.name.split(".", 1)[0]) - 1, categories=self.categories),
label=Label(int(pathlib.Path(path).parent.name.split(".", 1)[0]) - 1, categories=self._categories),
)

def _make_datapipe(
self,
resource_dps: List[IterDataPipe],
*,
config: DatasetConfig,
) -> IterDataPipe[Dict[str, Any]]:
def _datapipe(self, resource_dps: List[IterDataPipe]) -> IterDataPipe[Dict[str, Any]]:
dp = resource_dps[0]
dp = Filter(dp, self._is_not_rogue_file)
dp = hint_shuffling(dp)
dp = hint_sharding(dp)
return Mapper(dp, self._prepare_sample)

def _generate_categories(self, root: pathlib.Path) -> List[str]:
resources = self.resources(self.default_config)
def __len__(self) -> int:
return 30607

def _generate_categories(self) -> List[str]:
resources = self._resources()

dp = resources[0].load(root)
dp = resources[0].load(self._root)
dir_names = {pathlib.Path(path).parent.name for path, _ in dp}

return [name.split(".")[1] for name in sorted(dir_names)]