Diff of /mediaug/image_utils.py [000000] .. [05e710]

Switch to unified view

a b/mediaug/image_utils.py
1
from PIL import Image, ImageSequence, ImageDraw
2
import numpy as np
3
import cv2
4
5
from mediaug.utils import convert_array_to_poly
6
from mediaug.variables import COLOR_CYTO_MASK, COLOR_NUC_MASK
7
8
9
def np_to_pil(img):
10
    """ Converts a PIL format image to numpy
11
    Args:
12
      new_img (np.array): converted image
13
    Returns:
14
      img (PIL.Image): input image
15
    """
16
    return Image.fromarray(img)
17
18
19
def pil_to_np(img):
20
    """ Converts a PIL format image to numpy
21
    Args:
22
      img (PIL.Image): input image
23
    Returns:
24
      new_img (np.array): converted image
25
    """
26
    return np.array(img)
27
28
29
def read_tiff(path):
30
    """ Reads an image with a .tff extension, used in the Unet example.
31
    Args:
32
      path (str): The path of the image
33
    Returns:
34
      ans (np.array): Numpy array of the image values
35
    """
36
    return np.array([np.array(p) for p in ImageSequence.Iterator(Image.open(path))])
37
38
39
def read_bmp(img_path):
40
    """ Reads an image with a .bmp extension Returns np.array 
41
    Args:
42
      path (str): The path of the image
43
    Returns:
44
      ans (np.array): Numpy array of the image values
45
    """
46
    return cv2.imread(img_path)
47
48
49
def read_png(img_path):
50
    """ Reads an image with a .png extension. Returns np.array
51
    Args:
52
      path (str): The path of the image
53
    Returns:
54
      ans (np.array): Numpy array of the image values
55
    """
56
    return cv2.imread(img_path)
57
58
59
def read_dat_file(path):
60
    """ Reads an .dat file with polygons, the data from SIPaKMeD. Returns np.array
61
    Args:
62
      path (str): The path of the .dat file
63
    Returns:
64
      ans (np.array): The [n,2] array of a polygon
65
    """
66
    return np.loadtxt(path, delimiter=',')
67
68
69
def read_img(img_path):
70
    """ Reads an image
71
    Args:
72
      path (str): The path of the image
73
    Returns:
74
      ans (np.array): Numpy array of the image values
75
    """
76
    return cv2.imread(img_path)
77
78
79
def save_img(img, path):
80
    """ Save an img to given path
81
    Args:
82
      img (np.array): The image numpy array
83
      path (str): The path to save the image to
84
    """
85
    cv2.imwrite(path, img)
86
    return path
87
88
89
def rotate(image, angle):
90
    """ Rotates an image by angle in degrees, increases 
91
    the dimension of the imageas necessary
92
    Args:
93
      image (np.array): Image array
94
      angle (int): Degree of rotation clockwise in degrees
95
    Returns:
96
      rotated (np.array): The new rotated image array
97
    """
98
    (h, w) = image.shape[:2]
