From edc65cb76ebbe65be84757008f381a86dd682e69 Mon Sep 17 00:00:00 2001
From: Mathieu Beligon <mathieu@feedly.com>
Date: Sun, 13 Sep 2020 17:41:44 +0200
Subject: [PATCH] [common] (image) make Image a type alias

---
 common/polystar/common/models/image.py        | 17 ++++++------
 .../common/models/image_annotation.py         |  6 ++---
 .../dataset/perturbations/perturbator.py      | 26 ++++++++++++-------
 .../common/dataset/perturbations/utils.py     |  8 +++---
 .../research/common/dataset/roco_dataset.py   |  5 ++--
 .../research/common/datasets/image_dataset.py |  2 +-
 .../datasets/roco/directory_roco_dataset.py   |  4 +--
 .../image_dataset_generator.py                |  4 +--
 .../roco/test_directory_dataset_zoo.py        |  4 +--
 .../robots_at_runes/dataset/labeled_image.py  |  7 +++--
 10 files changed, 44 insertions(+), 39 deletions(-)

diff --git a/common/polystar/common/models/image.py b/common/polystar/common/models/image.py
index 61af256..fcc4faa 100644
--- a/common/polystar/common/models/image.py
+++ b/common/polystar/common/models/image.py
@@ -4,19 +4,20 @@ from typing import Iterable
 import cv2
 from nptyping import Array
 
+Image = Array[int, ..., ..., 3]
 
-class Image(Array[int, ..., ..., 3]):
-    @staticmethod
-    def from_path(image_path: Path, conversion: int = cv2.COLOR_BGR2RGB) -> "Image":
-        return cv2.cvtColor(cv2.imread(str(image_path), cv2.IMREAD_UNCHANGED), conversion)
 
-    def save(self, image_path: Path, conversion: int = cv2.COLOR_RGB2BGR):
-        image_path.parent.mkdir(exist_ok=True, parents=True)
-        cv2.imwrite(str(image_path), cv2.cvtColor(self, conversion))
+def load_image(image_path: Path, conversion: int = cv2.COLOR_BGR2RGB) -> Image:
+    return cv2.cvtColor(cv2.imread(str(image_path), cv2.IMREAD_UNCHANGED), conversion)
+
+
+def save_image(image: Image, image_path: Path, conversion: int = cv2.COLOR_RGB2BGR):
+    image_path.parent.mkdir(exist_ok=True, parents=True)
+    cv2.imwrite(str(image_path), cv2.cvtColor(image, conversion))
 
 
 def load_images_in_directory(
     directory: Path, pattern: str = "*", conversion: int = cv2.COLOR_BGR2RGB
 ) -> Iterable[Image]:
     for image_path in directory.glob(pattern):
-        yield Image.from_path(image_path, conversion)
+        yield load_image(image_path, conversion)
diff --git a/common/polystar/common/models/image_annotation.py b/common/polystar/common/models/image_annotation.py
index b34e411..36707b9 100644
--- a/common/polystar/common/models/image_annotation.py
+++ b/common/polystar/common/models/image_annotation.py
@@ -6,7 +6,7 @@ from xml.dom.minidom import parseString
 
 import xmltodict
 from dicttoxml import dicttoxml
-from polystar.common.models.image import Image
+from polystar.common.models.image import Image, load_image, save_image
 from polystar.common.models.object import Object, ObjectFactory
 
 
@@ -28,7 +28,7 @@ class ImageAnnotation:
     @property
     def image(self) -> Image:
         if self._image is None:
-            self._image = Image.from_path(self.image_path)
+            self._image = load_image(self.image_path)
         return self._image
 
     @staticmethod
@@ -78,5 +78,5 @@ class ImageAnnotation:
         self.image_path.parent.mkdir(exist_ok=True, parents=True)
         self.xml_path.parent.mkdir(exist_ok=True, parents=True)
 
-        Image.save(self.image, self.image_path)
+        save_image(self.image, self.image_path)
         self.xml_path.write_text(self.to_xml())
diff --git a/common/research/common/dataset/perturbations/perturbator.py b/common/research/common/dataset/perturbations/perturbator.py
index 986aa8a..4391fe9 100644
--- a/common/research/common/dataset/perturbations/perturbator.py
+++ b/common/research/common/dataset/perturbations/perturbator.py
@@ -6,15 +6,21 @@ from typing import List
 import cv2
 import matplotlib.pyplot as plt
 import numpy as np
