123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- import numpy as np
- from matplotlib import pyplot as plt
- from contextlib import contextmanager
- from matplotlib.patches import Rectangle
- from abc import ABC, abstractmethod, abstractproperty
- from . import utils
- class Parts(object):
- def __init__(self, image, part_annotations, rescale_size):
- super(Parts, self).__init__()
- annots = utils.rescale_parts(image, part_annotations, rescale_size)
- self._parts = [BasePart.new(a) for a in annots]
- self.rescale_size = rescale_size
- def __getitem__(self, i):
- return self._parts[i]
- @property
- def selected(self):
- return np.array([p.is_visible for p in self._parts], dtype=bool)
- @property
- def selected_idxs(self):
- return np.where(self.selected)[0]
- def select(self, idxs):
- if isinstance(idxs, np.ndarray) and idxs.dtype == bool:
- # a mask is present, so convert it to indeces
- idxs = np.where(idxs)[0]
- for p in self._parts:
- p.is_visible = p._id in idxs
- def invert_selection(self):
- self.select(np.logical_not(self.selected))
- 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]
- idxs, xy = zip(*vis)
- return np.array(idxs), np.array(xy).T
- def visible_crops(self, *args, **kwargs):
- return np.array([p.crop(*args, **kwargs) for p in self._parts])
- def plot(self, cmap=plt.cm.jet, **kwargs):
- for i, p in enumerate(self._parts):
- p.plot(color=cmap(i/len(self._parts)), **kwargs)
- def reveal(self, im, ratio, *args, **kwargs):
- res = np.zeros_like(im)
- for part in self._parts:
- if not part.is_visible: continue
- x, y, crop = part.reveal(im, ratio=ratio, *args, **kwargs)
- h, w, _ = crop.shape
- res[y:y+h, x:x+w] = crop
- return res
- class BasePart(ABC):
- def __init__(self, annotation):
- super(BasePart, self).__init__()
- self.read_annotation(annotation)
- @staticmethod
- def new(annotation):
- if len(annotation) == 4:
- return LocationPart(annotation)
- elif len(annotation) == 5:
- return BBoxPart(annotation)
- else:
- raise ValueError("Unknown part annotation format: {}".format(annotation))
- @abstractmethod
- def read_annotation(self, annotation):
- raise NotImplementedError
- @property
- def is_visible(self):
- return self._is_visible
- @is_visible.setter
- def is_visible(self, value):
- self._is_visible = bool(value)
- @property
- def xy(self):
- return np.array([self.x, self.y])
- @abstractmethod
- def crop(self, *args, **kwargs):
- raise NotImplementedError
- def plot(self, **kwargs):
- return
- 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)
- def reveal(self, im, ratio, *args, **kwargs):
- _h, _w, c = utils.dimensions(im)
- w, h = int(_w * ratio), int(_h * ratio)
- x,y = self.xy
- x, y = max(x - w // 2, 0), max(y - h // 2, 0)
- return x, y, im[y:y+h, x:x+w]
- def plot(self, im, ax, ratio, fill=False, linestyle="--", **kwargs):
- if not self.is_visible: return
- x, y = self.xy
- _h, _w, c = utils.dimensions(im)
- w, h = int(_w * ratio), int(_h * ratio)
- ax.add_patch(Rectangle(
- (x-w//2, y-h//2), w, h,
- fill=fill, linestyle=linestyle,
- **kwargs
- ))
- 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, image, padding_mode="edge", *args, **kwargs):
- return utils.crop(image, self.xy, self.w, self.h,
- padding_mode, is_location=False)
- def reveal(self, im, ratio, *args, **kwargs):
- _h, _w, c = utils.dimensions(im)
- x,y = self.xy
- return x, y, im[y:y+self.h, x:x+self.w]
- def plot(self, im, ax, ratio, fill=False, linestyle="--", **kwargs):
- ax.add_patch(Rectangle(
- (self.x, self.y), self.w, self.h,
- fill=fill, linestyle=linestyle,
- **kwargs
- ))
|