Explorar o código

Solved merge conflicts

Dimitri Korsch %!s(int64=6) %!d(string=hai) anos
pai
achega
346c69f711
Modificáronse 4 ficheiros con 88 adicións e 57 borrados
  1. 4 7
      nabirds/dataset/image.py
  2. 50 26
      nabirds/dataset/part.py
  3. 14 9
      nabirds/dataset/utils.py
  4. 20 15
      scripts/display_from_info.py

+ 4 - 7
nabirds/dataset/image.py

@@ -97,10 +97,9 @@ class ImageWrapper(object):
 	def crop(self, x, y, w, h):
 		result = self.copy()
 		# result.im = self.im[y:y+h, x:x+w]
-		result.im = self.im.crop(x, y, x+w, y+h)
+		result.im = self.im.crop((x, y, x+w, y+h))
 		if self.has_parts:
-			result.parts[:, 1] -= x
-			result.parts[:, 2] -= y
+			result.parts.offset(-x, -y)
 		return result
 
 	@should_have_parts
@@ -108,10 +107,8 @@ class ImageWrapper(object):
 		idxs, (xs,ys) = self.visible_part_locs()
 		f = np.logical_and
 		mask = f(f(x <= xs, xs <= x+w), f(y <= ys, ys <= y+h))
-		idxs = np.where(mask)
-		import pdb; pdb.set_trace()
 		result = self.copy()
-		result.parts.select(idxs)
+		result.parts.select(idxs[mask])
 		return result
 
 	def uniform_parts(self, ratio):
@@ -134,7 +131,7 @@ class ImageWrapper(object):
 
 	@should_have_parts
 	def visible_crops(self, ratio):
-		return self.parts.visible_crops(ratio=ratio)
+		return self.parts.visible_crops(self.im, ratio=ratio)
 
 	@should_have_parts
 	def visible_part_locs(self):

+ 50 - 26
nabirds/dataset/part.py

@@ -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)

+ 14 - 9
nabirds/dataset/utils.py

@@ -72,20 +72,25 @@ def visible_part_locs(p):
 	# idxs, locs, vis = __expand_parts(p)
 	# return idxs[vis], locs[vis].T
 
-def crop(im, xy, ratio=DEFAULT_RATIO, padding_mode="edge"):
-	h, w, c = dimensions(im)
-	crop_h, crop_w = int(h * ratio), int(w * ratio)
+def crop(im, xy, w, h, padding_mode="edge", is_location=True):
 
-	pad_h, pad_w = crop_h // 2, crop_w // 2
+	x, y = 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
 
-	x0, y0 = xy[0] - crop_w // 2 + pad_w, xy[1] - crop_h // 2 + pad_h
-	return padded_im[y0:y0+crop_h, x0:x0+crop_w]
+	return padded_im[y0:y0+h, x0:x0+w]
 
-def crops(im, xy, ratio=DEFAULT_RATIO, padding_mode="edge"):
-	return np.stack([crop(im, x, y, ratio, padding_mode)
-		for (x,y) in xy.T])
+def crops(im, xys, ratio=DEFAULT_RATIO, padding_mode="edge"):
+
+	h, w, c = dimensions(im)
+	crop_h, crop_w = int(h * ratio), int(w * ratio)
+	return np.stack([crop(im, xy, crop_w, crop_h, padding_mode)
+		for xy in xys.T])
 
 def visible_crops(im, p, *args, **kw):
 	res = p.visible_crops(*args, **kw)

+ 20 - 15
scripts/display_from_info.py

@@ -12,6 +12,7 @@ import yaml
 import logging
 import numpy as np
 import matplotlib.pyplot as plt
+from matplotlib.patches import Rectangle
 
 from argparse import ArgumentParser
 
@@ -80,23 +81,27 @@ def main(args):
 		im, parts, label = data[i]
 
 		idxs, xy = parts.visible_locs()
-		part_crops = parts.visible_crops(ratio=data.ratio)
+		part_crops = parts.visible_crops(im, ratio=data.ratio)
 		if args.rnd:
 			parts.invert_selection()
-			action_crops = parts.visible_crops(ratio=data.ratio)
-
-		fig1 = plt.figure(figsize=(16,9))
-		ax = fig1.add_subplot(2,1,1)
-		ax.imshow(im)
-		ax.set_title("Visible Parts")
-		ax.scatter(*xy, marker="x", c=idxs)
-		ax.axis("off")
-
-		ax = fig1.add_subplot(2,1,2)
-		ax.set_title("{}selected parts".format("randomly " if args.rnd else ""))
-		ax.imshow(utils.reveal_parts(im, xy, ratio=data.ratio))
-		# ax.scatter(*xy, marker="x", c=idxs)
-		ax.axis("off")
+			action_crops = parts.visible_crops(im, ratio=data.ratio)
+
+		fig1, axs = plt.subplots(2, 1, figsize=(16,9))
+		axs[0].axis("off")
+		axs[0].set_title("Visible Parts")
+		axs[0].imshow(im)
+		if not args.crop_to_bb:
+			x, y, w, h = data.bounding_box(i)
+			axs[0].add_patch(Rectangle(
+				(x,y), w, h,
+				fill=False,
+				linestyle="--"
+			))
+		axs[0].scatter(*xy, marker="x", c=idxs)
+
+		axs[1].axis("off")
+		axs[1].set_title("{}selected parts".format("randomly " if args.rnd else ""))
+		axs[1].imshow(utils.reveal_parts(im, xy, ratio=data.ratio))
 		crop_names = list(data._annot.part_names.values())
 		plot_crops(part_crops, "Selected parts", names=crop_names)