cell-data-loader 0.0.32__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,892 @@
1
+ #!/usr/bin/python
2
+
3
+ import os,sys,json,csv,cv2,glob,random,re
4
+ import numpy as np
5
+ from numpy.random import choice,shuffle
6
+ from math import floor,ceil,sqrt
7
+ from PIL import Image,ImageEnhance
8
+ from scipy import ndimage
9
+ from scipy.ndimage.interpolation import map_coordinates
10
+ from scipy.ndimage.filters import gaussian_filter
11
+ from .base_dataset import BaseDataset
12
+ import torch,torchvision
13
+ from contextlib import contextmanager
14
+ import warnings
15
+ #from random import shuffle
16
+ try:
17
+ from cellpose import models, io
18
+ except:
19
+ warnings.warn("Working Cellpose not installed -- cell segmentation not"+\
20
+ " possible")
21
+
22
+ import skimage
23
+ # DEFINE CELLPOSE MODEL
24
+
25
+ @contextmanager
26
+ def suppress_stdout():
27
+ with open(os.devnull, "w") as devnull:
28
+ old_stdout = sys.stdout
29
+ sys.stdout = devnull
30
+ try:
31
+ yield
32
+ finally:
33
+ sys.stdout = old_stdout
34
+
35
+ # Function to distort image, stolen shamelessly from
36
+ # https://www.kaggle.com/bguberfain/elastic-transform-for-data-augmentation
37
+ def elastic_transform(im, alpha, sigma, alph_af, rand_s=None):
38
+ """Elastic deformation of images as described in [Simard2003]_
39
+ (with modifications).
40
+ .. [Simard2003] Simard, Steinkraus and Platt, "Best Practices for
41
+ Convolutional Neural Networks applied to Visual Document
42
+ Analysis", in Proc. of the International Conference on
43
+ Document Analysis and Recognition, 2003.
44
+
45
+ Based on https://gist.github.com/erniejunior/601cdf56d2b424757de5
46
+ """
47
+ if rand_s is None:
48
+ rand_s = np.random.RandomState(None)
49
+
50
+ shape = im.shape
51
+ shape_size = shape[:2]
52
+
53
+ # Random affine
54
+ cent_squ = np.float32(shape_size) // 2
55
+ squ_siz = min(shape_size) // 3
56
+ pts1 = np.float32([cent_squ + squ_siz,
57
+ [cent_squ[0]+squ_siz, cent_squ[1]-squ_siz], cent_squ - squ_siz])
58
+ pts2 = pts1 + rand_s.uniform(-alph_af,
59
+ alph_af, size=pts1.shape).astype(np.float32)
60
+ M = cv2.getAffineTransform(pts1, pts2)
61
+ im = cv2.warpAffine(im, M, shape_size[::-1],
62
+ borderMode=cv2.BORDER_REFLECT_101)
63
+
64
+ dx = gaussian_filter((rand_s.rand(*shape) * 2 - 1), sigma) * alpha
65
+ dy = gaussian_filter((rand_s.rand(*shape) * 2 - 1), sigma) * alpha
66
+ dz = np.zeros_like(dx)
67
+
68
+ x, y, z = np.meshgrid(np.arange(shape[1]),\
69
+ np.arange(shape[0]),\
70
+ np.arange(shape[2]))
71
+ indices = np.reshape(y+dy, (-1, 1)),\
72
+ np.reshape(x+dx, (-1, 1)),\
73
+ np.reshape(z, (-1, 1))
74
+
75
+ return map_coordinates(im,indices,order=1,mode='reflect').reshape(shape)
76
+
77
+ def slice_and_augment(im,x,y,l,w,
78
+ trans = None,
79
+ out_size = None,
80
+ rotation=0,
81
+ x_shift = 0.0,
82
+ y_shift = 0.0,
83
+ size_shift = 0.0):
84
+ """
85
+ Slices out and augments a particular cell from a larger image, given
86
+ the box coordinates (x,y,l <length>, w <width>). The transformation array
87
+ can be added to augment images as well. The rotation and shifts are applied
88
+ separately since they may need to consider outside images for the corners.
89
+ """
90
+ if isinstance(im,np.ndarray):
91
+ dtype = "numpy"
92
+ imsize = im.shape
93
+ elif torch.is_tensor(im):
94
+ dtype = "torch"
95
+ imsize = im.size()
96
+ else:
97
+ raise Exception("Unimplemented type in slice_and_augment")
98
+ # Random horizontal and vertical shifts
99
+ x = x + round(max(l,w) * x_shift)
100
+ y = y + round(max(l,w) * y_shift)
101
+
102
+ # Random size shifts
103
+
104
+ y = int(y - 0.5*(size_shift * w))
105
+ x = int(x - 0.5*(size_shift * l))
106
+ l = int(l * (size_shift + 1))
107
+ w = int(w * (size_shift + 1))
108
+
109
+ # Arbitrary, continuous rotations
110
+ e = 2 # sqrt(2)
111
+ lw = max(w,l)
112
+ rr = range(floor((y + 0.5 * w) - e * lw),ceil((y + 0.5 * w) + e * lw))
113
+ rr = [_ % imsize[0] for _ in rr]
114
+ rr2 = range(floor((x + 0.5 * l) - e * lw),ceil((x + 0.5 * l) + e * lw))
115
+ rr2 = [_ % imsize[1] for _ in rr2]
116
+
117
+ if dtype == "torch":
118
+ imslice = torch.index_select(im,0,torch.LongTensor(rr))
119
+ imslice = torch.index_select(imslice,1,torch.LongTensor(rr2))
120
+ elif dtype == "numpy":
121
+ imslice = im.take(rr,axis=0)#,mode='wrap')
122
+ imslice = imslice.take(rr2,axis=1)#,mode='wrap')
123
+ else:
124
+ raise Exception("Unimplemented dtype: %s" % dtype)
125
+
126
+ if dtype == "torch":
127
+ imshap = imslice.size()
128
+ elif dtype == "numpy":
129
+ imshap = imslice.shape
130
+ else:
131
+ raise Exception("Unimplemented dtype: %s" % dtype)
132
+
133
+ if dtype == "torch":
134
+ imslice = torchvision.transforms.functional.rotate(imslice,rotation)
135
+ elif dtype == "numpy":
136
+ imslice = ndimage.rotate(imslice,rotation)
137
+ else:
138
+ raise Exception("Unimplemented dtype: %s" % dtype)
139
+
140
+ # Applies elastic distortion; see above
141
+ rr = range(int(0.5 * imshap[0] - lw/2.0),int(0.5 * imshap[0] + lw/2.0))
142
+ rr = [_ % imshap[0] for _ in rr]
143
+ rr2 = range(int(0.5 * imshap[1] - lw/2.0),int(0.5 * imshap[1] + lw/2.0))
144
+ rr2 = [_ % imshap[1] for _ in rr2]
145
+
146
+ if dtype == "torch":
147
+ imslice = torch.index_select(imslice,0,torch.LongTensor(rr))
148
+ imslice = torch.index_select(imslice,1,torch.LongTensor(rr2))
149
+ elif dtype == "numpy":
150
+ imslice = imslice.take(rr,axis=0)#,mode='wrap')
151
+ imslice = imslice.take(rr2,axis=1)#,mode='wrap')
152
+
153
+ #imslice = Image.fromarray(imslice)
154
+ if trans is not None: imslice = trans(imslice)
155
+
156
+ #imslice = PIL.ImageEnhance.Color(imslice).enhance(color_shift)
157
+
158
+ #increasing the contrast
159
+ #new_image = PIL.ImageEnhance.Contrast(image).enhance(1.2)
160
+ if out_size is not None:
161
+ if dtype == "torch":
162
+ imslice = torch.permute(torchvision.transforms.Resize(
163
+ out_size)(torch.permute(imslice,(2,0,1))),(1,2,0))
164
+ elif dtype == "numpy":
165
+ imslice = cv2.resize(imslice,out_size[::-1])
166
+ return imslice
167
+
168
+ def get_augmented_image(im,x,y,l,w,output_im_size = (70,70),augment = False,
169
+ soft_augment = False,c_shift_amt = 0.05,blur_amt = 0.2,
170
+ spatial_shift_amt = 0.02,elastic_distort = True,size_shift = 0.05,
171
+ rotate=True,reflect=True,js_params = None,contrast = 0.2,brightness=0.2):
172
+
173
+ if js_params is not None:
174
+ for k in js_params:
175
+ p = js_params[k]
176
+ if k == "blur_amt":
177
+ blur_amt = int(p)
178
+ elif k == "spatial_shift_amt":
179
+ spatial_shift_amt = float(p)
180
+ elif k == "elastic_distort":
181
+ elastic_distort = bool(int(p))
182
+ elif k == "rotate":
183
+ rotate = bool(int(p))
184
+ elif k == "reflect":
185
+ reflect = bool(int(p))
186
+ elif k == "c_shift_amt":
187
+ c_shift_amt = bool(int(p))
188
+ elif k == "size_shift":
189
+ size_shift = float(p)
190
+ else:
191
+ raise Exception("Invalid input parameter %s" %k)
192
+
193
+ c_shift_arr = np.random.rand(3,3)
194
+ c_shift_arr = c_shift_arr / np.sum(c_shift_arr,axis=1)
195
+ c_shift_arr = (np.eye(3) * (1-c_shift_amt) + c_shift_arr * c_shift_amt)
196
+
197
+ if augment:
198
+ imslice = slice_and_augment(im,x,y,l,w,
199
+ rotation= 0 if not rotate else 360 * np.random.random(),
200
+ reflection=False if not reflect else choice([True,False]),
201
+ color_shift = c_shift_arr,
202
+ blur = 1 + (0.5 - np.random.random()) * 2 * blur_amt,
203
+ contrast = 1 + (0.5 - np.random.random()) * 2 * contrast,
204
+ brightness = 1 + (0.5 - np.random.random()) * 2 * brightness,
205
+ x_shift = (0.5 - np.random.random()) * 2 *spatial_shift_amt,
206
+ y_shift = (0.5 - np.random.random()) * 2 *spatial_shift_amt,
207
+ size_shift = size_shift * (np.random.rand() - 0.5),
208
+ distort = elastic_distort)
209
+ elif soft_augment:
210
+ imslice = slice_and_augment(im,x,y,l,w,
211
+ rotation= 0 if not rotate else 360 * np.random.random(),
212
+ reflection=False if not reflect else choice([True,False]))
213
+ else:
214
+ imslice = slice_and_augment(im,x,y,l,w)
215
+ if not type(imslice) == Image.Image:
216
+ imslice = Image.fromarray(imslice)
217
+ imslice = imslice.resize((output_im_size[0],output_im_size[1]))
218
+ return imslice
219
+
220
+ def read_image_file(imfile,out_format = "torch"):
221
+ basename,ext = os.path.splitext(imfile)
222
+ if ext.lower() == ".jpg":
223
+ return
224
+ elif ext.lower() == ".cz":
225
+ return
226
+ elif ext.lower() == ".svz":
227
+ return
228
+ return
229
+
230
+ def read_label_file(label_file):
231
+ return
232
+
233
+ def is_image_file(filename):
234
+ basename,ext = os.path.splitext(filename)
235
+ return ext.lower() in [".jpg",".png",".jpeg",".czi",".svs",".tiff",".tif"]
236
+
237
+ def is_label_file(filename):
238
+ return True
239
+
240
+ def get_verify(X,Y):
241
+ h = int(X.shape[1] / 2)
242
+ w = int(X.shape[2] / 2)
243
+ q1 = np.expand_dims(np.mean(X[:,:h,:w,:],axis=(1,2,3)),axis=1) / 255.0
244
+ q2 = np.expand_dims(np.mean(X[:,h:,:w,:],axis=(1,2,3)),axis=1) / 255.0
245
+ q3 = np.expand_dims(np.mean(X[:,:h,w:,:],axis=(1,2,3)),axis=1) / 255.0
246
+ q4 = np.expand_dims(np.mean(X[:,h:,w:,:],axis=(1,2,3)),axis=1) / 255.0
247
+ c1 = np.expand_dims(np.mean(X[:,:,:,0],axis=(1,2)),axis=1) / 255.0
248
+ c2 = np.expand_dims(np.mean(X[:,:,:,1],axis=(1,2)),axis=1) / 255.0
249
+ c3 = np.expand_dims(np.mean(X[:,:,:,2],axis=(1,2)),axis=1) / 255.0
250
+ s = np.expand_dims(np.std(X,axis=(1,2,3)),axis=1) / 255.0
251
+ Y_ = np.concatenate((Y,q1,q2,q3,q4,c1,c2,c3,s),axis=1)
252
+
253
+ return Y_
254
+
255
+ def tile_image(im,autoencoder,a_dims=(600,600)):
256
+ for i in range(0,im.shape[0],a_dims[0]):
257
+ for j in range(0,im.shape[1],a_dims[1]):
258
+ if i+a_dims[0] > im.shape[0]:
259
+ i = im.shape[0] - a_dims[0] - 1
260
+ if j+a_dims[1] > im.shape[1]:
261
+ j = im.shape[1] - a_dims[1] - 1
262
+ input_im = torch.from_numpy(im[i:i+a_dims[0],
263
+ j:j+a_dims[1],:]).permute(2,1,0).unsqueeze(0).float()/255.0
264
+ input_im = autoencoder(input_im)
265
+ input_im = input_im.squeeze().permute(1,2,0).detach().numpy() * 255.0
266
+ im[i:i+a_dims[0],j:j+a_dims[1],:] = input_im
267
+ return im
268
+
269
+ def get_all_perimeters(mask):
270
+ perims = {}
271
+ if len(mask.shape) == 2:
272
+ for i in np.unique(mask.flatten()):
273
+ perims[i] = 0
274
+ for i in range(mask.shape[0]-1):
275
+ for j in range(mask.shape[1]-1):
276
+ if mask[i,j] != mask[i+1,j]:
277
+ perims[mask[i,j]] += 1
278
+ perims[mask[i+1,j]] += 1
279
+ if mask[i,j] != mask[i,j+1]:
280
+ perims[mask[i,j]] += 1
281
+ perims[mask[i,j+1]] += 1
282
+ elif len(mask.shape) == 3:
283
+ for i in range(mask.shape[2]):
284
+ perims[i] = 0
285
+ return perims
286
+
287
+ def get_perimeter(mask):
288
+ p = 0
289
+ for i in range(mask.shape[0]-1):
290
+ for j in range(mask.shape[1]-1):
291
+ if mask[i,j] != mask[i+1,j]:
292
+ p += 1
293
+ if mask[i,j] != mask[i,j+1]:
294
+ p += 1
295
+ return p
296
+
297
+ def get_smoothness(mask,mean_area = None):
298
+ area = np.sum(mask)
299
+ #return np.abs(area - mean_area)
300
+ return float(skimage.measure.perimeter(mask,neighbourhood=4) ** 2)/area
301
+
302
+ def crop2(dat, clp=True):
303
+ if clp: np.clip( dat, 0, 1, out=dat )
304
+ for i in range(dat.ndim):
305
+ dat = np.swapaxes(dat, 0, i) # send i-th axis to front
306
+ while np.all( dat[0]==0 ):
307
+ dat = dat[1:]
308
+ while np.all( dat[-1]==0 ):
309
+ dat = dat[:-1]
310
+ dat = np.swapaxes(dat, 0, i) # send i-th axis to its original position
311
+ return dat
312
+
313
+ def remove_unsmooth(mask,thresh=25):
314
+ smoothnesses = np.zeros((len(np.unique(mask))-1,))
315
+ if len(mask.shape) == 2:
316
+ for z in np.unique(mask.flatten()):
317
+ if z > 0:
318
+ z_mask = crop2(mask == z)
319
+ smoothnesses[int(z)-1] = get_smoothness(z_mask)
320
+ elif len(mask.shape) == 3:
321
+ for z in range(mask.shape[2]):
322
+ smoothnesses[z] = get_smoothness(crop2(np.squeeze(mask[:,:,z])))
323
+ max_smoothness = smoothnesses.max()
324
+ min_smoothness = smoothnesses.min()
325
+ for i in range(mask.max()):
326
+ if smoothnesses[i] > thresh:
327
+ mask[mask == i+1] = 0
328
+ mask = renumber_masks(mask)
329
+ return mask
330
+
331
+
332
+ def get_box_intersection_area(bb1,bb2):
333
+ """
334
+ Calculate the Intersection over Union (IoU) of two bounding boxes.
335
+
336
+ Parameters
337
+ ----------
338
+ bb1 : dict
339
+ Keys: {'x1', 'x2', 'y1', 'y2'}
340
+ The (x1, y1) position is at the top left corner,
341
+ the (x2, y2) position is at the bottom right corner
342
+ bb2 : dict
343
+ Keys: {'x1', 'x2', 'y1', 'y2'}
344
+ The (x, y) position is at the top left corner,
345
+ the (x2, y2) position is at the bottom right corner
346
+
347
+ Returns
348
+ -------
349
+ float
350
+ in [0, 1]
351
+ -----
352
+ Adapted from https://stackoverflow.com/questions/25349178/
353
+ calculating-percentage-of-bounding-box-overlap-for-image-detector-evaluation
354
+ """
355
+ # determine the coordinates of the intersection rectangle
356
+ bb1_x1 = bb1[1]
357
+ bb1_x2 = bb1[1] + bb1[3]
358
+ bb1_y1 = bb1[0]
359
+ bb1_y2 = bb1[0] + bb1[2]
360
+
361
+ bb2_x1 = bb2[1]
362
+ bb2_x2 = bb2[1] + bb2[3]
363
+ bb2_y1 = bb2[0]
364
+ bb2_y2 = bb2[0] + bb2[2]
365
+
366
+ x_left = max(bb1_x1, bb2_x1)
367
+ y_top = max(bb1_y1, bb2_y1)
368
+ x_right = min(bb1_x2, bb2_x2)
369
+ y_bottom = min(bb1_y2, bb2_y2)
370
+
371
+ if x_right < x_left or y_bottom < y_top:
372
+ return 0.0
373
+
374
+ # The intersection of two axis-aligned bounding boxes is always an
375
+ # axis-aligned bounding box
376
+ intersection_area = (x_right - x_left) * (y_bottom - y_top)
377
+
378
+ # compute the area of both AABBs
379
+ bb1_area = bb1[3] * bb1[2]
380
+ bb2_area = bb2[3] * bb2[2]
381
+
382
+ # compute the intersection over union by taking the intersection
383
+ # area and dividing it by the sum of prediction + ground-truth
384
+ # areas - the interesection area
385
+ iou = intersection_area / float(bb1_area + bb2_area - intersection_area)
386
+ assert iou >= 0.0
387
+ assert iou <= 1.0
388
+ return iou
389
+
390
+ def find_box_intersections(annotated_boxes,predicted_boxes,min_dice=0.5):
391
+ assert(min_dice >= 0.0)
392
+ assert(min_dice <= 1.0)
393
+ final_boxes = []
394
+ for i in range(predicted_boxes.shape[0]):
395
+ append = True
396
+ for j in range(annotated_boxes.shape[0]):
397
+ bb1 = predicted_boxes[i,:]
398
+ bb2 = annotated_boxes[j,:]
399
+ dice = get_box_intersection_area(bb1,bb2)
400
+ if dice > min_dice:
401
+ append = False
402
+ if append:
403
+ final_boxes.append(predicted_boxes[i,:])
404
+ return np.array(final_boxes)
405
+
406
+ def merge_predicted_and_annotated(annotated_boxes,predicted_boxes,min_dice=0.5):
407
+ intersected_boxes = find_box_intersections(annotated_boxes,predicted_boxes,
408
+ min_dice=min_dice)
409
+ if intersected_boxes.shape[1] == 4:
410
+ neg_labels = np.array(["negative" for _ in range(intersected_boxes.shape[0])])
411
+ neg_labels = np.expand_dims(neg_labels,axis=1)
412
+ neg_labels = neg_labels.astype(object)
413
+ intersected_boxes = intersected_boxes.astype(object)
414
+ intersected_boxes = np.concatenate((intersected_boxes,neg_labels),axis=1)
415
+ return np.concatenate((annotated_boxes,intersected_boxes),axis=0)
416
+
417
+ def get_average_mask_size(mask):
418
+ return np.sum(mask.flatten() > 0) / mask.max()
419
+
420
+ def renumber_masks(mask):
421
+ unique_m = np.sort(np.unique(mask))
422
+ assert(unique_m[0] == 0)
423
+ if unique_m[-1] == len(unique_m) - 1:
424
+ return mask
425
+ for i in range(1,len(unique_m)):
426
+ mask[mask == unique_m[i]] = i
427
+ return mask
428
+
429
+ def remove_edges(mask,min_edge_size=0.5):
430
+ avg_mask_size = get_average_mask_size(mask)
431
+ exclude_i = {}
432
+ for i in range(mask.shape[0]):
433
+ for j in [0,-1]:
434
+ val = mask[i,j]
435
+ if val > 0 and val not in exclude_i:
436
+ exclude_i[val] = True
437
+ if np.sum(mask.flatten() == val) < min_edge_size * avg_mask_size:
438
+ mask[mask == val] = 0
439
+ for i in range(mask.shape[1]):
440
+ for j in [0,-1]:
441
+ val = mask[j,i]
442
+ if val > 0 and val not in exclude_i:
443
+ exclude_i[val] = True
444
+ if np.sum(mask.flatten() == val) < min_edge_size * avg_mask_size:
445
+ mask[mask == val] = 0
446
+ mask = renumber_masks(mask)
447
+ return mask
448
+
449
+ def remove_smalls(mask,avg_fraction_thresh = 0.5):
450
+ avg_mask_size = get_average_mask_size(mask)
451
+ for i in range(1,mask.max() + 1):
452
+ area = np.sum(mask == i)
453
+ if area < avg_fraction_thresh * avg_mask_size:
454
+ mask[mask == i] = 0
455
+ mask = renumber_masks(mask)
456
+ return mask
457
+
458
+ def masks_to_boxes(masks):
459
+ if len(masks.shape) == 2:
460
+ num_masks = len(np.unique(masks)) - 1
461
+ elif len(masks.shape) == 3:
462
+ num_masks = masks.shape[2]
463
+ else:
464
+ raise Exception("Invalid mask length: %d" % len(masks.shape))
465
+ boxes = np.zeros((num_masks,4),dtype=int)
466
+ for i in range(num_masks):
467
+ if len(masks.shape) == 2:
468
+ y_bar = np.max(masks == i+1,axis=0)
469
+ x_bar = np.max(masks == i+1,axis=1)
470
+ elif len(masks.shape) == 3:
471
+ m = np.squeeze(masks[:,:,i])
472
+ y_bar = np.max(m,axis=0)
473
+ x_bar = np.max(m,axis=1)
474
+ x_min = np.argmax(x_bar)
475
+ y_min = np.argmax(y_bar)
476
+ x_max = len(x_bar) - np.argmax(x_bar[::-1]) - 1
477
+ y_max = len(y_bar) - np.argmax(y_bar[::-1]) - 1
478
+ boxes[i,0] = y_min
479
+ boxes[i,1] = x_min
480
+ boxes[i,2] = y_max - y_min
481
+ boxes[i,3] = x_max - x_min
482
+ if i == 1 and False:
483
+ print(boxes[i,:])
484
+ return boxes
485
+
486
+ def get_im(im):
487
+ if isinstance(im,str) and os.path.isfile(im):
488
+ return np.array(Image.open(im))
489
+ elif torch.is_tensor(im):
490
+ return im.numpy()
491
+ elif isinstance(im,np.ndarray):
492
+ return im
493
+ else:
494
+ raise Exception("Unsupported type %s" % str(type(im)))
495
+
496
+ def get_cell_segmentation_mask(imagename,model = None, modelname = "cellpose",gpu=True):
497
+ if isinstance(imagename,str):
498
+ save_mask_name = "%s_mask.npy" % os.path.splitext(imagename)[0]
499
+ if os.path.isfile(save_mask_name):
500
+ print("Loading mask %s" % save_mask_name)
501
+ return np.load(save_mask_name)
502
+ img = get_im(imagename)#img = io.imread(imagename)
503
+ if model == None:
504
+ if modelname == "cellpose":
505
+ try:
506
+ model = models.Cellpose(gpu=gpu, model_type='cyto')
507
+ except:
508
+ raise Exception("""Attempt to instantiate cellpose model
509
+ did not work. Please check cellpose dependency""")
510
+ if modelname == "cellpose":
511
+ try:
512
+ masks, flows, styles, diams = model.eval(img, diameter=None,channels=[0,0])
513
+ except RuntimeError:
514
+ model = models.Cellpose(gpu=False, model_type='cyto')
515
+ masks, flows, styles, diams = model.eval(img, diameter=None,channels=[0,0])
516
+ elif modelname == "deepcell":
517
+ model = CytoplasmSegmentation()
518
+ masks = model.predict(img, image_mpp=0.65)
519
+ if isinstance(imagename,str):
520
+ print("Saving mask %s" % save_mask_name)
521
+ np.save(save_mask_name,masks)
522
+ return masks
523
+
524
+
525
+ def get_touch_matrix(unique_vals,mask):
526
+ unique_vals = list(unique_vals)
527
+ touch_matrix = np.zeros((len(unique_vals),len(unique_vals))).astype(bool)
528
+ for i in range(touch_matrix.shape[0]-1):
529
+ for j in range(touch_matrix.shape[1]-1):
530
+ if i == j:
531
+ touch_matrix[i,j] = True
532
+ touch_matrix[i+1,j+1] = True
533
+ if mask[i,j] > 0:
534
+ u1 = unique_vals.index(mask[i,j])
535
+ for k in [(i,j+1),(i+1,j),(i+1,j+1)]:
536
+ if mask[k[0],k[1]] > 0:
537
+ u2 = unique_vals.index(mask[k[0],k[1]])
538
+ touch_matrix[u1,u2] = True
539
+ touch_matrix[u2,u1] = True
540
+ return touch_matrix
541
+
542
+ def get_all_combos(l,include=None):
543
+ if include is not None:
544
+ l2 = list(l.copy())
545
+ l2.remove(include)
546
+ l = np.array(l2)
547
+ all_combinations = []
548
+ for r in [0,1]:#range(len(l) + 1):
549
+ combinations_object = itertools.combinations(l, r)
550
+ combinations_list = [cc for cc in list(combinations_object)]
551
+ all_combinations += combinations_list
552
+ if include is not None:
553
+ all_combinations = [list(l) + [include] for l in all_combinations]
554
+ return all_combinations
555
+
556
+ def do_function(mask,mean_area=None):
557
+ unique_vals = np.unique(mask)
558
+ unique_vals_list = list(unique_vals)
559
+ if 0 in unique_vals_list:
560
+ unique_vals_list.remove(0)
561
+ else:
562
+ print("0 not in unique vals")
563
+ unique_vals = np.array(unique_vals_list)
564
+ if len(unique_vals) > 10:
565
+ tm = get_touch_matrix(unique_vals_list,mask)
566
+ else:
567
+ tm = None
568
+ island_mapping = {}
569
+ for u in unique_vals:
570
+ if tm is None:
571
+ all_combo_w_u = get_all_combos(unique_vals,include=u)
572
+ else:
573
+ #print(tm)
574
+ #print(tm[:,unique_vals_list.index(u)].astype(bool))
575
+ #print(unique_vals)
576
+ assert(len(unique_vals) == tm.shape[0])
577
+ combolist = unique_vals[tm[:,unique_vals_list.index(u)]]
578
+ #print(combolist)
579
+ if len(combolist) == 1:
580
+ all_combo_w_u = [[u]]
581
+ else:
582
+ all_combo_w_u = get_all_combos(combolist,include=u)
583
+ print(all_combo_w_u)
584
+ for c in all_combo_w_u:
585
+ smoothness = get_smoothness(mask,c,mean_area=mean_area)
586
+ if u not in island_mapping or island_mapping[u][1] > smoothness:
587
+ island_mapping[u] = (c,smoothness)
588
+ ims = [island_mapping[u][0] for u in island_mapping]
589
+ print("len(ims) %d" % len(ims))
590
+ ims_nodups = []
591
+ lolx = {}
592
+ for im in ims:
593
+ i_sort = sorted(im)
594
+ ss = str(i_sort)
595
+ if ss not in lolx:
596
+ lolx[ss] = True
597
+ ims_nodups.append(i_sort)
598
+ print(ims_nodups)
599
+ return ims_nodups
600
+
601
+ def resolve_intersections(masks):
602
+ mean_area = np.mean([np.sum(masks == i) for i in range(1,masks.max()+1)])
603
+ labeled_array, num_features = ndimage.label(masks != 0, np.ones((3,3)))
604
+ all_pairs = []
605
+ for i in np.unique(labeled_array):
606
+ mask_copy = masks.copy()
607
+ mask_copy[labeled_array != i] = 0
608
+ mask_copy = mask_copy[~np.all(mask_copy == 0,axis=1),:]
609
+ mask_copy = mask_copy[:,~np.all(mask_copy == 0,axis=0)]
610
+ print(mask_copy)
611
+ pairs = do_function(mask_copy,mean_area=mean_area)
612
+ all_pairs += pairs
613
+ masks_3D = np.zeros((masks.shape[0],masks.shape[1],len(all_pairs))).astype(bool)
614
+ print("masks_3D.shape: %s"%str(masks_3D.shape))
615
+ print("all_pairs len %d" % len(all_pairs))
616
+ for i in range(len(all_pairs)):
617
+ pair = all_pairs[i]
618
+ for p in pair:
619
+ masks_3D[:,:,i] = np.logical_or(masks_3D[:,:,i],masks == p)
620
+ print("masks_3D.shape: %s"%str(masks_3D.shape))
621
+ return masks_3D
622
+
623
+ def get_cell_boxes_from_image(imagename,model=None,gpu=True,
624
+ remove_edge_cells=True,return_model=False,verbose=False,
625
+ do_resolve_intersections=False,do_remove_smalls = True,
626
+ do_remove_unsmooth=False):
627
+ #temp = "temp.npy"
628
+ #if not os.path.isfile(temp) and False:
629
+ if verbose:
630
+ masks = get_cell_segmentation_mask(imagename,gpu=gpu,model=model)
631
+ else:
632
+ with suppress_stdout():
633
+ masks = get_cell_segmentation_mask(imagename,gpu=gpu,model=model)
634
+ if remove_edge_cells:
635
+ max_m = masks.max()
636
+ masks = remove_edges(masks,min_edge_size=np.inf)
637
+ if verbose: print("Removing %d cells from edges" % (max_m - masks.max()))
638
+ # np.save(temp,masks)
639
+ #else:
640
+ # masks = np.load(temp)
641
+ if do_remove_smalls:
642
+ masks = remove_smalls(masks)
643
+ # This gave some errors
644
+ #if do_remove_unsmooth:
645
+ # masks = remove_unsmooth(masks)
646
+ if do_resolve_intersections:
647
+ print("masks.shape: %s" % str(masks.shape))
648
+ print("unique_vals len - 1: %d" % (len(np.unique(masks))-1))
649
+ masks = resolve_intersections(masks)
650
+ boxes = masks_to_boxes(masks)
651
+ if return_model:
652
+ return boxes,masks,model
653
+ else:
654
+ return boxes,masks
655
+
656
+ def get_image_array(imagename,boxes,output_size = (64,64),
657
+ whole_im_autoencoder=None):
658
+ image = get_im(imagename)#np.array(Image.open(imagename))
659
+ if whole_im_autoencoder is not None:
660
+ image = tile_image(image,whole_im_autoencoder)
661
+ X = np.zeros((boxes.shape[0],output_size[0],output_size[1],3))
662
+ for i in range(boxes.shape[0]):
663
+ x = boxes[i,0]
664
+ y = boxes[i,1]
665
+ l = boxes[i,2]
666
+ w = boxes[i,3]
667
+ imslice = slice_and_augment(image,x,y,l,w)
668
+ if not type(imslice) == Image.Image:
669
+ imslice = Image.fromarray(imslice)
670
+ imslice = imslice.resize(output_size)
671
+ imarr = np.expand_dims(np.array(imslice),axis=0)
672
+ X[i,:,:,:] = imarr
673
+ return X
674
+
675
+ def get_predicted_labels(imagename,single_cell_pred_model_filepath='.',
676
+ gpu=True,remove_edge_cells = True,verbose=True,single_cell_pred_model=None,
677
+ lb=None,segmodel=None,just_segment = False,autoencoder=None,whole_im_autoencoder=None,
678
+ use_get_verify = False,num_dropout_tests = 1):
679
+ if not just_segment and single_cell_pred_model is None and lb is None:
680
+ single_cell_pred_model,lb = \
681
+ load_model_w_label_processor(output_folder = \
682
+ single_cell_pred_model_filepath,get_lb=True)
683
+ boxes,masks = get_cell_boxes_from_image(imagename,gpu=gpu,model=segmodel)
684
+ if just_segment:
685
+ return None,masks
686
+ X = get_image_array(imagename,boxes,whole_im_autoencoder=whole_im_autoencoder)
687
+ if autoencoder is not None:
688
+ X = autoencoder(X)
689
+ X = X - X.min()
690
+ X = X / X.max()
691
+ X = X * 255.0
692
+ if num_dropout_tests == 1:
693
+ Y = single_cell_pred_model.predict(X,batch_size=1)
694
+ else:
695
+ for layer in single_cell_pred_model.layers:
696
+ if "dropout" not in layer.name.lower():
697
+ layer.trainable = False
698
+ Y_many = np.array(
699
+ [single_cell_pred_model(X,training=True) for _ in range(num_dropout_tests)]
700
+ )
701
+ print(np.std(Y_many,axis=0))
702
+ Y = np.mean(Y_many,axis=0)
703
+ Ym = np.argmax(Y[:,:len(lb.classes_)],axis=1)
704
+ Y_uncertain = np.zeros((Y.shape[0]))
705
+ for i in range(Y.shape[0]):
706
+ if np.all(Y[i,:] > 0.0):
707
+ Y_uncertain[i] = 1
708
+ #assert(len(np.unique(Ym)) == 2)
709
+ assert(len(np.unique(masks)) == masks.max() + 1)
710
+ assert(masks.max() == len(Ym))
711
+ Ym = np.expand_dims(Ym,axis=1)
712
+ Yl = lb.inverse_transform(Ym)
713
+ Yl=np.expand_dims(Yl,axis=1)
714
+ for i in range(len(Ym)):
715
+ if Y_uncertain[i] == 1:
716
+ Yl[i] = "uncertain"
717
+ masks[masks == i + 1] = -1
718
+ else:
719
+ masks[masks == i + 1] = Ym[i] + 1
720
+ if not use_get_verify:
721
+ return np.concatenate((boxes,Yl,Y[:,:len(lb.classes_)]),axis=1),masks
722
+ else:
723
+ Y = get_verify(X,Y[:,:len(lb.classes_) + 8])
724
+ return np.concatenate((boxes,Yl,Y),axis=1),masks
725
+
726
+ def read_in_csv(labels):
727
+ if isinstance(labels,str) and os.path.isfile(labels):
728
+ foo = []
729
+ with open(labels,'r') as fileobj:
730
+ csvreader = csv.reader(fileobj)
731
+ for row in csvreader:
732
+ foo.append(row)
733
+ labels = foo
734
+ return labels
735
+
736
+ def get_classification_percent(labels,class_labels,get_total=False):
737
+ results = {}
738
+ total = 0
739
+ labels = read_in_csv(labels)
740
+ assert isinstance(labels,list)
741
+ for c in class_labels:
742
+ results[c] = 0
743
+ for row in labels:
744
+ total += 1
745
+ l = row[4]
746
+ results[l] += 1
747
+ #if results["ring"] > 20: results["ring"] = 0
748
+ if total == 0:
749
+ return results
750
+ if not get_total:
751
+ for r in results:
752
+ results[r] = results[r] / total
753
+ return results
754
+
755
+ def segment_folder(folderpath,modelpath='.',random_sample=None,
756
+ output_folderpath = None,model=None,lb=None,segmodel=None,autoencoder=None,
757
+ whole_im_autoencoder=None,skip_if_present=False,shuffle_order=False):
758
+ if output_folderpath == None:
759
+ output_folderpath = folderpath
760
+ imagenames = glob.glob(os.path.join(folderpath,'*.jpg'))
761
+ if random_sample is not None:
762
+ assert(isinstance(random_sample,int) and random_sample > 0)
763
+ imagenames = list(imagenames)
764
+ if shuffle_order:shuffle(imagenames)
765
+ imagenames = imagenames[:random_sample]
766
+ if not os.path.isdir(output_folderpath):
767
+ os.makedirs(output_folderpath)
768
+ for imagename in imagenames:
769
+ basename = os.path.splitext(os.path.basename(imagename))[0]
770
+ npy_save = os.path.join(output_folderpath,'%s_predicted.npy'%basename)
771
+ csv_save = os.path.join(output_folderpath,'%s_predicted.csv'%basename)
772
+ if os.path.isfile(npy_save) or os.path.isfile(csv_save) \
773
+ and skip_if_present:
774
+ continue
775
+ labels,masks = get_predicted_labels(imagename,
776
+ gpu=True,
777
+ single_cell_pred_model_filepath=modelpath,
778
+ single_cell_pred_model=model,
779
+ lb=lb,
780
+ segmodel=None,
781
+ autoencoder=autoencoder,
782
+ whole_im_autoencoder=whole_im_autoencoder,
783
+ use_get_verify = True,num_dropout_tests = 10)
784
+ #np.save(npy_save,masks)
785
+ try:
786
+ np.savetxt(csv_save,labels,delimiter=",",fmt='%s')
787
+ except:
788
+ print("Couldn't save the labels")
789
+
790
+ def get_labelnames_set(label_csv):
791
+ ll = read_in_csv(label_csv)
792
+ foo = []
793
+ for row in ll:
794
+ foo.append(row[4])
795
+ return set(foo)
796
+
797
+ def get_folder_classification(folderpath,get_total=False,get_everything = False):
798
+ labelnames = glob.glob(os.path.join(folderpath,'*.csv'))
799
+ class_labels = set([])
800
+ for labelname in labelnames:
801
+ class_labels = class_labels.union(get_labelnames_set(labelname))
802
+ class_labels = sorted(list(class_labels))
803
+ all_results = {}
804
+ for labelname in labelnames:
805
+ result = get_classification_percent(labelname,class_labels,
806
+ get_total=get_total)
807
+ if True:#result["ring"] < 16:
808
+ for r in result:
809
+ if r not in all_results:
810
+ all_results[r] = []
811
+ all_results[r].append(result[r])
812
+ if get_total and get_everything:
813
+ return all_results
814
+ for r in all_results:
815
+ all_results[r] = float(np.mean(all_results[r]))
816
+ return all_results
817
+
818
+
819
+ def get_file_list_from_str(obj):
820
+ assert(isinstance(obj, str))
821
+ if os.path.isfile(obj):
822
+ if is_image_file(obj):
823
+ return [obj]
824
+ else:
825
+ return []
826
+ elif os.path.isdir(obj):
827
+ all_filename_list = []
828
+ for root, dirs, files in os.walk(obj, topdown=False):
829
+ for name in files:
830
+ filename = os.path.join(root, name)
831
+ if is_image_file(filename):
832
+ all_filename_list.append(filename)
833
+ return all_filename_list
834
+ else:
835
+ raise Exception("Invalid string input: %s" % obj)
836
+
837
+ def get_file_list_from_list(obj,allow_list_of_list=True):
838
+ assert(isinstance(obj,list))
839
+ if np.all([isinstance(_,list) for _ in obj]):
840
+ if not allow_list_of_list:
841
+ raise Exception("Cannot have nested lists")
842
+ list_of_list = []
843
+ for l in obj:
844
+ list_of_list = list_of_list + get_file_list(l,
845
+ allow_list_of_list = False)
846
+ return list_of_list
847
+ elif np.all([isinstance(_,str) for _ in obj]):
848
+ list_of_str = []
849
+ for l in obj:
850
+ list_of_str = list_of_str + get_file_list_from_str(l)
851
+ return list_of_str
852
+ else:
853
+ raise Exception("""Inputs must be strings, lists of lists,
854
+ or lists of strings""")
855
+
856
+ def get_file_list(obj,allow_list_of_list=True):
857
+ if isinstance(obj,str):
858
+ obj = get_file_list_from_str(obj)
859
+ elif isinstance(obj,list):
860
+ obj = get_file_list_from_list(obj)
861
+ else:
862
+ raise Exception("Invalid path input")
863
+ assert(isinstance(obj,list))
864
+ assert(np.all([isinstance(_,str) for _ in obj]))
865
+ if np.all([len(_) == 0 for _ in obj]):
866
+ raise Exception("No valid files found")
867
+ elif np.any([len(_) == 0 for _ in obj]):
868
+ raise Exception("One without valid files")
869
+ return obj
870
+
871
+ if __name__ == '__main__':
872
+ imagename = sys.argv[1]
873
+ output_folder = os.path.join('.','image_segment_examples')
874
+ just_segment = False
875
+ if not os.path.isdir(os.path.join('.','models')):
876
+ just_segment = True
877
+ if not os.path.isdir(output_folder):
878
+ os.mkdir(output_folder)
879
+ foldername = "%s_%s" % (os.path.basename(os.path.dirname(imagename)),
880
+ os.path.splitext(os.path.basename(imagename))[0])
881
+ image_output_folder = os.path.join(output_folder,foldername)
882
+ if not os.path.isdir(image_output_folder):
883
+ os.mkdir(image_output_folder)
884
+ labels,masks = get_predicted_labels(imagename,gpu=True,just_segment=just_segment)
885
+
886
+ np.save(os.path.join(image_output_folder,'masks.npy'),masks)
887
+ if not just_segment:
888
+ np.save(os.path.join(image_output_folder,'masks_sum.npy'),masks)
889
+ np.savetxt(os.path.join(image_output_folder,'labels.csv'),labels,
890
+ delimiter=",",fmt='%s')
891
+ copyfile(imagename,os.path.join(image_output_folder,os.path.basename(imagename)))
892
+ print(imagename)