-
-from polystar.common.models.image import Image
-from research.common.dataset.perturbations.image_modifiers.brightness import BrightnessModifier
-from research.common.dataset.perturbations.image_modifiers.contrast import ContrastModifier
-from research.common.dataset.perturbations.image_modifiers.gaussian_blur import GaussianBlurrer
-from research.common.dataset.perturbations.image_modifiers.gaussian_noise import GaussianNoiser
-from research.common.dataset.perturbations.image_modifiers.horizontal_blur import HorizontalBlurrer
-from research.common.dataset.perturbations.image_modifiers.image_modifier_abc import ImageModifierABC
-from research.common.dataset.perturbations.image_modifiers.saturation import SaturationModifier
+from polystar.common.models.image import Image, load_image
+from research.common.dataset.perturbations.image_modifiers.brightness import \
+    BrightnessModifier
+from research.common.dataset.perturbations.image_modifiers.contrast import \
+    ContrastModifier
+from research.common.dataset.perturbations.image_modifiers.gaussian_blur import \
+    GaussianBlurrer
+from research.common.dataset.perturbations.image_modifiers.gaussian_noise import \
+    GaussianNoiser
+from research.common.dataset.perturbations.image_modifiers.horizontal_blur import \
+    HorizontalBlurrer
+from research.common.dataset.perturbations.image_modifiers.image_modifier_abc import \
+    ImageModifierABC
+from research.common.dataset.perturbations.image_modifiers.saturation import \
+    SaturationModifier
 
 
 @dataclass
@@ -37,7 +43,7 @@ class ImagePerturbator:
 
 if __name__ == "__main__":
     EXAMPLE_DIR = Path(__file__).parent / "examples"
