|
@@ -1,93 +1,167 @@
|
|
|
# CV Datasets Wrapper
|
|
|
|
|
|
-<!-- For more deatils to see how to use this library take a look at [nabirds/display.py](nabirds/display.py). -->
|
|
|
-
|
|
|
-## Annotation and Image Loading
|
|
|
+## Installation
|
|
|
+```bash
|
|
|
+pip install cvdatasets
|
|
|
+```
|
|
|
|
|
|
-Here is some example code how to load images and use the predefined train-test split.
|
|
|
+## Motivation
|
|
|
+We want to follow the interface of custom [PyTorch datasets](https://pytorch.org/tutorials/beginner/basics/data_tutorial.html#creating-a-custom-dataset-for-your-files) (originally presented by [Chainer](https://docs.chainer.org/en/latest/reference/generated/chainer.dataset.DatasetMixin.html#chainer.dataset.DatasetMixin)):
|
|
|
|
|
|
```python
|
|
|
-# replace NAB_Annotations with CUB_Annotations to load CUB200-2011 annotations
|
|
|
-from cvdatasets import NAB_Annotations, Dataset
|
|
|
+class OurDataset(DatasetInterface):
|
|
|
+ def __init__(self, *args, **kwargs):
|
|
|
+ super().__init__()
|
|
|
+ # read the data annotations, select proper split, etc.
|
|
|
+
|
|
|
+ def __len__(self) -> int:
|
|
|
+ return len(self.images)
|
|
|
+
|
|
|
+ def __getitem__(self, index):
|
|
|
+ # read the image and the according label
|
|
|
+ # transform the image (e.g. with augmentations)
|
|
|
+ return img, label
|
|
|
+```
|
|
|
+
|
|
|
+Additionally, we would like to add support for reading of part annotations (or bounding boxes, hierarchies, etc.) and select the correct dataset annotations based on command-line arguments.
|
|
|
+The straight-forward way is to create a look-up file (we call it **data config file**) and store all required information there, e.g.:
|
|
|
+
|
|
|
+```yaml
|
|
|
+# data config file
|
|
|
+BASE_DIR: /data/your_data_folder/
|
|
|
|
|
|
-annot = NAB_Annotations("path/to/nab/folder")
|
|
|
+DATA_DIR: datasets
|
|
|
+
|
|
|
+DATASETS:
|
|
|
+ # each dataset is found as
|
|
|
+ # <BASE_DIR>/<DATA_DIR>/<DATASET.folder>/<DATASET.annotation>
|
|
|
+ CUB200:
|
|
|
+ folder: birds
|
|
|
+ annotations: cub200/ORIGINAL
|
|
|
|
|
|
-train_data = Dataset(uuids=annot.train_uuids, annotations=annot)
|
|
|
-test_data = Dataset(uuids=annot.test_uuids, annotations=annot)
|
|
|
+ CUB200_2fold:
|
|
|
+ folder: birds
|
|
|
+ annotations: cub200/2fold
|
|
|
+
|
|
|
+ NAB:
|
|
|
+ folder: birds
|
|
|
+ annotations: NAB/2fold
|
|
|
|
|
|
-print("Loaded {} training and {} test images".format(len(train_data), len(test_data)))
|
|
|
```
|
|
|
|
|
|
-Alternatively, you can create an annotation and a dataset instance from a YAML dataset file:
|
|
|
+```python
|
|
|
+# your data initialization code
|
|
|
+data_config = "path/to/data/config_file.yml"
|
|
|
+annot = Annnotation.load(data_config, dataset="CUB200")
|
|
|
+train, test = annot.new_train_test_datasets()
|
|
|
+
|
|
|
+# now we can create any data loader that supports the before-mentioned dataset API:
|
|
|
+train_loader = DataLoader(train, batch_size=32)
|
|
|
+test_loader = DataLoader(test, batch_size=32)
|
|
|
+```
|
|
|
+
|
|
|
+The advantage of this approach is that you can have different data config files for different environments, but your data initialization code remains the same.
|
|
|
+
|
|
|
+
|
|
|
+## Basic usage
|
|
|
+Now we dive a bit deeper into the actual usage examples:
|
|
|
+
|
|
|
+### 1. Load annotations from a data config file
|
|
|
+The example in the motivation section is already almost a working example.
|
|
|
+We just need to modify the code a bit:
|
|
|
|
|
|
```python
|
|
|
-annot = NAB_Annotations("path/to/yaml/config_file.yml")
|
|
|
+from cvdatasets import AnnotationType
|
|
|
+from munch import munchify
|
|
|
|
|
|
-train_data = annot.new_dataset("train")
|
|
|
-test_data = annot.new_dataset("test")
|
|
|
+# this args can also be result of argparse's parse_args or any other data class
|
|
|
+args = munchify(dict(data="path/to/data/config_file.yml", dataset="CUB200"))
|
|
|
+
|
|
|
+annot = AnnotationType.new_annotation(args)
|
|
|
+train, test = annot.new_train_test_datasets()
|
|
|
```
|
|
|
|
|
|
-An example YAML dataset file could be the following:
|
|
|
+### 2. Load annotations without a data config file
|
|
|
+Alternatively, you can create an annotation instance directly by pointing to a directory.
|
|
|
+Hereby, we implemented *file list*, *folder*, and *JSON* annotations:
|
|
|
|
|
|
-```yaml
|
|
|
-BASE_DIR: /data/your_data_folder/
|
|
|
+```python
|
|
|
+from cvdatasets import FileListAnnotations
|
|
|
+from cvdatasets import FolderAnnotations
|
|
|
+from cvdatasets import JSONAnnotations
|
|
|
+
|
|
|
+annot = FileListAnnotations(
|
|
|
+ root_or_infofile="path/to/eg/CUB200",
|
|
|
+ # this indicates which ID in the "tr_ID.txt" file is used for validation;
|
|
|
+ # all other ids in this file will be assigned to the training split
|
|
|
+ test_fold_id=0
|
|
|
+)
|
|
|
+
|
|
|
+annot = FolderAnnotations(
|
|
|
+ root_or_infofile="ImageNet/has/folder/annotations",
|
|
|
+ folders=dict( # < these are the default folders, where the different splits are selected on
|
|
|
+ train_images="train",
|
|
|
+ val_images="val",
|
|
|
+ test_images=("test", True) # < "True" indicates that the test folder is optional
|
|
|
+ )
|
|
|
+)
|
|
|
+
|
|
|
+annot = JSONAnnotations(root_or_infofile="iNaturalist/datasets/have/this")
|
|
|
+
|
|
|
+# afterwards proceed as usual:
|
|
|
+train, test = annot.new_train_test_datasets()
|
|
|
+```
|
|
|
|
|
|
-# in BASE_DIR should be "datasets" and "models" folder
|
|
|
-DATA_DIR: datasets
|
|
|
-MODEL_DIR: models
|
|
|
+### 3. Load datasets based on a custom datasets class
|
|
|
+Per default, the resulting dataset instances (`cvdatasets.dataset.Dataset`) will return a tuple of a numpy-array, parts (if present, otherwise `None`), and a label:
|
|
|
+```python
|
|
|
+im_array, parts, label = train[0]
|
|
|
+```
|
|
|
+
|
|
|
+There is a possibility to return an object (`cvdatasets.dataset.image.ImageWrapper`) holding a bunch of interesting information about the loaded image (e.g., a PIL instance of the image or the numpy representation):
|
|
|
+```python
|
|
|
+from cvdatasets.dataset import ImageWrapperDataset
|
|
|
|
|
|
+train, test = annot.new_train_test_datasets(dataset_cls=ImageWrapperDataset)
|
|
|
+im_obj = train[0]
|
|
|
|
|
|
-# in /data/your_data_folder/datasets should be "birds" and there should be a "cub200_11" folder with the CUB200 dataset. this represents default annotation folder.
|
|
|
-DATASETS:
|
|
|
- CUB200: &cub200
|
|
|
- folder: birds
|
|
|
- annotations: cub200_11
|
|
|
-
|
|
|
-# Here we define different types of part annotations
|
|
|
-PARTS:
|
|
|
- # uniform 5x5 parts
|
|
|
- UNI: &parts_uni
|
|
|
- <<: *cub200
|
|
|
- is_uniform: true
|
|
|
- annotations: cub200_11
|
|
|
- rescale_size: !!int -1
|
|
|
- scales:
|
|
|
- - 0.2
|
|
|
-
|
|
|
- # ground truth parts
|
|
|
- GT: &parts_gt
|
|
|
- <<: *cub200
|
|
|
- annotations: cub200_11
|
|
|
- rescale_size: !!int -1
|
|
|
- scales:
|
|
|
- - 0.31
|
|
|
-
|
|
|
- # NAC parts with 2 scales
|
|
|
- NAC: &parts_nac
|
|
|
- <<: *cub200
|
|
|
- annotations: NAC/2017-bilinear
|
|
|
- feature_suffix: 20parts_gt
|
|
|
- rescale_size: !!int 224
|
|
|
- scales:
|
|
|
- - 0.31
|
|
|
- - 0.45
|
|
|
+pil_image = im_obj.im
|
|
|
+numpy_array = im_obj.im_array
|
|
|
+
|
|
|
+# there is a shortcut to get the same output as the default Dataset class
|
|
|
+im_array, parts, label = im_obj.as_tuple()
|
|
|
```
|
|
|
|
|
|
+Using the same idea, you can also define your own dataset class and perform everything you want with these outputs (including applying augmentations):
|
|
|
|
|
|
-## Dataset Iteration
|
|
|
```python
|
|
|
-import matplotlib.pyplot as plt
|
|
|
|
|
|
-# either access the images directly
|
|
|
-im, parts, label = test_data[100]
|
|
|
-plt.imshow(im)
|
|
|
-plt.show()
|
|
|
+from torch.utils.data import Dataset as BaseDataset
|
|
|
+from torch.utils.data import DataLoader
|
|
|
+
|
|
|
+from cvdatasets import FileListAnnotations
|
|
|
+from cvdatasets import ImageWrapperDataset
|
|
|
+
|
|
|
+class Dataset(ImageWrapperDataset, BaseDataset):
|
|
|
+ def __init__(self, *args, **kwargs):
|
|
|
+ super().__init__(*args, **kwargs)
|
|
|
+ # inialize training and validation augmentations
|
|
|
+
|
|
|
+ def __getitem__(self, i):
|
|
|
+ im_obj = super().__getitem__(i)
|
|
|
+
|
|
|
+ pil_im = im_obj.im
|
|
|
+ label = im_obj.label
|
|
|
+
|
|
|
+ aug_im = self.augment(pil_im)
|
|
|
+
|
|
|
+ return aug_im, label
|
|
|
|
|
|
-# or iterate over the dataset
|
|
|
-for im, parts, label in train_data:
|
|
|
- plt.imshow(im)
|
|
|
- plt.show()
|
|
|
+annot = FileListAnnotations(root_or_infofile="path/to/CUB200")
|
|
|
+train, test = annot.new_train_test_datasets(dataset_cls=Dataset)
|
|
|
|
|
|
+train_loader = DataLoader(train, batch_size=32)
|
|
|
+test_loader = DataLoader(test, batch_size=32)
|
|
|
```
|
|
|
|
|
|
## Working with Part Annotations
|