Bladeren bron

updated the README

Dimitri Korsch 1 jaar geleden
bovenliggende
commit
10071ddb78
1 gewijzigde bestanden met toevoegingen van 138 en 64 verwijderingen
  1. 138 64
      README.md

+ 138 - 64
README.md

@@ -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