-    rune_img = Image.from_path(EXAMPLE_DIR / "test.png")
+    rune_img = load_image(EXAMPLE_DIR / "test.png")
     perturbator = ImagePerturbator(
         [
             ContrastModifier(),
diff --git a/common/research/common/dataset/perturbations/utils.py b/common/research/common/dataset/perturbations/utils.py
index 551bc65..fb90ebe 100644
--- a/common/research/common/dataset/perturbations/utils.py
+++ b/common/research/common/dataset/perturbations/utils.py
@@ -3,15 +3,15 @@ from pathlib import Path
 import cv2
 import matplotlib.pyplot as plt
 import numpy as np
-
-from polystar.common.models.image import Image
-from research.common.dataset.perturbations.image_modifiers.image_modifier_abc import ImageModifierABC
+from polystar.common.models.image import load_image
+from research.common.dataset.perturbations.image_modifiers.image_modifier_abc import \
+    ImageModifierABC
 
 EXAMPLE_DIR = Path(__file__).parent / "examples"
 
 
 def simple_modifier_demo(modifier: ImageModifierABC):
-    rune_img = Image.from_path(EXAMPLE_DIR / "test.png")
+    rune_img = load_image(EXAMPLE_DIR / "test.png")
     rune_perturbed = modifier.modify(rune_img, 1)
     cv2.imwrite(str(EXAMPLE_DIR / f"res_{modifier}.png"), cv2.cvtColor(rune_perturbed, cv2.COLOR_RGB2BGR))
     side_by_side_display = np.hstack((rune_img, rune_perturbed))
diff --git a/common/research/common/dataset/roco_dataset.py b/common/research/common/dataset/roco_dataset.py
index b90eefc..7c8b9e6 100644
--- a/common/research/common/dataset/roco_dataset.py
+++ b/common/research/common/dataset/roco_dataset.py
@@ -3,8 +3,7 @@ from pathlib import Path
 from typing import Iterable
 
 from more_itertools import ilen
-
-from polystar.common.models.image import Image
+from polystar.common.models.image import Image, load_image
 from polystar.common.models.image_annotation import ImageAnnotation
 
 
@@ -17,7 +16,7 @@ class ROCODataset:
     @property
     def images(self) -> Iterable[Image]:
         for image_path in self.image_paths:
-            yield Image.from_path(image_path)
+            yield load_image(image_path)
 
     @property
     def image_annotations(self) -> Iterable[ImageAnnotation]:
diff --git a/common/research/common/datasets/image_dataset.py b/common/research/common/datasets/image_dataset.py
index 9ede632..0476336 100644
--- a/common/research/common/datasets/image_dataset.py
+++ b/common/research/common/datasets/image_dataset.py
@@ -25,7 +25,7 @@ class ImageFileDataset(LazyDataset[Path, TargetT], ABC):
         pass
 
     def open(self) -> ImageDataset:
-        return self.transform_examples(Image.from_path)
+        return self.transform_examples(load_image)
 
     def __len__(self):
         return ilen(self.image_files)
diff --git a/common/research/common/datasets/roco/directory_roco_dataset.py b/common/research/common/datasets/roco/directory_roco_dataset.py
index 9857bea..5df4021 100644
--- a/common/research/common/datasets/roco/directory_roco_dataset.py
+++ b/common/research/common/datasets/roco/directory_roco_dataset.py
@@ -1,6 +1,6 @@
 from pathlib import Path
 
-from polystar.common.models.image import Image
+from polystar.common.models.image import Image, save_image
 from research.common.datasets.image_dataset import ImageDirectoryDataset
 from research.common.datasets.roco.roco_annotation import ROCOAnnotation
 
@@ -21,5 +21,5 @@ class DirectoryROCODataset(ImageDirectoryDataset[ROCOAnnotation]):
         self.annotations_dir.mkdir()
 
     def add(self, image: Image, annotation: ROCOAnnotation):
-        Image.save(image, self.images_dir / f"{annotation.name}.jpg")
+        save_image(image, self.images_dir / f"{annotation.name}.jpg")
         (self.annotations_dir / f"{annotation.name}.xml").write_text(annotation.to_xml())
diff --git a/common/research/common/image_pipeline_evaluation/image_dataset_generator.py b/common/research/common/image_pipeline_evaluation/image_dataset_generator.py
index 862b049..f9b321b 100644
--- a/common/research/common/image_pipeline_evaluation/image_dataset_generator.py
+++ b/common/research/common/image_pipeline_evaluation/image_dataset_generator.py
@@ -2,7 +2,7 @@ from abc import abstractmethod
 from pathlib import Path
 from typing import Generic, Iterable, List, Tuple, TypeVar
 
-from polystar.common.models.image import Image
+from polystar.common.models.image import Image, load_image
 from research.common.dataset.directory_roco_dataset import DirectoryROCODataset
 
 T = TypeVar("T")
@@ -17,7 +17,7 @@ class ImageDatasetGenerator(Generic[T]):
             prev_total_size = len(images)
             for img_path, label in self.from_roco_dataset(dataset):
                 images_path.append(img_path)
-                images.append(Image.from_path(img_path))
+                images.append(load_image(img_path))
                 labels.append(label)
             dataset_sizes.append(len(images) - prev_total_size)
         return images_path, images, labels, dataset_sizes
diff --git a/common/tests/common/unittests/datasets/roco/test_directory_dataset_zoo.py b/common/tests/common/unittests/datasets/roco/test_directory_dataset_zoo.py
index 69ad224..fbb3707 100644
--- a/common/tests/common/unittests/datasets/roco/test_directory_dataset_zoo.py
+++ b/common/tests/common/unittests/datasets/roco/test_directory_dataset_zoo.py
@@ -4,7 +4,7 @@ from unittest import TestCase
 
 from numpy import asarray, float32
 from numpy.testing import assert_array_almost_equal
-from polystar.common.models.image import Image
+from polystar.common.models.image import save_image
 from research.common.datasets.roco.directory_roco_dataset import \
     DirectoryROCODataset
 from research.common.datasets.roco.roco_annotation import ROCOAnnotation
@@ -35,7 +35,7 @@ class TestDirectoryROCODataset(TestCase):
             dataset.annotations_dir.mkdir()
             dataset.images_dir.mkdir()
             (dataset.annotations_dir / "frame_1.xml").write_text(annotation.to_xml())
-            Image.save(image, dataset.images_dir / "frame_1.jpg")
+            save_image(image, dataset.images_dir / "frame_1.jpg")
 
             image_dataset = dataset.open()
 
diff --git a/robots-at-runes/research/robots_at_runes/dataset/labeled_image.py b/robots-at-runes/research/robots_at_runes/dataset/labeled_image.py
index bc49b1c..e6894d1 100644
--- a/robots-at-runes/research/robots_at_runes/dataset/labeled_image.py
+++ b/robots-at-runes/research/robots_at_runes/dataset/labeled_image.py
@@ -4,10 +4,9 @@ from typing import Any, Dict, Iterable, List
 from xml.dom.minidom import parseString
 
 import cv2
-
 import xmltodict
 from dicttoxml import dicttoxml
-from polystar.common.models.image import Image
+from polystar.common.models.image import Image, load_image, save_image
 
 
 @dataclass
@@ -35,7 +34,7 @@ class LabeledImage:
     point_of_interests: List[PointOfInterest] = field(default_factory=list)
 
     def save(self, directory_path: Path, name: str):
-        Image.save(self.image, directory_path / "image" / f"{name}.jpg")
+        save_image(self.image, directory_path / "image" / f"{name}.jpg")
         self._save_annotation(directory_path / "image_annotation" / f"{name}.xml")
 
     def _save_annotation(self, annotation_path: Path):
@@ -57,7 +56,7 @@ class LabeledImage:
         directory: Path, name: str, conversion: int = cv2.COLOR_BGR2RGB, ext: str = "jpg"
     ) -> "LabeledImage":
         return LabeledImage(
-            image=Image.from_path(directory / "image" / f"{name}.{ext}", conversion),
+            image=load_image(directory / "image" / f"{name}.{ext}", conversion),
             point_of_interests=PointOfInterest.from_annotation_file(directory / "image_annotation" / f"{name}.xml"),
         )
 
-- 
GitLab