|
@@ -1,4 +1,6 @@
|
|
|
import numpy as np
|
|
|
+from contextlib import contextmanager
|
|
|
+from abc import ABC, abstractmethod, abstractproperty
|
|
|
|
|
|
from . import utils
|
|
|
|
|
@@ -7,7 +9,7 @@ class Parts(object):
|
|
|
super(Parts, self).__init__()
|
|
|
annots = utils.rescale_parts(image, part_annotations, rescale_size)
|
|
|
|
|
|
- self._parts = [ImagePart(image, a) for a in annots]
|
|
|
+ self._parts = [BasePart.new(a) for a in annots]
|
|
|
self.rescale_size = rescale_size
|
|
|
|
|
|
def __getitem__(self, i):
|
|
@@ -29,12 +31,14 @@ class Parts(object):
|
|
|
for p in self._parts:
|
|
|
p.is_visible = p._id in idxs
|
|
|
|
|
|
+
|
|
|
def invert_selection(self):
|
|
|
self.select(np.logical_not(self.selected))
|
|
|
|
|
|
- def set_visibility(self, idxs, value):
|
|
|
- for p in self._parts[idxs]:
|
|
|
- p.is_visible = value
|
|
|
+ def offset(self, dx, dy):
|
|
|
+ for p in self._parts:
|
|
|
+ p.x += dx
|
|
|
+ p.y += dy
|
|
|
|
|
|
def visible_locs(self):
|
|
|
vis = [(p._id, p.xy) for p in self._parts if p.is_visible]
|
|
@@ -44,35 +48,29 @@ class Parts(object):
|
|
|
def visible_crops(self, *args, **kwargs):
|
|
|
return np.array([p.crop(*args, **kwargs) for p in self._parts])
|
|
|
|
|
|
-class ImagePart(object):
|
|
|
+class BasePart(ABC):
|
|
|
def __init__(self, image, annotation):
|
|
|
- super(ImagePart, self).__init__()
|
|
|
+ super(BasePart, self).__init__()
|
|
|
self.image = image
|
|
|
+ self.read_annotation(annotation)
|
|
|
|
|
|
+ @staticmethod
|
|
|
+ def new(image, annotation):
|
|
|
if len(annotation) == 4:
|
|
|
- # here x,y are the center of the part
|
|
|
- self._id, self.x, self.y, self._is_visible = annotation
|
|
|
- self.w, self.h = None, None
|
|
|
-
|
|
|
+ return LocationPart(image, annotation)
|
|
|
elif len(annotation) == 5:
|
|
|
- # here x,y are top left corner of the part
|
|
|
- self._id, self.x, self.y, self.w, self.h = annotation
|
|
|
- self._is_visible = True
|
|
|
-
|
|
|
+ return BBoxPart(image, annotation)
|
|
|
else:
|
|
|
- raise ValueError("Unknown annotation format: {}".format(annotation))
|
|
|
+ raise ValueError("Unknown part annotation format: {}".format(annotation))
|
|
|
|
|
|
- def crop(self, ratio=None, padding_mode="edge"):
|
|
|
- if not self.is_visible:
|
|
|
- h, w, c = utils.dimensions(self.image)
|
|
|
- crop_h, crop_w = int(h * ratio), int(w * ratio)
|
|
|
- return np.zeros((crop_h, crop_w, c), dtype=np.uint8)
|
|
|
- else:
|
|
|
- return utils.crop(self.image, self.xy, ratio, padding_mode)
|
|
|
+
|
|
|
+ @abstractmethod
|
|
|
+ def read_annotation(self, annotation):
|
|
|
+ raise NotImplementedError
|
|
|
|
|
|
@property
|
|
|
def is_visible(self):
|
|
|
- return bool(self._is_visible)
|
|
|
+ return self._is_visible
|
|
|
|
|
|
@is_visible.setter
|
|
|
def is_visible(self, value):
|
|
@@ -82,9 +80,35 @@ class ImagePart(object):
|
|
|
def xy(self):
|
|
|
return np.array([self.x, self.y])
|
|
|
|
|
|
- @property
|
|
|
- def wh(self):
|
|
|
- return np.array([self.w, self.h])
|
|
|
+ @abstractmethod
|
|
|
+ def crop(self, padding_mode="edge", *args, **kwargs):
|
|
|
+ raise NotImplementedError
|
|
|
+
|
|
|
+class LocationPart(BasePart):
|
|
|
+
|
|
|
+ def read_annotation(self, annotation):
|
|
|
+ # here x,y are the center of the part
|
|
|
+ self._id, self.x, self.y, self.is_visible = annotation
|
|
|
+ self._ratio = None
|
|
|
+
|
|
|
+ def crop(self, image, ratio=None, padding_mode="edge", *args, **kwargs):
|
|
|
+ ratio = ratio or self._ratio
|
|
|
+ _h, _w, c = utils.dimensions(image)
|
|
|
+ w, h = int(_w * ratio), int(_h * ratio)
|
|
|
+
|
|
|
+ if not self.is_visible:
|
|
|
+ return np.zeros((h, w, c), dtype=np.uint8)
|
|
|
+ else:
|
|
|
+ return utils.crop(image, self.xy, w, h,
|
|
|
+ padding_mode, is_location=True)
|
|
|
|
|
|
+class BBoxPart(BasePart):
|
|
|
|
|
|
+ def read_annotation(self, annotation):
|
|
|
+ # here x,y are top left corner of the part
|
|
|
+ self._id, self.x, self.y, self.w, self.h = annotation
|
|
|
+ self._is_visible = True
|
|
|
|
|
|
+ def crop(self, padding_mode="edge", *args, **kwargs):
|
|
|
+ return utils.crop(self.image, self.xy, self.w, self.h,
|
|
|
+ padding_mode, is_location=False)
|