Bladeren bron

moved some data utils functions to Part class

Dimitri Korsch 6 jaren geleden
bovenliggende
commit
4497325a58
4 gewijzigde bestanden met toevoegingen van 141 en 99 verwijderingen
  1. 1 2
      nabirds/dataset/image.py
  2. 53 22
      nabirds/dataset/part.py
  3. 81 70
      nabirds/dataset/utils.py
  4. 6 5
      scripts/display_from_info.py

+ 1 - 2
nabirds/dataset/image.py

@@ -139,9 +139,8 @@ class ImageWrapper(object):
 
 	@should_have_parts
 	def reveal_visible(self, ratio):
-		_, xy = self.visible_part_locs()
 		result = self.copy()
-		result.im = utils.reveal_parts(self.im, xy, ratio=ratio)
+		result.im = self.parts.reveal(self.im, ratio=ratio)
 		return result
 
 	@should_have_parts

+ 53 - 22
nabirds/dataset/part.py

@@ -8,12 +8,11 @@ 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
+		self._parts = [BasePart.new(image, a, rescale_size) for a in part_annotations]
 
 	def __getitem__(self, i):
 		return self._parts[i]
@@ -68,24 +67,31 @@ class Parts(object):
 
 
 class BasePart(ABC):
-	def __init__(self, annotation):
-		super(BasePart, self).__init__()
-		self.read_annotation(annotation)
 
 	@staticmethod
-	def new(annotation):
+	def new(image, annotation, rescale_size=-1):
 		if len(annotation) == 4:
-			return LocationPart(annotation)
+			return LocationPart(image, annotation, rescale_size)
 		elif len(annotation) == 5:
-			return BBoxPart(annotation)
+			return BBoxPart(image, annotation, rescale_size)
 		else:
 			raise ValueError("Unknown part annotation format: {}".format(annotation))
 
 
 	@abstractmethod
-	def read_annotation(self, annotation):
+	def __init__(self, *args, **kwargs):
 		raise NotImplementedError
 
+	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
@@ -98,20 +104,32 @@ class BasePart(ABC):
 	def xy(self):
 		return np.array([self.x, self.y])
 
-	@abstractmethod
-	def crop(self, *args, **kwargs):
-		raise NotImplementedError
+	def crop(self, im, w, h, padding_mode="edge", is_location=True):
+
+		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]
+
 
 	def plot(self, **kwargs):
 		return
 
-
 class LocationPart(BasePart):
+	DEFAULT_RATIO = np.sqrt(49 / 400) # 0.35
+
+	def __init__(self, image, annotation, rescale_size):
+		annotation = self.rescale(image, annotation, rescale_size)
 
-	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
+		self._ratio = LocationPart.DEFAULT_RATIO
 
 	def crop(self, image, ratio=None, padding_mode="edge", *args, **kwargs):
 		ratio = ratio or self._ratio
@@ -121,11 +139,10 @@ class LocationPart(BasePart):
 		if not self.is_visible:
 			return np.zeros((h, w, c), dtype=np.uint8)
 		else:
