|
a |
|
b/utils/libs.py |
|
|
1 |
import glob |
|
|
2 |
import numpy as np |
|
|
3 |
import cv2 |
|
|
4 |
from PIL import Image |
|
|
5 |
from utils import config |
|
|
6 |
|
|
|
7 |
|
|
|
8 |
def write(filename, content, class_num=2, color_map=True): |
|
|
9 |
""" |
|
|
10 |
Save image array to a specified path. |
|
|
11 |
The image will be automatically recolored via the class number. |
|
|
12 |
:param filename: The specified path. |
|
|
13 |
:param content: Numpy array containing the image. |
|
|
14 |
:param class_num: Total class number. |
|
|
15 |
:param color_map: Whether change the probability into gray grade. |
|
|
16 |
""" |
|
|
17 |
if class_num <= 1: |
|
|
18 |
raise Exception('ERROR: Class number should be >= 2.') |
|
|
19 |
color_stage = 255. / (class_num - 1) if color_map else 1.0 |
|
|
20 |
new_image = Image.fromarray(np.uint8(content * color_stage)) |
|
|
21 |
new_image.save(filename, "PNG") |
|
|
22 |
|
|
|
23 |
|
|
|
24 |
def generate_effective_regions(size): |
|
|
25 |
""" |
|
|
26 |
This function is used to generate effective regions for inference according to the given slide size. |
|
|
27 |
:param size: Given slide size, should be in the form of [w, h]. |
|
|
28 |
""" |
|
|
29 |
width = size[0] |
|
|
30 |
height = size[1] |
|
|
31 |
x_step = int(width / config.CENTER_SIZE) |
|
|
32 |
y_step = int(height / config.CENTER_SIZE) |
|
|
33 |
regions = [] |
|
|
34 |
for x in range(0, x_step): |
|
|
35 |
for y in range(0, y_step): |
|
|
36 |
regions.append([x * config.CENTER_SIZE, y * config.CENTER_SIZE, 0, 0, |
|
|
37 |
config.CENTER_SIZE - 1, config.CENTER_SIZE - 1]) |
|
|
38 |
if not height % config.CENTER_SIZE == 0: |
|
|
39 |
for x in range(0, x_step): |
|
|
40 |
regions.append([x * config.CENTER_SIZE, height - config.CENTER_SIZE, |
|
|
41 |
0, (y_step + 1) * config.CENTER_SIZE - height, |
|
|
42 |
config.CENTER_SIZE - 1, config.CENTER_SIZE - 1]) |
|
|
43 |
if not width % config.CENTER_SIZE == 0: |
|
|
44 |
for y in range(0, y_step): |
|
|
45 |
regions.append([width - config.CENTER_SIZE, y * config.CENTER_SIZE, |
|
|
46 |
(x_step + 1) * config.CENTER_SIZE - width, 0, |
|
|
47 |
config.CENTER_SIZE - 1, config.CENTER_SIZE - 1]) |
|
|
48 |
if not (height % config.CENTER_SIZE == 0 or width % config.CENTER_SIZE == 0): |
|
|
49 |
regions.append([width - config.CENTER_SIZE, height - config.CENTER_SIZE, |
|
|
50 |
(x_step + 1) * config.CENTER_SIZE - width, (y_step + 1) * config.CENTER_SIZE - height, |
|
|
51 |
config.CENTER_SIZE - 1, config.CENTER_SIZE - 1]) |
|
|
52 |
return regions |
|
|
53 |
|
|
|
54 |
|
|
|
55 |
def generate_overlap_tile(region, dimensions): |
|
|
56 |
""" |
|
|
57 |
This function is used to process border patches. |
|
|
58 |
""" |
|
|
59 |
shifted_region_x = region[0] - config.BORDER_SIZE |
|
|
60 |
shifted_region_y = region[1] - config.BORDER_SIZE |
|
|
61 |
clip_region_x = config.BORDER_SIZE |
|
|
62 |
clip_region_y = config.BORDER_SIZE |
|
|
63 |
if region[0] == 0: |
|
|
64 |
shifted_region_x = shifted_region_x + config.BORDER_SIZE |
|
|
65 |
clip_region_x = 0 |
|
|
66 |
if region[1] == 0: |
|
|
67 |
shifted_region_y = shifted_region_y + config.BORDER_SIZE |
|
|
68 |
clip_region_y = 0 |
|
|
69 |
if region[0] == dimensions[0] - config.CENTER_SIZE: |
|
|
70 |
shifted_region_x = shifted_region_x - config.BORDER_SIZE |
|
|
71 |
clip_region_x = 2 * config.BORDER_SIZE |
|
|
72 |
if region[1] == dimensions[1] - config.CENTER_SIZE: |
|
|
73 |
shifted_region_y = shifted_region_y - config.BORDER_SIZE |
|
|
74 |
clip_region_y = 2 * config.BORDER_SIZE |
|
|
75 |
return [shifted_region_x, shifted_region_y], [clip_region_x, clip_region_y] |
|
|
76 |
|
|
|
77 |
|
|
|
78 |
def image_to_array(input_image): |
|
|
79 |
""" |
|
|
80 |
Loads image into numpy array. |
|
|
81 |
""" |
|
|
82 |
im_array = np.array(input_image.getdata(), dtype=np.uint8) |
|
|
83 |
im_array = im_array.reshape((input_image.size[0], input_image.size[1])) |
|
|
84 |
return im_array |
|
|
85 |
|
|
|
86 |
|
|
|
87 |
def post_processing(image_patch): |
|
|
88 |
""" |
|
|
89 |
Remove small noisy points. |
|
|
90 |
""" |
|
|
91 |
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (config.FILTER_KERNEL, config.FILTER_KERNEL)) |
|
|
92 |
open_patch = cv2.morphologyEx(image_patch, cv2.MORPH_OPEN, kernel) |
|
|
93 |
close_patch = cv2.morphologyEx(open_patch, cv2.MORPH_CLOSE, kernel) |
|
|
94 |
return close_patch |
|
|
95 |
|
|
|
96 |
|
|
|
97 |
def concat_patches(temp_dir, image_name): |
|
|
98 |
""" |
|
|
99 |
Concatenate the predicted patches into a thumbnail result. |
|
|
100 |
""" |
|
|
101 |
prediction_list = glob.glob(temp_dir + image_name + '*_prediction.png') |
|
|
102 |
patch_list = [] |
|
|
103 |
for prediction_image in prediction_list: |
|
|
104 |
name_parts = prediction_image.split('/')[-1].split('_') |
|
|
105 |
pos_x, pos_y = int(name_parts[-3]), int(name_parts[-2]) |
|
|
106 |
patch_list.append([pos_x, pos_y]) |
|
|
107 |
image_patches = [] |
|
|
108 |
patch_list.sort() |
|
|
109 |
last_x = -1 |
|
|
110 |
row_patch = [] |
|
|
111 |
for position in patch_list: |
|
|
112 |
pos_x = position[0] |
|
|
113 |
pos_y = position[1] |
|
|
114 |
image = Image.open(temp_dir + '_'.join([image_name, str(pos_x), str(pos_y), 'prediction']) + '.png') |
|
|
115 |
original_width, original_height = image.size |
|
|
116 |
if original_width < config.THUMBNAIL_RATIO or original_height < config.THUMBNAIL_RATIO: |
|
|
117 |
continue |
|
|
118 |
image = image.resize( |
|
|
119 |
(int(original_width / config.THUMBNAIL_RATIO), |
|
|
120 |
int(original_height / config.THUMBNAIL_RATIO)), Image.NEAREST) |
|
|
121 |
image_patch = image_to_array(image) |
|
|
122 |
if not pos_x == last_x: |
|
|
123 |
last_x = pos_x |
|
|
124 |
if len(row_patch) == 0: |
|
|
125 |
row_patch = image_patch |
|
|
126 |
else: |
|
|
127 |
if not len(image_patches) == 0: |
|
|
128 |
image_patches = np.column_stack((image_patches, row_patch)) |
|
|
129 |
else: |
|
|
130 |
image_patches = row_patch |
|
|
131 |
row_patch = image_patch |
|
|
132 |
else: |
|
|
133 |
row_patch = np.row_stack((row_patch, image_patch)) |
|
|
134 |
prediction = np.column_stack((image_patches, row_patch)) |
|
|
135 |
return prediction |