Bläddra i källkod

refactored part module

Dimitri Korsch 6 år sedan
förälder
incheckning
3a9500c302

+ 0 - 258
nabirds/dataset/part.py

@@ -1,258 +0,0 @@
-import numpy as np
-
-from contextlib import contextmanager
-from matplotlib import pyplot as plt
-from matplotlib.patches import Rectangle
-from abc import ABC, abstractproperty
-
-from nabirds import utils
-
-class BaseParts(ABC):
-
-	def __getitem__(self, i):
-		return self._parts[i]
-
-	def __repr__(self):
-		return repr(np.stack([p.as_annotation for p in self._parts]))
-
-	@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 hide_outside_bb(self, *bounding_box):
-		for p in self._parts:
-			p.hide_if_outside(*bounding_box)
-
-	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 Parts(BaseParts):
-
-	def __init__(self, image, part_annotations, rescale_size):
-		super(Parts, self).__init__()
-		self._parts = [BasePart.new(image, a, rescale_size) for a in part_annotations]
-
-
-class UniformParts(BaseParts):
-
-	def __init__(self, image, ratio):
-		super(UniformParts, self).__init__()
-		self._parts = list(self.generate_parts(image, ratio))
-
-	def generate_parts(self, im, ratio, round_op=np.floor):
-		h, w, c = utils.dimensions(im)
-
-		part_w = round_op(w * ratio).astype(np.int32)
-		part_h = round_op(h * ratio).astype(np.int32)
-
-		n, m = w // part_w, h // part_h
-
-		# fit best possible part_w and part_h
-		part_w = int(w / n)
-		part_h = int(h / m)
-
-		for i in range(n*m):
-			row, col = np.unravel_index(i, (n, m))
-			x, y = col * part_w, row * part_h
-
-			yield BBoxPart(im, [i, x, y, part_w, part_h])
-
-class BasePart(ABC):
-
-	def __repr__(self):
-		return repr(self.as_annotation)
-
-	@staticmethod
-	def new(image, annotation, rescale_size=-1):
-		if len(annotation) == 4:
-			return LocationPart(image, annotation, rescale_size)
-		elif len(annotation) == 5:
-			return BBoxPart(image, annotation, rescale_size)
-		else:
-			raise ValueError("Unknown part annotation format: {}".format(annotation))
-
-	def rescale(self, image, annotation, rescale_size):
-		if rescale_size is not None and rescale_size > 0:
-			h, w, c = utils.dimensions(image)
-			scale = np.array([w, h]) / rescale_size
-			xy = annotation[1:3]
-			xy = xy * scale
-			annotation[1:3] = xy
-
-		return annotation
-
-	@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])
-
-	def crop(self, im, w, h, padding_mode="edge", is_location=True):
-		if not self.is_visible:
-			_, _, c = utils.dimensions(im)
-			return np.zeros((h, w, c), dtype=np.uint8)
-
-		x, y = self.xy
-		pad_h, pad_w = h // 2, w // 2
-
-		padded_im = np.pad(im, [(pad_h, pad_h), (pad_w, pad_w), [0,0]], mode=padding_mode)
-		x0, y0 = x + pad_w, y + pad_h
-
-		if is_location:
-			x0, y0 = x0 - w // 2, y0 - h // 2
-
-		return padded_im[y0:y0+h, x0:x0+w]
-
-	@abstractproperty
-	def middle(self):
-		raise NotImplementedError
-
-	def plot(self, **kwargs):
-		return
-
-	def hide_if_outside(self, x, y, w, h):
-		mid_x, mid_y = self.middle
-		self.is_visible = ((x <= mid_x <= x+w) and (y <= mid_y <= y+h))
-
-class LocationPart(BasePart):
-	DEFAULT_RATIO = np.sqrt(49 / 400) # 0.35
-
-	def __init__(self, image, annotation, rescale_size=None):
-		super(LocationPart, self).__init__()
-
-		annotation = self.rescale(image, annotation, rescale_size)
-		# here x,y are the center of the part
-		self._id, self.x, self.y, self.is_visible = annotation
-		self._ratio = LocationPart.DEFAULT_RATIO
-
-	def as_annotation(self):
-		return np.array([self._id, self.x, self.y, self.is_visible])
-
-	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)
-		return super(LocationPart, self).crop(image, w, h,
-				padding_mode, is_location=True)
-
-	def reveal(self, im, ratio=None, *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
-		))
-
-		ax.scatter(*self.middle, marker="x", color="white", alpha=0.8)
-
-
-	@property
-	def middle(self):
-		return np.array([self.x, self.y])
-
-
-class BBoxPart(BasePart):
-
-	def __init__(self, image, annotation, rescale_size=None):
-		super(BBoxPart, self).__init__()
-
-		annotation = self.rescale(image, annotation, rescale_size)
-		# 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
-
-	@property
-	def as_annotation(self):
-		return np.array([self._id, self.x, self.y, self.w, self.h])
-
-	def rescale(self, image, annotation, rescale_size):
-		if rescale_size is not None and rescale_size > 0:
-			annotation = super(BBoxPart, self).rescale(image, annotation, rescale_size)
-			h, w, c = utils.dimensions(image)
-			scale = np.array([w, h]) / rescale_size
-			wh = annotation[3:5]
-			wh = wh * scale
-			annotation[3:5] = wh
-
-		return annotation
-
-	@property
-	def middle(self):
-		return np.array([self.x + self.w // 2, self.y + self.h // 2])
-
-	def crop(self, image, padding_mode="edge", *args, **kwargs):
-		return super(BBoxPart, self).crop(image, 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
-		))
-
-		ax.scatter(*self.middle, marker="x", color="white", alpha=0.8)
-

+ 1 - 0
nabirds/dataset/part/__init__.py

@@ -0,0 +1 @@
+from .collection import Parts, UniformParts

+ 102 - 0
nabirds/dataset/part/annotation.py

@@ -0,0 +1,102 @@
+import numpy as np
+
+from matplotlib.patches import Rectangle
+
+from nabirds import utils
+from .base import BasePart
+
+class LocationPart(BasePart):
+	DEFAULT_RATIO = np.sqrt(49 / 400) # 0.35
+
+	def __init__(self, image, annotation, rescale_size=None):
+		super(LocationPart, self).__init__()
+
+		annotation = self.rescale(image, annotation, rescale_size)
+		# here x,y are the center of the part
+		self._id, self.x, self.y, self.is_visible = annotation
+		self._ratio = LocationPart.DEFAULT_RATIO
+
+	def as_annotation(self):
+		return np.array([self._id, self.x, self.y, self.is_visible])
+
+	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)
+		return super(LocationPart, self).crop(image, w, h,
+				padding_mode, is_location=True)
+
+	def reveal(self, im, ratio=None, *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
+		))
+
+		ax.scatter(*self.middle, marker="x", color="white", alpha=0.8)
+
+
+	@property
+	def middle(self):
+		return np.array([self.x, self.y])
+
+class BBoxPart(BasePart):
+
+	def __init__(self, image, annotation, rescale_size=None):
+		super(BBoxPart, self).__init__()
+
+		annotation = self.rescale(image, annotation, rescale_size)
+		# 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
+
+	@property
+	def as_annotation(self):
+		return np.array([self._id, self.x, self.y, self.w, self.h])
+
+	def rescale(self, image, annotation, rescale_size):
+		if rescale_size is not None and rescale_size > 0:
+			annotation = super(BBoxPart, self).rescale(image, annotation, rescale_size)
+			h, w, c = utils.dimensions(image)
+			scale = np.array([w, h]) / rescale_size
+			wh = annotation[3:5]
+			wh = wh * scale
+			annotation[3:5] = wh
+
+		return annotation
+
+	@property
+	def middle(self):
+		return np.array([self.x + self.w // 2, self.y + self.h // 2])
+
+	def crop(self, image, padding_mode="edge", *args, **kwargs):
+		return super(BBoxPart, self).crop(image, 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
+		))
+
+		ax.scatter(*self.middle, marker="x", color="white", alpha=0.8)
+

+ 132 - 0
nabirds/dataset/part/base.py

@@ -0,0 +1,132 @@
+import numpy as np
+
+from matplotlib import pyplot as plt
+from abc import ABC, abstractproperty
+
+from nabirds import utils
+
+class BasePartCollection(ABC):
+
+	def __getitem__(self, i):
+		return self._parts[i]
+
+	def __repr__(self):
+		return repr(np.stack([p.as_annotation for p in self._parts]))
+
+	@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 hide_outside_bb(self, *bounding_box):
+		for p in self._parts:
+			p.hide_if_outside(*bounding_box)
+
+	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 __repr__(self):
+		return repr(self.as_annotation)
+
+	@staticmethod
+	def new(image, annotation, rescale_size=-1):
+		from .annotation import BBoxPart, LocationPart
+		if len(annotation) == 4:
+			return LocationPart(image, annotation, rescale_size)
+		elif len(annotation) == 5:
+			return BBoxPart(image, annotation, rescale_size)
+		else:
+			raise ValueError("Unknown part annotation format: {}".format(annotation))
+
+	def rescale(self, image, annotation, rescale_size):
+		if rescale_size is not None and rescale_size > 0:
+			h, w, c = utils.dimensions(image)
+			scale = np.array([w, h]) / rescale_size
+			xy = annotation[1:3]
+			xy = xy * scale
+			annotation[1:3] = xy
+
+		return annotation
+
+	@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])
+
+	def crop(self, im, w, h, padding_mode="edge", is_location=True):
+		if not self.is_visible:
+			_, _, c = utils.dimensions(im)
+			return np.zeros((h, w, c), dtype=np.uint8)
+
+		x, y = self.xy
+		pad_h, pad_w = h // 2, w // 2
+
+		padded_im = np.pad(im, [(pad_h, pad_h), (pad_w, pad_w), [0,0]], mode=padding_mode)
+		x0, y0 = x + pad_w, y + pad_h
+
+		if is_location:
+			x0, y0 = x0 - w // 2, y0 - h // 2
+
+		return padded_im[y0:y0+h, x0:x0+w]
+
+	@abstractproperty
+	def middle(self):
+		raise NotImplementedError
+
+	def plot(self, **kwargs):
+		return
+
+	def hide_if_outside(self, x, y, w, h):
+		mid_x, mid_y = self.middle
+		self.is_visible = ((x <= mid_x <= x+w) and (y <= mid_y <= y+h))