-			return utils.crop(image, self.xy, w, h,
+			return super(LocationPart, self).crop(image, w, h,
 				padding_mode, is_location=True)
 
-
-	def reveal(self, im, ratio, *args, **kwargs):
+	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
@@ -146,13 +163,27 @@ class LocationPart(BasePart):
 
 class BBoxPart(BasePart):
 
-	def read_annotation(self, annotation):
+	def __init__(self, image, annotation, rescale_size):
+
+		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
+		self.is_visible = True
+
+	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
+
 
 	def crop(self, image, padding_mode="edge", *args, **kwargs):
-		return utils.crop(image, self.xy, self.w, self.h,
+		return super(BBoxPart, self).crop(image, self.w, self.h,
 			padding_mode, is_location=False)
 
 	def reveal(self, im, ratio, *args, **kwargs):

+ 81 - 70
nabirds/dataset/utils.py

@@ -3,26 +3,6 @@ from PIL.Image import Image as PIL_Image
 
 DEFAULT_RATIO = np.sqrt(49 / 400)
 
-# def __expand_parts(p):
-# 	return p[:, 0], p[:, 1:3], p[:, 3].astype(bool)
-
-def rescale_parts(im, parts, part_rescale_size):
-	if part_rescale_size is None or part_rescale_size < 0:
-		return parts
-	h, w, c = dimensions(im)
-	scale = np.array([w, h]) / part_rescale_size
-
-	xy = parts[:, 1:3]
-	xy = xy * scale
-	parts[:, 1:3] = xy
-
-	if parts.shape[1] == 5:
-		wh = parts[:, 3:5]
-		wh = wh * scale
-		parts[:, 3:5] = wh
-
-	return parts
-
 def dimensions(im):
 	if isinstance(im, np.ndarray):
 		if im.ndim != 3:
@@ -65,55 +45,7 @@ def uniform_parts(im, ratio=DEFAULT_RATIO, round_op=np.floor):
 
 	return parts
 
-def visible_part_locs(p):
-	res = p.visible_locs()
-	return res
-
-	# idxs, locs, vis = __expand_parts(p)
-	# return idxs[vis], locs[vis].T
-
-def crop(im, xy, w, h, padding_mode="edge", is_location=True):
-
-	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
-
-	return padded_im[y0:y0+h, x0:x0+w]
-
-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)
-	return res
-
-	# idxs, locs, vis = __expand_parts(p)
-	# parts = crops(asarray(im), locs[vis].T, *args, **kw)
-	# res = np.zeros((len(idxs),) + parts.shape[1:], dtype=parts.dtype)
-	# res[vis] = parts
-	# return res
-
-def reveal_parts(im, xy, ratio=DEFAULT_RATIO):
-	h, w, c = dimensions(im)
-	crop_h, crop_w = int(h * ratio), int(w * ratio)
-	im = asarray(im)
-	res = np.zeros_like(im)
-	for x, y in xy.T:
-		x0, y0 = max(x - crop_w // 2, 0), max(y - crop_h // 2, 0)
-		res[y0:y0+crop_h, x0:x0+crop_w] = im[y0:y0+crop_h, x0:x0+crop_w]
-
-	return res
-
-def select(crops, mask):
+def select_crops(crops, mask):
 	selected = np.zeros_like(crops)
 	selected[mask] = crops[mask]
 	return selected
@@ -127,7 +59,7 @@ def random_select(idxs, xy, part_crops, *args, **kw):
 	xy = xy[:, rnd_idxs]
 
 	mask = selection_mask(idxs, len(part_crops))
-	selected_crops = select(part_crops, mask)
+	selected_crops = select_crops(part_crops, mask)
 
 	return idxs, xy, selected_crops
 
@@ -143,3 +75,82 @@ def random_idxs(idxs, rnd=None, n_parts=None):
 	res = rnd.choice(idxs, n_parts, replace=False)
 	res.sort()
 	return res
+
+
+
+##### DEPRECATED #####
+
+# def __expand_parts(p):
+# 	return p[:, 0], p[:, 1:3], p[:, 3].astype(bool)
+
+def rescale_parts(im, parts, part_rescale_size):
+	raise DeprecationWarning("Do not use me!")
+	# if part_rescale_size is None or part_rescale_size < 0:
+	# 	return parts
+	# h, w, c = dimensions(im)
+	# scale = np.array([w, h]) / part_rescale_size
+
+	# xy = parts[:, 1:3]
+	# xy = xy * scale
+	# parts[:, 1:3] = xy
+
+	# if parts.shape[1] == 5:
+	# 	wh = parts[:, 3:5]
+	# 	wh = wh * scale
+	# 	parts[:, 3:5] = wh
+
+	# return parts
+
+def visible_part_locs(p):
+	raise DeprecationWarning("Do not use me!")
+	# res = p.visible_locs()
+	# return res
+
+	# idxs, locs, vis = __expand_parts(p)
+	# return idxs[vis], locs[vis].T
+
+
+def crop(im, xy, w, h, padding_mode="edge", is_location=True):
+	raise DeprecationWarning("Do not use me!")
+
+	# 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
+
+	# return padded_im[y0:y0+h, x0:x0+w]
+
+def crops(im, xys, ratio=DEFAULT_RATIO, padding_mode="edge"):
+	raise DeprecationWarning("Do not use me!")
+
+	# 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):
+	raise DeprecationWarning("Do not use me!")
+	# res = p.visible_crops(*args, **kw)
+	# return res
+
+	# idxs, locs, vis = __expand_parts(p)
+	# parts = crops(asarray(im), locs[vis].T, *args, **kw)
+	# res = np.zeros((len(idxs),) + parts.shape[1:], dtype=parts.dtype)
+	# res[vis] = parts
+	# return res
+
+def reveal_parts(im, xy, ratio=DEFAULT_RATIO):
+	raise DeprecationWarning("Do not use me!")
+	# h, w, c = dimensions(im)
+	# crop_h, crop_w = int(h * ratio), int(w * ratio)
+	# im = asarray(im)
+	# res = np.zeros_like(im)
+	# for x, y in xy.T:
+	# 	x0, y0 = max(x - crop_w // 2, 0), max(y - crop_h // 2, 0)
+	# 	res[y0:y0+crop_h, x0:x0+crop_w] = im[y0:y0+crop_h, x0:x0+crop_w]
+
+	# return res

+ 6 - 5
scripts/display_from_info.py

@@ -78,11 +78,6 @@ def main(args):
 	for i in range(start, max(start, start + n_images)):
 		im, parts, label = data[i]
 
-		idxs, xy = parts.visible_locs()
-		part_crops = parts.visible_crops(im, ratio=data.ratio)
-		if args.rnd:
-			parts.invert_selection()
-			action_crops = parts.visible_crops(im, ratio=data.ratio)
 
 		fig1, axs = plt.subplots(2, 1, figsize=(16,9))
 		axs[0].axis("off")
@@ -96,6 +91,12 @@ def main(args):
 		axs[1].set_title("{}selected parts".format("randomly " if args.rnd else ""))
 		axs[1].imshow(parts.reveal(im, ratio=data.ratio))
 		crop_names = list(data._annot.part_names.values())
+
+		part_crops = parts.visible_crops(im, ratio=data.ratio)
+		if args.rnd:
+			parts.invert_selection()
+			action_crops = parts.visible_crops(im, ratio=data.ratio)
+
 		plot_crops(part_crops, "Selected parts", names=crop_names)
 
 		if args.rnd: