|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""Utility functions for segmentations.""" |
|
from __future__ import absolute_import |
|
from __future__ import division |
|
from __future__ import print_function |
|
|
|
import math |
|
import numpy as np |
|
import cv2 |
|
|
|
|
|
def paste_instance_masks(masks, |
|
detected_boxes, |
|
image_height, |
|
image_width): |
|
"""Paste instance masks to generate the image segmentation results. |
|
|
|
Args: |
|
masks: a numpy array of shape [N, mask_height, mask_width] representing the |
|
instance masks w.r.t. the `detected_boxes`. |
|
detected_boxes: a numpy array of shape [N, 4] representing the reference |
|
bounding boxes. |
|
image_height: an integer representing the height of the image. |
|
image_width: an integer representing the width of the image. |
|
|
|
Returns: |
|
segms: a numpy array of shape [N, image_height, image_width] representing |
|
the instance masks *pasted* on the image canvas. |
|
""" |
|
|
|
def expand_boxes(boxes, scale): |
|
"""Expands an array of boxes by a given scale.""" |
|
|
|
|
|
|
|
w_half = boxes[:, 2] * .5 |
|
h_half = boxes[:, 3] * .5 |
|
x_c = boxes[:, 0] + w_half |
|
y_c = boxes[:, 1] + h_half |
|
|
|
w_half *= scale |
|
h_half *= scale |
|
|
|
boxes_exp = np.zeros(boxes.shape) |
|
boxes_exp[:, 0] = x_c - w_half |
|
boxes_exp[:, 2] = x_c + w_half |
|
boxes_exp[:, 1] = y_c - h_half |
|
boxes_exp[:, 3] = y_c + h_half |
|
|
|
return boxes_exp |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_, mask_height, mask_width = masks.shape |
|
scale = max((mask_width + 2.0) / mask_width, |
|
(mask_height + 2.0) / mask_height) |
|
|
|
ref_boxes = expand_boxes(detected_boxes, scale) |
|
ref_boxes = ref_boxes.astype(np.int32) |
|
padded_mask = np.zeros((mask_height + 2, mask_width + 2), dtype=np.float32) |
|
segms = [] |
|
for mask_ind, mask in enumerate(masks): |
|
im_mask = np.zeros((image_height, image_width), dtype=np.uint8) |
|
|
|
padded_mask[1:-1, 1:-1] = mask[:, :] |
|
|
|
ref_box = ref_boxes[mask_ind, :] |
|
w = ref_box[2] - ref_box[0] + 1 |
|
h = ref_box[3] - ref_box[1] + 1 |
|
w = np.maximum(w, 1) |
|
h = np.maximum(h, 1) |
|
|
|
mask = cv2.resize(padded_mask, (w, h)) |
|
mask = np.array(mask > 0.5, dtype=np.uint8) |
|
|
|
x_0 = min(max(ref_box[0], 0), image_width) |
|
x_1 = min(max(ref_box[2] + 1, 0), image_width) |
|
y_0 = min(max(ref_box[1], 0), image_height) |
|
y_1 = min(max(ref_box[3] + 1, 0), image_height) |
|
|
|
im_mask[y_0:y_1, x_0:x_1] = mask[ |
|
(y_0 - ref_box[1]):(y_1 - ref_box[1]), |
|
(x_0 - ref_box[0]):(x_1 - ref_box[0]) |
|
] |
|
segms.append(im_mask) |
|
|
|
segms = np.array(segms) |
|
assert masks.shape[0] == segms.shape[0] |
|
return segms |
|
|
|
|
|
def paste_instance_masks_v2(masks, |
|
detected_boxes, |
|
image_height, |
|
image_width): |
|
"""Paste instance masks to generate the image segmentation (v2). |
|
|
|
Args: |
|
masks: a numpy array of shape [N, mask_height, mask_width] representing the |
|
instance masks w.r.t. the `detected_boxes`. |
|
detected_boxes: a numpy array of shape [N, 4] representing the reference |
|
bounding boxes. |
|
image_height: an integer representing the height of the image. |
|
image_width: an integer representing the width of the image. |
|
|
|
Returns: |
|
segms: a numpy array of shape [N, image_height, image_width] representing |
|
the instance masks *pasted* on the image canvas. |
|
""" |
|
_, mask_height, mask_width = masks.shape |
|
|
|
segms = [] |
|
for i, mask in enumerate(masks): |
|
box = detected_boxes[i, :] |
|
xmin = box[0] |
|
ymin = box[1] |
|
xmax = xmin + box[2] |
|
ymax = ymin + box[3] |
|
|
|
|
|
|
|
|
|
xmin_int = int(math.floor(xmin)) |
|
xmax_int = int(math.ceil(xmax)) |
|
ymin_int = int(math.floor(ymin)) |
|
ymax_int = int(math.ceil(ymax)) |
|
|
|
alpha = box[2] / (1.0 * mask_width) |
|
beta = box[3] / (1.0 * mask_height) |
|
|
|
|
|
M_mask_to_image = np.array( |
|
[[alpha, 0, xmin], |
|
[0, beta, ymin], |
|
[0, 0, 1]], |
|
dtype=np.float32) |
|
|
|
M_image_to_crop = np.array( |
|
[[1, 0, -xmin_int], |
|
[0, 1, -ymin_int], |
|
[0, 0, 1]], |
|
dtype=np.float32) |
|
M = np.dot(M_image_to_crop, M_mask_to_image) |
|
|
|
|
|
|
|
M = np.dot( |
|
np.dot( |
|
np.array([[1, 0, -0.5], |
|
[0, 1, -0.5], |
|
[0, 0, 1]], np.float32), |
|
M), |
|
np.array([[1, 0, 0.5], |
|
[0, 1, 0.5], |
|
[0, 0, 1]], np.float32)) |
|
|
|
cropped_mask = cv2.warpPerspective( |
|
mask.astype(np.float32), M, |
|
(xmax_int - xmin_int, ymax_int - ymin_int)) |
|
cropped_mask = np.array(cropped_mask > 0.5, dtype=np.uint8) |
|
|
|
img_mask = np.zeros((image_height, image_width)) |
|
x0 = max(min(xmin_int, image_width), 0) |
|
x1 = max(min(xmax_int, image_width), 0) |
|
y0 = max(min(ymin_int, image_height), 0) |
|
y1 = max(min(ymax_int, image_height), 0) |
|
img_mask[y0:y1, x0:x1] = cropped_mask[ |
|
(y0 - ymin_int):(y1 - ymin_int), |
|
(x0 - xmin_int):(x1 - xmin_int)] |
|
|
|
segms.append(img_mask) |
|
|
|
segms = np.array(segms) |
|
return segms |
|
|
|
|