|
import math |
|
from io import BytesIO |
|
import cv2 |
|
import matplotlib.pyplot as plt |
|
import numpy as np |
|
import skimage.draw |
|
from PIL import Image |
|
|
|
|
|
def get_image_size(filepath): |
|
im = Image.open(filepath) |
|
return im.size |
|
|
|
|
|
def load_image(image_filepath): |
|
image = Image.open(image_filepath) |
|
image.load() |
|
image_array = np.array(image, dtype=np.uint8) |
|
image.close() |
|
return image_array |
|
|
|
|
|
def padded_boundingbox(boundingbox, padding): |
|
boundingbox_new = np.empty_like(boundingbox) |
|
boundingbox_new[0:2] = boundingbox[0:2] + padding |
|
boundingbox_new[2:4] = boundingbox[2:4] - padding |
|
return boundingbox_new |
|
|
|
|
|
def center_bbox(spatial_shape, output_shape): |
|
""" |
|
Return a bbox centered in spatial_shape with size output_shape |
|
|
|
:param spatial_shape: |
|
:param output_shape: |
|
:return: |
|
""" |
|
center = (spatial_shape[0] / 2, spatial_shape[1] / 2) |
|
half_output_shape = (output_shape[0] / 2, output_shape[1] / 2) |
|
bbox = [center[0] - half_output_shape[0], center[1] - half_output_shape[1], center[0] + half_output_shape[0], center[1] + half_output_shape[1]] |
|
bbox = bbox_to_int(bbox) |
|
return bbox |
|
|
|
|
|
def bbox_add_margin(bbox, margin): |
|
bbox_new = bbox.copy() |
|
bbox_new[0:2] -= margin |
|
bbox_new[2:4] += margin |
|
return bbox_new |
|
|
|
|
|
def bbox_to_int(bbox): |
|
bbox_new = [ |
|
int(np.floor(bbox[0])), |
|
int(np.floor(bbox[1])), |
|
int(np.ceil(bbox[2])), |
|
int(np.ceil(bbox[3])), |
|
] |
|
return bbox_new |
|
|
|
|
|
def draw_line_aa_in_patch(edge, patch_bounds): |
|
rr, cc, prob = skimage.draw.line_aa(edge[0][0], edge[0][1], edge[1][0], edge[1][1]) |
|
keep_mask = (patch_bounds[0] <= rr) & (rr < patch_bounds[2]) \ |
|
& (patch_bounds[1] <= cc) & (cc < patch_bounds[3]) |
|
rr = rr[keep_mask] |
|
cc = cc[keep_mask] |
|
prob = prob[keep_mask] |
|
return rr, cc, prob |
|
|
|
|
|
def convert_array_to_jpg_bytes(image_array, mode=None): |
|
img = Image.fromarray(image_array, mode=mode) |
|
output = BytesIO() |
|
img.save(output, format="JPEG", quality=90) |
|
contents = output.getvalue() |
|
output.close() |
|
return contents |
|
|
|
|
|
def displacement_map_to_transformation_maps(disp_field_map): |
|
disp_field_map = disp_field_map.astype(np.float32) |
|
i = np.arange(disp_field_map.shape[0], dtype=np.float32) |
|
j = np.arange(disp_field_map.shape[1], dtype=np.float32) |
|
iv, jv = np.meshgrid(i, j, indexing="ij") |
|
reverse_map_i = iv + disp_field_map[:, :, 1] |
|
reverse_map_j = jv + disp_field_map[:, :, 0] |
|
return reverse_map_i, reverse_map_j |
|
|
|
def apply_displacement_field_to_image(image, disp_field_map): |
|
trans_map_i, trans_map_j = displacement_map_to_transformation_maps(disp_field_map) |
|
misaligned_image = cv2.remap(image, trans_map_j, trans_map_i, cv2.INTER_CUBIC) |
|
return misaligned_image |
|
|
|
|
|
def apply_displacement_fields_to_image(image, disp_field_maps): |
|
disp_field_map_count = disp_field_maps.shape[0] |
|
misaligned_image_list = [] |
|
for i in range(disp_field_map_count): |
|
misaligned_image = apply_displacement_field_to_image(image, disp_field_maps[i, :, :, :]) |
|
misaligned_image_list.append(misaligned_image) |
|
return misaligned_image_list |
|
|
|
|
|
def get_axis_patch_count(length, stride, patch_res): |
|
total_double_padding = patch_res - stride |
|
patch_count = max(1, int(math.ceil((length - total_double_padding) / stride))) |
|
return patch_count |
|
|
|
|
|
def compute_patch_boundingboxes(image_size, stride, patch_res): |
|
""" |
|
|
|
@param image_size: |
|
@param stride: |
|
@param patch_res: |
|
@return: [[row_start, col_start, row_end, col_end], ...] |
|
""" |
|
im_rows = image_size[0] |
|
im_cols = image_size[1] |
|
|
|
row_patch_count = get_axis_patch_count(im_rows, stride, patch_res) |
|
col_patch_count = get_axis_patch_count(im_cols, stride, patch_res) |
|
|
|
patch_boundingboxes = [] |
|
for i in range(0, row_patch_count): |
|
if i < row_patch_count - 1: |
|
row_slice_begin = i * stride |
|
row_slice_end = row_slice_begin + patch_res |
|
else: |
|
row_slice_end = im_rows |
|
row_slice_begin = row_slice_end - patch_res |
|
for j in range(0, col_patch_count): |
|
if j < col_patch_count - 1: |
|
col_slice_begin = j*stride |
|
col_slice_end = col_slice_begin + patch_res |
|
else: |
|
col_slice_end = im_cols |
|
col_slice_begin = col_slice_end - patch_res |
|
|
|
patch_boundingbox = np.array([row_slice_begin, col_slice_begin, row_slice_end, col_slice_end], dtype=np.int32) |
|
assert row_slice_end - row_slice_begin == col_slice_end - col_slice_begin == patch_res, "ERROR: patch does not have the requested shape" |
|
patch_boundingboxes.append(patch_boundingbox) |
|
|
|
return patch_boundingboxes |
|
|
|
|
|
def clip_boundingbox(boundingbox, clip_list): |
|
assert len(boundingbox) == len(clip_list), "len(boundingbox) should be equal to len(clip_values)" |
|
clipped_boundingbox = [] |
|
for bb_value, clip in zip(boundingbox[:2], clip_list[:2]): |
|
clipped_value = max(clip, bb_value) |
|
clipped_boundingbox.append(clipped_value) |
|
for bb_value, clip in zip(boundingbox[2:], clip_list[2:]): |
|
clipped_value = min(clip, bb_value) |
|
clipped_boundingbox.append(clipped_value) |
|
return clipped_boundingbox |
|
|
|
|
|
def crop_or_pad_image_with_boundingbox(image, patch_boundingbox): |
|
im_rows = image.shape[0] |
|
im_cols = image.shape[1] |
|
|
|
row_padding_before = max(0, - patch_boundingbox[0]) |
|
col_padding_before = max(0, - patch_boundingbox[1]) |
|
row_padding_after = max(0, patch_boundingbox[2] - im_rows) |
|
col_padding_after = max(0, patch_boundingbox[3] - im_cols) |
|
|
|
|
|
row_padding = row_padding_before + row_padding_after |
|
col_padding = col_padding_before + col_padding_after |
|
row_padding_before = row_padding // 2 |
|
col_padding_before = col_padding // 2 |
|
row_padding_after = row_padding - row_padding // 2 |
|
col_padding_after = col_padding - col_padding // 2 |
|
|
|
clipped_patch_boundingbox = clip_boundingbox(patch_boundingbox, [0, 0, im_rows, im_cols]) |
|
|
|
if len(image.shape) == 2: |
|
patch = image[clipped_patch_boundingbox[0]:clipped_patch_boundingbox[2], clipped_patch_boundingbox[1]:clipped_patch_boundingbox[3]] |
|
patch = np.pad(patch, [(row_padding_before, row_padding_after), (col_padding_before, col_padding_after)], mode="constant") |
|
elif len(image.shape) == 3: |
|
patch = image[clipped_patch_boundingbox[0]:clipped_patch_boundingbox[2], clipped_patch_boundingbox[1]:clipped_patch_boundingbox[3], :] |
|
patch = np.pad(patch, [(row_padding_before, row_padding_after), (col_padding_before, col_padding_after), (0, 0)], mode="constant") |
|
else: |
|
print("Image input does not have the right shape/") |
|
patch = None |
|
return patch |
|
|
|
|
|
def make_grid(images, padding=2, pad_value=0, return_offsets=False): |
|
nmaps = images.shape[0] |
|
ymaps = int(math.floor(math.sqrt(nmaps))) |
|
xmaps = nmaps // ymaps |
|
height, width = int(images.shape[1] + padding), int(images.shape[2] + padding) |
|
grid = np.zeros((height * ymaps + padding, width * xmaps + padding, images.shape[3])) + pad_value |
|
k = 0 |
|
offsets = [] |
|
for y in range(ymaps): |
|
for x in range(xmaps): |
|
if k >= nmaps: |
|
break |
|
x_offset = x * width + padding |
|
y_offset = y * height + padding |
|
grid[y * height + padding:(y+1) * height, x * width + padding:(x+1) * width, :] = images[k] |
|
offsets.append((x_offset, y_offset)) |
|
k = k + 1 |
|
if return_offsets: |
|
return grid, offsets |
|
else: |
|
return grid |
|
|
|
|
|
if __name__ == "__main__": |
|
im_rows = 5 |
|
im_cols = 10 |
|
stride = 1 |
|
patch_res = 15 |
|
|
|
image = np.random.randint(0, 256, size=(im_rows, im_cols, 3), dtype=np.uint8) |
|
image = Image.fromarray(image) |
|
image = np.array(image) |
|
plt.ion() |
|
plt.figure(1) |
|
plt.imshow(image) |
|
plt.show() |
|
|
|
|
|
patch_boundingboxes = compute_patch_boundingboxes(image.shape[0:2], stride, patch_res) |
|
|
|
plt.figure(2) |
|
|
|
for patch_boundingbox in patch_boundingboxes: |
|
patch = crop_or_pad_image_with_boundingbox(image, patch_boundingbox) |
|
plt.imshow(patch) |
|
plt.show() |
|
input("Press <Enter> to finish...") |
|
|