Mercurial > repos > perssond > s3segmenter
comparison S3segmenter.py @ 2:96d0d969ebc9 draft default tip
planemo upload for repository https://github.com/goeckslab/tools-mti/tree/main/tools/s3segmenter commit 0f4f17235c5961c2fd3d4c30180507f66214c11d
author | goeckslab |
---|---|
date | Fri, 16 Sep 2022 20:05:54 +0000 |
parents | 41e8efe8df43 |
children |
comparison
equal
deleted
inserted
replaced
1:41e8efe8df43 | 2:96d0d969ebc9 |
---|---|
1 import matplotlib.pyplot as plt | |
2 import tifffile | |
3 import os | |
4 import numpy as np | |
5 from skimage import io as skio | |
6 from scipy.ndimage import * | |
7 import scipy.ndimage as ndi | |
8 from skimage.morphology import * | |
9 from skimage.morphology import extrema | |
10 from skimage import morphology | |
11 from skimage.measure import regionprops | |
12 from skimage.transform import resize | |
13 from skimage.filters import gaussian, threshold_otsu, threshold_local | |
14 from skimage.feature import peak_local_max | |
15 from skimage.color import label2rgb | |
16 from skimage.io import imsave,imread | |
17 from skimage.segmentation import clear_border, watershed, find_boundaries | |
18 from scipy.ndimage.filters import uniform_filter | |
19 from os.path import * | |
20 from os import listdir, makedirs, remove | |
21 import pickle | |
22 import shutil | |
23 import fnmatch | |
24 import cv2 | |
25 import sys | |
26 import argparse | |
27 import re | |
28 import copy | |
29 import datetime | |
30 from joblib import Parallel, delayed | |
31 from rowit import WindowView, crop_with_padding_mask | |
32 from save_tifffile_pyramid import save_pyramid | |
33 import subprocess | |
34 import ome_types | |
35 | |
36 | |
37 def imshowpair(A,B): | |
38 plt.imshow(A,cmap='Purples') | |
39 plt.imshow(B,cmap='Greens',alpha=0.5) | |
40 plt.show() | |
41 | |
42 | |
43 def imshow(A): | |
44 plt.imshow(A) | |
45 plt.show() | |
46 | |
47 def overlayOutline(outline,img): | |
48 img2 = img.copy() | |
49 stacked_img = np.stack((img2,)*3, axis=-1) | |
50 stacked_img[outline > 0] = [65535, 0, 0] | |
51 imshowpair(img2,stacked_img) | |
52 | |
53 def normI(I): | |
54 Irs=resize(I,(I.shape[0]//10,I.shape[1]//10) ); | |
55 p1 = np.percentile(Irs,10); | |
56 J = I-p1; | |
57 p99 = np.percentile(Irs,99.99); | |
58 J = J/(p99-p1); | |
59 return J | |
60 | |
61 def contour_pm_watershed( | |
62 contour_pm, sigma=2, h=0, tissue_mask=None, | |
63 padding_mask=None, min_area=None, max_area=None | |
64 ): | |
65 if tissue_mask is None: | |
66 tissue_mask = np.ones_like(contour_pm) | |
67 padded = None | |
68 if padding_mask is not None and np.any(padding_mask == 0): | |
69 contour_pm, padded = crop_with_padding_mask( | |
70 contour_pm, padding_mask, return_mask=True | |
71 ) | |
72 tissue_mask = crop_with_padding_mask( | |
73 tissue_mask, padding_mask | |
74 ) | |
75 | |
76 maxima = peak_local_max( | |
77 extrema.h_maxima( | |
78 ndi.gaussian_filter(np.invert(contour_pm), sigma=sigma), | |
79 h=h | |
80 ), | |
81 indices=False, | |
82 footprint=np.ones((3, 3)) | |
83 ) | |
84 maxima = label(maxima).astype(np.int32) | |
85 | |
86 # Passing mask into the watershed function will exclude seeds outside | |
87 # of the mask, which gives fewer and more accurate segments | |
88 maxima = watershed( | |
89 contour_pm, maxima, watershed_line=True, mask=tissue_mask | |
90 ) > 0 | |
91 | |
92 if min_area is not None and max_area is not None: | |
93 maxima = label(maxima, connectivity=1).astype(np.int32) | |
94 areas = np.bincount(maxima.ravel()) | |
95 size_passed = np.arange(areas.size)[ | |
96 np.logical_and(areas > min_area, areas < max_area) | |
97 ] | |
98 maxima *= np.isin(maxima, size_passed) | |
99 np.greater(maxima, 0, out=maxima) | |
100 | |
101 if padded is None: | |
102 return maxima.astype(np.bool) | |
103 else: | |
104 padded[padded == 1] = maxima.flatten() | |
105 return padded.astype(np.bool) | |
106 | |
107 def S3AreaSegmenter(nucleiPM, images, TMAmask, threshold,fileprefix,outputPath): | |
108 nucleiCenters = nucleiPM[:,:,0] | |
109 TMAmask= (nucleiCenters>np.amax(nucleiCenters)*0.8)*TMAmask | |
110 area = [] | |
111 area.append(np.sum(np.sum(TMAmask))) | |
112 for iChan in range(len(images)): | |
113 image_gauss = gaussian(resize(images[iChan,:,:],(int(0.25*images.shape[1]),int(0.25*images.shape[2]))),1) | |
114 if threshold ==-1: | |
115 threshold = threshold_otsu(image_gauss) | |
116 mask=resize(image_gauss>threshold,(images.shape[1],images.shape[2]),order = 0)*TMAmask | |
117 area.append(np.sum(np.sum(mask))) | |
118 os.mk | |
119 np.savetxt(outputPath + os.path.sep + fileprefix + '_area.csv',(np.transpose(np.asarray(area))),fmt='%10.5f') | |
120 return TMAmask | |
121 | |
122 def getMetadata(path,commit): | |
123 with tifffile.TiffFile(path) as tif: | |
124 if not tif.ome_metadata: | |
125 try: | |
126 x_res_tag = tif.pages[0].tags['XResolution'].value | |
127 y_res_tag = tif.pages[0].tags['YResolution'].value | |
128 physical_size_x = x_res_tag[0] / x_res_tag[1] | |
129 physical_size_y = y_res_tag[0] / y_res_tag[1] | |
130 except KeyError: | |
131 physical_size_x = 1 | |
132 physical_size_y = 1 | |
133 metadata_args = dict( | |
134 pixel_sizes=(physical_size_y, physical_size_x), | |
135 pixel_size_units=('µm', 'µm'), | |
136 software= 's3segmenter v' + commit | |
137 ) | |
138 else: | |
139 metadata=ome_types.from_xml(tif.ome_metadata) | |
140 metadata = metadata.images[0].pixels | |
141 metadata_args = dict( | |
142 pixel_sizes=(metadata.physical_size_y,metadata.physical_size_x), | |
143 pixel_size_units=('µm', 'µm'), | |
144 software= 's3segmenter v' + commit | |
145 ) | |
146 return metadata_args | |
147 | |
148 def S3NucleiBypass(nucleiPM,nucleiImage,logSigma,TMAmask,nucleiFilter,nucleiRegion): | |
149 foregroundMask = nucleiPM | |
150 P = regionprops(foregroundMask, nucleiImage) | |
151 prop_keys = ['mean_intensity', 'label','area'] | |
152 def props_of_keys(prop, keys): | |
153 return [prop[k] for k in keys] | |
154 | |
155 mean_ints, labels, areas = np.array(Parallel(n_jobs=6)(delayed(props_of_keys)(prop, prop_keys) | |
156 for prop in P | |
157 ) | |
158 ).T | |
159 del P | |
160 maxArea = (logSigma[1]**2)*3/4 | |
161 minArea = (logSigma[0]**2)*3/4 | |
162 passed = np.logical_and(areas > minArea, areas < maxArea) | |
163 | |
164 foregroundMask *= np.isin(foregroundMask, labels[passed]) | |
165 np.greater(foregroundMask, 0, out=foregroundMask) | |
166 foregroundMask = label(foregroundMask, connectivity=1).astype(np.int32) | |
167 return foregroundMask | |
168 | |
169 def S3NucleiSegmentationWatershed(nucleiPM,nucleiImage,logSigma,TMAmask,nucleiFilter,nucleiRegion): | |
170 nucleiContours = nucleiPM[:,:,1] | |
171 nucleiCenters = nucleiPM[:,:,0] | |
172 mask = resize(TMAmask,(nucleiImage.shape[0],nucleiImage.shape[1]),order = 0)>0 | |
173 if nucleiRegion == 'localThreshold' or nucleiRegion == 'localMax': | |
174 Imax = peak_local_max(extrema.h_maxima(255-nucleiContours,logSigma[0]),indices=False) | |
175 Imax = label(Imax).astype(np.int32) | |
176 foregroundMask = watershed(nucleiContours, Imax, watershed_line=True) | |
177 P = regionprops(foregroundMask, np.amax(nucleiCenters) - nucleiCenters - nucleiContours) | |
178 prop_keys = ['mean_intensity', 'label','area'] | |
179 def props_of_keys(prop, keys): | |
180 return [prop[k] for k in keys] | |
181 | |
182 mean_ints, labels, areas = np.array(Parallel(n_jobs=6)(delayed(props_of_keys)(prop, prop_keys) | |
183 for prop in P | |
184 ) | |
185 ).T | |
186 del P | |
187 maxArea = (logSigma[1]**2)*3/4 | |
188 minArea = (logSigma[0]**2)*3/4 | |
189 passed = np.logical_and.reduce(( | |
190 np.logical_and(areas > minArea, areas < maxArea), | |
191 np.less(mean_ints, 50) | |
192 )) | |
193 | |
194 foregroundMask *= np.isin(foregroundMask, labels[passed]) | |
195 np.greater(foregroundMask, 0, out=foregroundMask) | |
196 foregroundMask = label(foregroundMask, connectivity=1).astype(np.int32) | |
197 | |
198 else: | |
199 if len(logSigma)==1: | |
200 nucleiDiameter = [logSigma*0.5, logSigma*1.5] | |
201 else: | |
202 nucleiDiameter = logSigma | |
203 logMask = nucleiCenters > 150 | |
204 | |
205 win_view_setting = WindowView(nucleiContours.shape, 2000, 500) | |
206 | |
207 nucleiContours = win_view_setting.window_view_list(nucleiContours) | |
208 padding_mask = win_view_setting.padding_mask() | |
209 mask = win_view_setting.window_view_list(mask) | |
210 | |
211 maxArea = (logSigma[1]**2)*3/4 | |
212 minArea = (logSigma[0]**2)*3/4 | |
213 | |
214 foregroundMask = np.array( | |
215 Parallel(n_jobs=6)(delayed(contour_pm_watershed)( | |
216 img, sigma=logSigma[1]/30, h=logSigma[1]/30, tissue_mask=tm, | |
217 padding_mask=m, min_area=minArea, max_area=maxArea | |
218 ) for img, tm, m in zip(nucleiContours, mask, padding_mask)) | |
219 ) | |
220 | |
221 del nucleiContours, mask | |
222 | |
223 foregroundMask = win_view_setting.reconstruct(foregroundMask) | |
224 foregroundMask = label(foregroundMask, connectivity=1).astype(np.int32) | |
225 | |
226 if nucleiFilter == 'IntPM': | |
227 int_img = nucleiCenters | |
228 elif nucleiFilter == 'Int': | |
229 int_img = nucleiImage | |
230 | |
231 print(' ', datetime.datetime.now(), 'regionprops') | |
232 P = regionprops(foregroundMask, int_img) | |
233 | |
234 def props_of_keys(prop, keys): | |
235 return [prop[k] for k in keys] | |
236 | |
237 prop_keys = ['mean_intensity', 'area', 'solidity', 'label'] | |
238 mean_ints, areas, solidities, labels = np.array( | |
239 Parallel(n_jobs=6)(delayed(props_of_keys)(prop, prop_keys) | |
240 for prop in P | |
241 ) | |
242 ).T | |
243 del P | |
244 | |
245 MITh = threshold_otsu(mean_ints) | |
246 | |
247 minSolidity = 0.8 | |
248 | |
249 passed = np.logical_and.reduce(( | |
250 np.greater(mean_ints, MITh), | |
251 np.logical_and(areas > minArea, areas < maxArea), | |
252 np.greater(solidities, minSolidity) | |
253 )) | |
254 | |
255 # set failed mask label to zero | |
256 foregroundMask *= np.isin(foregroundMask, labels[passed]) | |
257 | |
258 np.greater(foregroundMask, 0, out=foregroundMask) | |
259 foregroundMask = label(foregroundMask, connectivity=1).astype(np.int32) | |
260 | |
261 return foregroundMask | |
262 | |
263 def bwmorph(mask,radius): | |
264 mask = np.array(mask,dtype=np.uint8) | |
265 #labels = label(mask) | |
266 background = nucleiMask == 0 | |
267 distances, (i, j) = distance_transform_edt(background, return_indices=True) | |
268 cellMask = nucleiMask.copy() | |
269 finalmask = background & (distances <= radius) | |
270 cellMask[finalmask] = nucleiMask[i[finalmask], j[finalmask]] | |
271 | |
272 # imshowpair(cellMask,mask) | |
273 return cellMask | |
274 # imshow(fg) | |
275 # fg = cv2.dilate(mask,ndimage.generate_binary_structure(2, 2)) | |
276 # bg = 1-fg-mask | |
277 # imshowpair(bg,mask) | |
278 | |
279 def S3CytoplasmSegmentation(nucleiMask,cyto,mask,cytoMethod='distanceTransform',radius = 5): | |
280 mask = (nucleiMask + resize(mask,(nucleiMask.shape[0],nucleiMask.shape[1]),order=0))>0 | |
281 gdist = distance_transform_edt(1-(nucleiMask>0)) | |
282 if cytoMethod == 'distanceTransform': | |
283 mask = np.array(mask,dtype=np.uint32) | |
284 markers= nucleiMask | |
285 elif cytoMethod == 'hybrid': | |
286 cytoBlur = gaussian(cyto,2) | |
287 c1 = uniform_filter(cytoBlur, 3, mode='reflect') | |
288 c2 = uniform_filter(cytoBlur*cytoBlur, 3, mode='reflect') | |
289 grad = np.sqrt(c2 - c1*c1)*np.sqrt(9./8) | |
290 grad[np.isnan(grad)]=0 | |
291 gdist= np.sqrt(np.square(grad) + 0.000001*np.amax(grad)/np.amax(gdist)*np.square(gdist)) | |
292 bg = binary_erosion(np.invert(mask),morphology.selem.disk(radius, np.uint8)) | |
293 markers=nucleiMask.copy() | |
294 markers[bg==1] = np.amax(nucleiMask)+1 | |
295 markers = label(markers>0,connectivity=1) | |
296 mask = np.ones(nucleiMask.shape) | |
297 del bg | |
298 elif cytoMethod == 'ring': | |
299 # mask =np.array(bwmorph(nucleiMask,radius)*mask,dtype=np.uint32)>0 | |
300 mask =np.array(bwmorph(nucleiMask,radius),dtype=np.uint32)>0 | |
301 markers= nucleiMask | |
302 | |
303 cellMask =clear_border(watershed(gdist,markers,watershed_line=True)) | |
304 del gdist, markers, cyto | |
305 cellMask = np.array(cellMask*mask,dtype=np.uint32) | |
306 | |
307 finalCellMask = np.zeros(cellMask.shape,dtype=np.uint32) | |
308 P = regionprops(label(cellMask>0,connectivity=1),nucleiMask>0,cache=False) | |
309 count=0 | |
310 for props in P: | |
311 if props.max_intensity>0 : | |
312 count += 1 | |
313 yi = props.coords[:, 0] | |
314 xi = props.coords[:, 1] | |
315 finalCellMask[yi, xi] = count | |
316 nucleiMask = np.array(nucleiMask>0,dtype=np.uint32) | |
317 nucleiMask = finalCellMask*nucleiMask | |
318 cytoplasmMask = np.subtract(finalCellMask,nucleiMask) | |
319 return cytoplasmMask,nucleiMask,finalCellMask | |
320 | |
321 def exportMasks(mask,image,outputPath,filePrefix,fileName,commit,metadata_args,saveFig=True,saveMasks = True): | |
322 outputPath =outputPath + os.path.sep + filePrefix | |
323 if not os.path.exists(outputPath): | |
324 os.makedirs(outputPath) | |
325 previewPath = outputPath + os.path.sep + 'qc' | |
326 if not os.path.exists(previewPath): | |
327 os.makedirs(previewPath) | |
328 | |
329 if saveMasks ==True: | |
330 save_pyramid( | |
331 mask, | |
332 outputPath + os.path.sep + fileName + '.ome.tif', | |
333 channel_names=fileName, | |
334 is_mask=True, | |
335 **metadata_args | |
336 ) | |
337 if saveFig== True: | |
338 mask=np.uint8(mask>0) | |
339 edges = find_boundaries(mask,mode = 'outer') | |
340 stacked_img=np.stack((np.uint16(edges)*np.amax(image),image),axis=0) | |
341 save_pyramid( | |
342 stacked_img, | |
343 previewPath + os.path.sep + fileName + 'Outlines.ome.tif', | |
344 channel_names=[f'{fileName} outlines', 'Segmentation image'], | |
345 is_mask=False, | |
346 **metadata_args | |
347 ) | |
348 | |
349 def S3punctaDetection(spotChan,mask,sigma,SD): | |
350 Ilog = -gaussian_laplace(np.float32(spotChan),sigma) | |
351 tissueMask = spotChan >0 | |
352 fgm=peak_local_max(Ilog, indices=False,footprint=np.ones((3, 3)))*tissueMask | |
353 test=Ilog[fgm==1] | |
354 med = np.median(test) | |
355 mad = np.median(np.absolute(test - med)) | |
356 thresh = med + 1.4826*SD*mad | |
357 return (Ilog>thresh)*fgm*(mask>0) | |
358 | |
359 | |
360 | |
361 if __name__ == '__main__': | |
362 parser=argparse.ArgumentParser() | |
363 parser.add_argument("--imagePath") | |
364 parser.add_argument("--contoursClassProbPath",default ='') | |
365 parser.add_argument("--nucleiClassProbPath",default ='') | |
366 parser.add_argument("--stackProbPath",default ='') | |
367 parser.add_argument("--outputPath") | |
368 parser.add_argument("--dearrayPath") | |
369 parser.add_argument("--maskPath") | |
370 parser.add_argument("--probMapChan",type = int, default = -1) | |
371 parser.add_argument("--mask",choices=['TMA', 'tissue','none'],default = 'tissue') | |
372 parser.add_argument("--crop",choices=['interactiveCrop','autoCrop','noCrop','dearray','plate'], default = 'noCrop') | |
373 parser.add_argument("--cytoMethod",choices=['hybrid','distanceTransform','bwdistanceTransform','ring'],default = 'distanceTransform') | |
374 parser.add_argument("--nucleiFilter",choices=['IntPM','LoG','Int','none'],default = 'IntPM') | |
375 parser.add_argument("--nucleiRegion",choices=['watershedContourDist','watershedContourInt','watershedBWDist','dilation','localThreshold','localMax','bypass','pixellevel'], default = 'watershedContourInt') | |
376 parser.add_argument("--pixelThreshold",type = float, default = -1) | |
377 parser.add_argument("--segmentCytoplasm",choices = ['segmentCytoplasm','ignoreCytoplasm'],default = 'ignoreCytoplasm') | |
378 parser.add_argument("--cytoDilation",type = int, default = 5) | |
379 parser.add_argument("--logSigma",type = int, nargs = '+', default = [3, 60]) | |
380 parser.add_argument("--CytoMaskChan",type=int, nargs = '+', default=[2]) | |
381 parser.add_argument("--pixelMaskChan",type=int, nargs = '+', default=[2]) | |
382 parser.add_argument("--TissueMaskChan",type=int, nargs = '+', default=0) | |
383 parser.add_argument("--detectPuncta",type=int, nargs = '+', default=[0]) | |
384 parser.add_argument("--punctaSigma", nargs = '+', type=float, default=[0]) | |
385 parser.add_argument("--punctaSD", nargs = '+', type=float, default=[4]) | |
386 parser.add_argument("--saveMask",action='store_false') | |
387 parser.add_argument("--saveFig",action='store_false') | |
388 args = parser.parse_args() | |
389 | |
390 imagePath = args.imagePath | |
391 outputPath = args.outputPath | |
392 nucleiClassProbPath = args.nucleiClassProbPath | |
393 contoursClassProbPath = args.contoursClassProbPath | |
394 stackProbPath = args.stackProbPath | |
395 maskPath = args.maskPath | |
396 | |
397 commit = '1.3.11'#subprocess.check_output(['git', 'describe', '--tags']).decode('ascii').strip() | |
398 metadata = getMetadata(imagePath,commit) | |
399 | |
400 fileName = os.path.basename(imagePath) | |
401 filePrefix = fileName[0:fileName.index('.')] | |
402 | |
403 # convert 1-based indexing to 0-based indexing | |
404 CytoMaskChan = args.CytoMaskChan | |
405 CytoMaskChan[:] = [number - 1 for number in CytoMaskChan] | |
406 pixelMaskChan = args.pixelMaskChan | |
407 pixelMaskChan[:] = [number - 1 for number in pixelMaskChan] | |
408 | |
409 | |
410 if not os.path.exists(outputPath): | |
411 os.makedirs(outputPath) | |
412 | |
413 | |
414 # get channel used for nuclei segmentation | |
415 | |
416 if len(contoursClassProbPath)>0: | |
417 legacyMode = 1 | |
418 probPrefix = os.path.basename(contoursClassProbPath) | |
419 nucMaskChan = int(probPrefix.split('ContoursPM_')[1].split('.')[0]) | |
420 elif len(stackProbPath)>0: | |
421 legacyMode = 0 | |
422 probPrefix = os.path.basename(stackProbPath) | |
423 else: | |
424 print('NO PROBABILITY MAP PROVIDED') | |
425 | |
426 if args.probMapChan==-1: | |
427 print('Using first channel by default!') | |
428 nucMaskChan = 0 | |
429 else: | |
430 nucMaskChan = args.probMapChan | |
431 nucMaskChan = nucMaskChan -1 #convert 1-based indexing to 0-based indexing | |
432 | |
433 if args.TissueMaskChan==0: | |
434 TissueMaskChan = copy.copy(CytoMaskChan) | |
435 TissueMaskChan.append(nucMaskChan) | |
436 else: | |
437 TissueMaskChan = args.TissueMaskChan[:] | |
438 TissueMaskChan[:] = [number - 1 for number in TissueMaskChan]#convert 1-based indexing to 0-based indexing | |
439 TissueMaskChan.append(nucMaskChan) | |
440 | |
441 #crop images if needed | |
442 if args.crop == 'interactiveCrop': | |
443 nucleiCrop = tifffile.imread(imagePath,key = nucMaskChan) | |
444 r=cv2.selectROI(resize(nucleiCrop,(nucleiCrop.shape[0] // 30, nucleiCrop.shape[1] // 30))) | |
445 cv2.destroyWindow('select') | |
446 rect=np.transpose(r)*30 | |
447 PMrect= [rect[1], rect[0], rect[3], rect[2]] | |
448 nucleiCrop = nucleiCrop[int(rect[1]):int(rect[1]+rect[3]), int(rect[0]):int(rect[0]+rect[2])] | |
449 elif args.crop == 'noCrop' or args.crop == 'dearray' or args.crop == 'plate': | |
450 nucleiCrop = tifffile.imread(imagePath,key = nucMaskChan) | |
451 rect = [0, 0, nucleiCrop.shape[0], nucleiCrop.shape[1]] | |
452 PMrect= rect | |
453 elif args.crop == 'autoCrop': | |
454 nucleiCrop = tifffile.imread(imagePath,key = nucMaskChan) | |
455 rect = [np.round(nucleiCrop.shape[0]/3), np.round(nucleiCrop.shape[1]/3),np.round(nucleiCrop.shape[0]/3), np.round(nucleiCrop.shape[1]/3)] | |
456 PMrect= rect | |
457 nucleiCrop = nucleiCrop[int(rect[0]):int(rect[0]+rect[2]), int(rect[1]):int(rect[1]+rect[3])] | |
458 | |
459 if legacyMode ==1: | |
460 nucleiProbMaps = tifffile.imread(nucleiClassProbPath,key=0) | |
461 nucleiPM = nucleiProbMaps[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])] | |
462 nucleiProbMaps = tifffile.imread(contoursClassProbPath,key=0) | |
463 PMSize = nucleiProbMaps.shape | |
464 nucleiPM = np.dstack((nucleiPM,nucleiProbMaps[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])])) | |
465 else: | |
466 nucleiProbMaps = imread(stackProbPath) | |
467 if len(nucleiProbMaps.shape)==2: | |
468 nucleiPM = nucleiProbMaps[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])] | |
469 else: | |
470 nucleiPM = nucleiProbMaps[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3]),:] | |
471 PMSize = nucleiProbMaps.shape | |
472 | |
473 # mask the core/tissue | |
474 if args.crop == 'dearray': | |
475 TMAmask = tifffile.imread(maskPath) | |
476 elif args.crop =='plate': | |
477 TMAmask = np.ones(nucleiCrop.shape) | |
478 | |
479 else: | |
480 tissue = np.empty((len(TissueMaskChan),nucleiCrop.shape[0],nucleiCrop.shape[1]),dtype=np.uint16) | |
481 count=0 | |
482 if args.crop == 'noCrop': | |
483 for iChan in TissueMaskChan: | |
484 tissueCrop =tifffile.imread(imagePath,key=iChan) | |
485 tissue_gauss = gaussian(tissueCrop,1) | |
486 #tissue_gauss[tissue_gauss==0]=np.nan | |
487 tissue[count,:,:] =np.log2(tissue_gauss+1)>threshold_otsu(np.log2(tissue_gauss+1)) | |
488 count+=1 | |
489 else: | |
490 for iChan in TissueMaskChan: | |
491 tissueCrop = tifffile.imread(imagePath,key=iChan) | |
492 tissue_gauss = gaussian(tissueCrop[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])],1) | |
493 tissue[count,:,:] = np.log2(tissue_gauss+1)>threshold_otsu(np.log2(tissue_gauss+1)) | |
494 count+=1 | |
495 TMAmask = np.max(tissue,axis = 0) | |
496 | |
497 | |
498 del tissue_gauss, tissue | |
499 | |
500 # nuclei segmentation | |
501 if args.nucleiRegion == 'pixellevel': | |
502 pixelTissue = np.empty((len(pixelMaskChan),nucleiCrop.shape[0],nucleiCrop.shape[1]),dtype=np.uint16) | |
503 count=0 | |
504 for iChan in pixelMaskChan: | |
505 pixelCrop = tifffile.imread(imagePath,key=iChan) | |
506 pixelTissue[count,:,:] = pixelCrop[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])] | |
507 count+=1 | |
508 nucleiMask = S3AreaSegmenter(nucleiPM, pixelTissue, TMAmask,args.pixelThreshold,filePrefix,outputPath) | |
509 elif args.nucleiRegion == 'bypass': | |
510 nucleiMask = S3NucleiBypass(nucleiPM,nucleiCrop,args.logSigma,TMAmask,args.nucleiFilter,args.nucleiRegion) | |
511 else: | |
512 nucleiMask = S3NucleiSegmentationWatershed(nucleiPM,nucleiCrop,args.logSigma,TMAmask,args.nucleiFilter,args.nucleiRegion) | |
513 del nucleiPM | |
514 # cytoplasm segmentation | |
515 if args.segmentCytoplasm == 'segmentCytoplasm': | |
516 count =0 | |
517 if args.crop == 'noCrop' or args.crop == 'dearray' or args.crop == 'plate': | |
518 cyto=np.empty((len(CytoMaskChan),nucleiCrop.shape[0],nucleiCrop.shape[1]),dtype=np.uint16) | |
519 for iChan in CytoMaskChan: | |
520 cyto[count,:,:] = tifffile.imread(imagePath, key=iChan) | |
521 count+=1 | |
522 elif args.crop =='autoCrop': | |
523 cyto=np.empty((len(CytoMaskChan),int(rect[2]),int(rect[3])),dtype=np.int16) | |
524 for iChan in CytoMaskChan: | |
525 cytoFull= tifffile.imread(imagePath, key=iChan) | |
526 cyto[count,:,:] = cytoFull[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])] | |
527 count+=1 | |
528 else: | |
529 cyto=np.empty((len(CytoMaskChan),rect[3],rect[2]),dtype=np.int16) | |
530 for iChan in CytoMaskChan: | |
531 cytoFull= tifffile.imread(imagePath, key=iChan) | |
532 cyto[count,:,:] = cytoFull[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])] | |
533 count+=1 | |
534 cyto = np.amax(cyto,axis=0) | |
535 cytoplasmMask,nucleiMaskTemp,cellMask = S3CytoplasmSegmentation(nucleiMask,cyto,TMAmask,args.cytoMethod,args.cytoDilation) | |
536 exportMasks(nucleiMaskTemp,nucleiCrop,outputPath,filePrefix,'nuclei',commit,metadata,args.saveFig,args.saveMask) | |
537 exportMasks(cytoplasmMask,cyto,outputPath,filePrefix,'cyto',commit,metadata,args.saveFig,args.saveMask) | |
538 exportMasks(cellMask,cyto,outputPath,filePrefix,'cell',commit,metadata,args.saveFig,args.saveMask) | |
539 | |
540 cytoplasmMask,nucleiMaskTemp,cellMask = S3CytoplasmSegmentation(nucleiMask,cyto,TMAmask,'ring',args.cytoDilation) | |
541 exportMasks(nucleiMaskTemp,nucleiCrop,outputPath,filePrefix,'nucleiRing',commit,metadata,args.saveFig,args.saveMask) | |
542 exportMasks(cytoplasmMask,cyto,outputPath,filePrefix,'cytoRing',commit,metadata,args.saveFig,args.saveMask) | |
543 exportMasks(cellMask,cyto,outputPath,filePrefix,'cellRing',commit,metadata,args.saveFig,args.saveMask) | |
544 | |
545 elif args.segmentCytoplasm == 'ignoreCytoplasm': | |
546 exportMasks(nucleiMask,nucleiCrop,outputPath,filePrefix,'nuclei',commit,metadata) | |
547 cellMask = nucleiMask | |
548 exportMasks(nucleiMask,nucleiCrop,outputPath,filePrefix,'cell',commit,metadata) | |
549 cytoplasmMask = nucleiMask | |
550 | |
551 detectPuncta = args.detectPuncta | |
552 if (np.min(detectPuncta)>0): | |
553 detectPuncta[:] = [number - 1 for number in detectPuncta] #convert 1-based indexing to 0-based indexing | |
554 if len(detectPuncta) != len(args.punctaSigma): | |
555 args.punctaSigma = args.punctaSigma[0] * np.ones(len(detectPuncta)) | |
556 | |
557 | |
558 if len(detectPuncta) != len(args.punctaSD): | |
559 args.punctaSD = args.punctaSD[0] * np.ones(len(detectPuncta)) | |
560 | |
561 counter=0 | |
562 for iPunctaChan in detectPuncta: | |
563 punctaChan = tifffile.imread(imagePath,key = iPunctaChan) | |
564 punctaChan = punctaChan[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])] | |
565 spots=S3punctaDetection(punctaChan,cellMask,args.punctaSigma[counter],args.punctaSD[counter]) | |
566 cellspotmask = nucleiMask | |
567 P = regionprops(cellspotmask,intensity_image = spots ,cache=False) | |
568 numSpots = [] | |
569 for prop in P: | |
570 numSpots.append(np.uint16(np.round(prop.mean_intensity * prop.area))) | |
571 np.savetxt(outputPath + os.path.sep + 'numSpots_chan' + str(iPunctaChan+1) +'.csv',(np.transpose(np.asarray(numSpots))),fmt='%10.5f') | |
572 edges = 1-(cellMask>0) | |
573 stacked_img=np.stack((np.uint16((spots+edges)>0)*np.amax(punctaChan),punctaChan),axis=0) | |
574 | |
575 | |
576 outputPathPuncta = outputPath + os.path.sep + filePrefix + os.path.sep + 'punctaChan'+str(iPunctaChan+1) + 'Outlines.ome.tif' | |
577 | |
578 # metadata_args = dict( | |
579 # pixel_sizes=(metadata.physical_size_y, metadata.physical_size_x), | |
580 # pixel_size_units=('µm', 'µm'), | |
581 # software= 's3segmenter v' + commit | |
582 # ) | |
583 save_pyramid( | |
584 stacked_img, | |
585 outputPathPuncta, | |
586 channel_names=['puncta outlines', 'image channel'], | |
587 is_mask=False, | |
588 **metadata | |
589 ) | |
590 | |
591 counter=counter+1 | |
592 |