99
    (cX, cY) = (w // 2, h // 2)
100
 
101
    # grab the rotation matrix (applying the negative of the
102
    # angle to rotate clockwise), then grab the sine and cosine
103
    # (i.e., the rotation components of the matrix)
104
    M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
105
    cos = np.abs(M[0, 0])
106
    sin = np.abs(M[0, 1])
107
 
108
    # compute the new bounding dimensions of the image
109
    nW = int((h * sin) + (w * cos))
110
    nH = int((h * cos) + (w * sin))
111
 
112
    # adjust the rotation matrix to take into account translation
113
    M[0, 2] += (nW / 2) - cX
114
    M[1, 2] += (nH / 2) - cY
115
    return cv2.warpAffine(image, M, (nW, nH))
116
117
118
def soften_mask(mask, amount=5):
119
    """ Softens the edges of a mask by dialating it and then Gaussian blurring
120
    Args:
121
      mask (np.array): The mask array
122
      amount (int): The number of times to apply the dilation, should be arround 5
123
    Returns:
124
      ans (np.array): The transformed mask
125
    """
126
    kernel = np.ones((5,5), np.uint8) 
127
    mask_dilation = cv2.dilate(mask, kernel, iterations=amount)
128
    blur = cv2.GaussianBlur(mask_dilation, (5,5), 0)
129
    return cv2.max(mask, blur)
130
131
132
def get_blank_mask(img, greyscale=False):
133
    """ Return a blank mask the same size as the given image
134
    Args:
135
      img (np.array): The input image array
136
      greyscale (Boolean): Make it a single channel mask
137
    Returns:
138
      mask (np.array): Black mask the size of img
139
    """
140
    if greyscale:
141
        return np.zeros(img.shape[:2], np.uint8)
142
    return np.zeros(img.shape,np.uint8)
143
144
145
def image_on_image_alpha(bg, fg, fg_mask, center):
146
    """ Place an image on an image with a backround mask
147
    Blends them using the alpha mask.
148
    Args:
149
      bg (np.array): Background image array
150
      fg (np.array): Foreground image array
151
      fg_mask (np.array): Foreground alpha mask array
152
      center (tuple[int,int]): The postion on bg to place fg
153
    Returns:
154
      ans (np.array): The merged image
155
    """
156
    alpha = np.zeros(bg.shape[:2], dtype=np.uint8)
157
    alpha = place_img_on_img(alpha, fg_mask, center)
158
    cell_img = place_img_on_img(bg.copy(), fg, center)
159
160
    alpha = alpha.astype(float)/255
161
    alpha = np.repeat(alpha[:, :, np.newaxis], 3, axis=2)
162
163
    fg = cv2.multiply(alpha, cell_img.astype(float))
164
    bg = cv2.multiply(1.0 - alpha, bg.astype(float))
165
166
    return cv2.add(fg, bg).astype(np.uint8)
167
168
169
def place_img_on_img(bg, fg, center):
170
    """ Place an image on top of another image
171
    Args:
172
      bg (np.array): Backgourn image
173
      fg (np.array): Foreground image
174
      center (x, y): Where to put CENTER of fg image
175
    Returns:
176
      ans (np.array): The new image
177
    """
178
    ch, cw = center
179
    fg_h, fg_w = fg.shape[:2]
180
    bg_h, bg_w = bg.shape[:2]
181
182
    # check offset in bg
183
    if cw < 0 or cw > bg_w or ch < 0 or ch > bg_h:
184
        raise ValueError('Center not in backgound bounds')
185
186
    # find top left corner of fg in respect to bg
187
    left_w = cw - (fg_w // 2)
188
    left_h = ch - (fg_h // 2)
189
    end_w = left_w + fg_w
190
    end_h = left_h + fg_h
191
192
    # for if goes over bg boundries
193
    abs_left_w = max(left_w, 0)
194
    abs_left_h = max(left_h, 0)
195
    abs_end_w = min(end_w, bg_w)
196
    abs_end_h = min(end_h, bg_h)
197
    
198
    # for fg boundries
199
    fg_left_w = abs(min(left_w, 0))
200
    fg_left_h = abs(min(left_h, 0))
201
    diff_w = bg_w - end_w
202
    diff_h = bg_h - end_h
203
    if diff_w >= 0:
204
        fg_end_w = fg_w
205
    else:
206
        fg_end_w = diff_w
207
    if diff_h >= 0:
208
        fg_end_h = fg_h
209
    else:
210
        fg_end_h = diff_h
211
212
    ans = np.copy(bg)
213
    ans[abs_left_h:abs_end_h, abs_left_w:abs_end_w] = fg[fg_left_h:fg_end_h, fg_left_w:fg_end_w]
214
    return ans
215
216
217
def generate_cell_mask(img, cyto, nuc):
218
    """ Generate a mask for a labelled cell
219
    Args:
220
      img (np.array): The image array
221
      cyto (np.array): A [n,2] numpy array representing the polygon for the cytoplasm of a cell
222
      nuc (np.array): A [n,2] numpy array representing the polygon for the nucleus of a cell
223
    Returns:
224
      mask (np.array): The mask of parts of a cell
225
    """
226
    w, h, _ = img.shape
227
    mask = Image.new(mode="RGB", size=(h, w))
228
    ImageDraw.Draw(mask).polygon(convert_array_to_poly(cyto), outline=None, fill=COLOR_CYTO_MASK)
229
    ImageDraw.Draw(mask).polygon(convert_array_to_poly(nuc), outline=None, fill=COLOR_NUC_MASK)
230
    return np.array(mask)
231
232
233
def generate_cell_mask_list(img, cytos, nucs):
234
    """ Generate a masks for a list of labelled cell in slide
235
    Args:
236
      img (np.array): The image array
237
      cytos (list[np.array]): List of[n,2] numpy array representing the polygon for the cytoplasm of a cell
238
      nucs (list[np.array]): List of [n,2] numpy array representing the polygon for the nucleus of a cell
239
    Returns:
240
      mask (np.array): The mask of cells in slide
241
    """
242
    h, w, _ = img.shape
243
    mask = Image.new(mode="RGB", size=(w, h))
244
    for poly in cytos:
245
        ImageDraw.Draw(mask).polygon(convert_array_to_poly(poly), outline=None, fill=COLOR_CYTO_MASK)
246
    for poly in nucs:
247
        ImageDraw.Draw(mask).polygon(convert_array_to_poly(poly), outline=None, fill=COLOR_NUC_MASK)
248
    return np.array(mask)
249
250
251
def is_greyscale(img):
252
    """ Checks if an image array is greyscale
253
    Args:
254
      img (np.array): The image array
255
    Returns:
256
      is_greyscaled (Boolean): Is the image a greyscale image
257
    """
258
    if len(img.shape) < 3:
259
        return True
260
    return False