Source code for cocohelper.transforms.transform
"""Generic transformation for the COCO images and annotations.
"""
from typing import List, Tuple, Union
from abc import ABC, abstractmethod
from cocohelper import COCOHelper, COCOHelperPaths
from os.path import join
from pathlib import Path
from PIL import Image
import numpy as np
import json
# TODO: clean code in this file (there is a small margin of improvement)
[docs]class Transform(ABC):
[docs] def transform_dataset(
self,
coco: COCOHelper,
out_dir: Union[str, Path]
) -> COCOHelper:
"""
Apply an abstract transformation on the whole dataset.
TODO: should we apply on the whole dataset eagerly or use a lazy execution when the data is obtained?
- In the first case, *apply* takes a COCODataset and returns a new modified COCODataset.
- In the second case probably COCODataset should have a reference to a Transform and apply just-in-time
when an element is retrieved.
"""
if type(out_dir) == str:
out_dir = Path(out_dir)
json_fname = join(str(out_dir), COCOHelperPaths.ann_dir, COCOHelperPaths.ann_fname)
images_dir = join(str(out_dir), COCOHelperPaths.img_dir)
json_dataset = coco.to_json_dataset()
del json_dataset['paths']
images = []
annotations = []
for image in json_dataset['images']:
# transforms example
image_data, anns = coco.get_img_sample(img_id=image['id'])
tr_image, tr_anns = self.apply(image_data['image'], anns)
# save image
image_fname = images_dir / image['file_name']
image_fname.parent.mkdir(parents=True, exist_ok=True)
Image.fromarray(tr_image).save(image_fname)
# change height/width
h, w, _ = tr_image.shape
image['height'] = h
image['width'] = w
images.append(image)
annotations += tr_anns
json_dataset['images'] = images
json_dataset['annotations'] = annotations
Path(json_fname).parent.mkdir(parents=True, exist_ok=True)
with open(json_fname, 'w') as f:
json.dump(json_dataset, f)
return COCOHelper.load_json(json_fname)
[docs] @abstractmethod
def apply(
self,
img: np.ndarray,
anns: List[dict]
) -> Tuple[np.ndarray, List[dict]]:
"""
Apply the transformation to the image array and its annotations.
Args:
img: image array
anns: annotations for this image
Returns:
Transformed image array and annotations
"""
pass
[docs] @staticmethod
def compute_bbox_area(
bbox: List[int]
) -> int:
"""
Compute area from a bounding box.
Args:
bbox: bounding box.
Returns:
The area inside the given bounding box.
"""
_, _, w, h = bbox
return w * h