+ 37 - 0
nabirds/dataset/part/collection.py

@@ -0,0 +1,37 @@
+import numpy as np
+
+from nabirds import utils
+
+from .base import BasePartCollection, BasePart
+from .annotation import BBoxPart
+
+class Parts(BasePartCollection):
+
+	def __init__(self, image, part_annotations, rescale_size):
+		super(Parts, self).__init__()
+		self._parts = [BasePart.new(image, a, rescale_size) for a in part_annotations]
+
+
+class UniformParts(BasePartCollection):
+
+	def __init__(self, image, ratio):
+		super(UniformParts, self).__init__()
+		self._parts = list(self.generate_parts(image, ratio))
+
+	def generate_parts(self, im, ratio, round_op=np.floor):
+		h, w, c = utils.dimensions(im)
+
+		part_w = round_op(w * ratio).astype(np.int32)
+		part_h = round_op(h * ratio).astype(np.int32)
+
+		n, m = w // part_w, h // part_h
+
+		# fit best possible part_w and part_h
+		part_w = int(w / n)
+		part_h = int(h / m)
+
+		for i in range(n*m):
+			row, col = np.unravel_index(i, (n, m))
+			x, y = col * part_w, row * part_h
+
+			yield BBoxPart(im, [i, x, y, part_w, part_h])