Mercurial > repos > perssond > s3segmenter
changeset 0:37acf42a824b draft
"planemo upload for repository https://github.com/ohsu-comp-bio/S3segmenter commit d89a61efd4c465a1e6bf5b99b0f78fb19be5bdea-dirty"
author | perssond |
---|---|
date | Fri, 12 Mar 2021 00:18:40 +0000 |
parents | |
children | 41e8efe8df43 |
files | S3segmenter.py macros.xml rowit.py s3segmenter.xml |
diffstat | 4 files changed, 884 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/S3segmenter.py Fri Mar 12 00:18:40 2021 +0000 @@ -0,0 +1,629 @@ +import matplotlib.pyplot as plt +import tifffile +import os +import numpy as np +from skimage import io as skio +from scipy.ndimage import * +import scipy.ndimage as ndi +from skimage.morphology import * +from skimage.morphology import extrema +from skimage import morphology +from skimage.measure import regionprops +from skimage.transform import resize +from skimage.filters import gaussian, threshold_otsu, threshold_local +from skimage.feature import peak_local_max +from skimage.color import label2rgb +from skimage.io import imsave,imread +from skimage.segmentation import clear_border, watershed, find_boundaries +from scipy.ndimage.filters import uniform_filter +from os.path import * +from os import listdir, makedirs, remove +import pickle +import shutil +import fnmatch +import cv2 +import sys +import argparse +import re +import copy +import datetime +from joblib import Parallel, delayed +from rowit import WindowView, crop_with_padding_mask + + +def imshowpair(A,B): + plt.imshow(A,cmap='Purples') + plt.imshow(B,cmap='Greens',alpha=0.5) + plt.show() + + +def imshow(A): + plt.imshow(A) + plt.show() + +def overlayOutline(outline,img): + img2 = img.copy() + stacked_img = np.stack((img2,)*3, axis=-1) + stacked_img[outline > 0] = [65535, 0, 0] + imshowpair(img2,stacked_img) + +def normI(I): + Irs=resize(I,(I.shape[0]//10,I.shape[1]//10) ); + p1 = np.percentile(Irs,10); + J = I-p1; + p99 = np.percentile(Irs,99.99); + J = J/(p99-p1); + return J + +def contour_pm_watershed( + contour_pm, sigma=2, h=0, tissue_mask=None, + padding_mask=None, min_area=None, max_area=None +): + if tissue_mask is None: + tissue_mask = np.ones_like(contour_pm) + padded = None + if padding_mask is not None and np.any(padding_mask == 0): + contour_pm, padded = crop_with_padding_mask( + contour_pm, padding_mask, return_mask=True + ) + tissue_mask = crop_with_padding_mask( + tissue_mask, padding_mask + ) + + maxima = peak_local_max( + extrema.h_maxima( + ndi.gaussian_filter(np.invert(contour_pm), sigma=sigma), + h=h + ), + indices=False, + footprint=np.ones((3, 3)) + ) + maxima = label(maxima).astype(np.int32) + + # Passing mask into the watershed function will exclude seeds outside + # of the mask, which gives fewer and more accurate segments + maxima = watershed( + contour_pm, maxima, watershed_line=True, mask=tissue_mask + ) > 0 + + if min_area is not None and max_area is not None: + maxima = label(maxima, connectivity=1).astype(np.int32) + areas = np.bincount(maxima.ravel()) + size_passed = np.arange(areas.size)[ + np.logical_and(areas > min_area, areas < max_area) + ] + maxima *= np.isin(maxima, size_passed) + np.greater(maxima, 0, out=maxima) + + if padded is None: + return maxima.astype(np.bool) + else: + padded[padded == 1] = maxima.flatten() + return padded.astype(np.bool) + +def S3AreaSegmenter(nucleiPM, images, TMAmask, threshold,outputPath): + nucleiCenters = nucleiPM[:,:,0] + TMAmask= (nucleiCenters>np.amax(nucleiCenters)*0.8)*TMAmask + area = [] + area.append(np.sum(np.sum(TMAmask))) + for iChan in range(len(images)): + image_gauss = gaussian(resize(images[iChan,:,:],(int(0.25*images.shape[1]),int(0.25*images.shape[2]))),1) + if threshold ==-1: + threshold = threshold_otsu(image_gauss) + mask=resize(image_gauss>threshold,(images.shape[1],images.shape[2]),order = 0)*TMAmask + area.append(np.sum(np.sum(mask))) + np.savetxt(outputPath + os.path.sep + 'area.csv',(np.transpose(np.asarray(area))),fmt='%10.5f') + return TMAmask + + +def S3NucleiSegmentationWatershed(nucleiPM,nucleiImage,logSigma,TMAmask,nucleiFilter,nucleiRegion): + nucleiContours = nucleiPM[:,:,1] + nucleiCenters = nucleiPM[:,:,0] + + mask = resize(TMAmask,(nucleiImage.shape[0],nucleiImage.shape[1]),order = 0)>0 + + if nucleiRegion == 'localThreshold': + Imax = peak_local_max(extrema.h_maxima(255-nucleiContours,logSigma[0]),indices=False) + Imax = label(Imax).astype(np.int32) + foregroundMask = watershed(nucleiContours, Imax, watershed_line=True) + P = regionprops(foregroundMask, np.amax(nucleiCenters) - nucleiCenters - nucleiContours) + prop_keys = ['mean_intensity', 'label','area'] + def props_of_keys(prop, keys): + return [prop[k] for k in keys] + + mean_ints, labels, areas = np.array(Parallel(n_jobs=6)(delayed(props_of_keys)(prop, prop_keys) + for prop in P + ) + ).T + del P + maxArea = (logSigma[1]**2)*3/4 + minArea = (logSigma[0]**2)*3/4 + passed = np.logical_and.reduce(( + np.logical_and(areas > minArea, areas < maxArea), + np.less(mean_ints, 50) + )) + + foregroundMask *= np.isin(foregroundMask, labels[passed]) + np.greater(foregroundMask, 0, out=foregroundMask) + foregroundMask = label(foregroundMask, connectivity=1).astype(np.int32) + elif nucleiRegion =='bypass': + foregroundMask = nucleiPM[:,:,0] + P = regionprops(foregroundMask, nucleiImage) + prop_keys = ['mean_intensity', 'label','area'] + def props_of_keys(prop, keys): + return [prop[k] for k in keys] + + mean_ints, labels, areas = np.array(Parallel(n_jobs=6)(delayed(props_of_keys)(prop, prop_keys) + for prop in P + ) + ).T + del P + maxArea = (logSigma[1]**2)*3/4 + minArea = (logSigma[0]**2)*3/4 + passed = np.logical_and(areas > minArea, areas < maxArea) + + foregroundMask *= np.isin(foregroundMask, labels[passed]) + np.greater(foregroundMask, 0, out=foregroundMask) + foregroundMask = label(foregroundMask, connectivity=1).astype(np.int32) + else: + + if len(logSigma)==1: + nucleiDiameter = [logSigma*0.5, logSigma*1.5] + else: + nucleiDiameter = logSigma + logMask = nucleiCenters > 150 + + win_view_setting = WindowView(nucleiContours.shape, 2000, 500) + + nucleiContours = win_view_setting.window_view_list(nucleiContours) + padding_mask = win_view_setting.padding_mask() + mask = win_view_setting.window_view_list(mask) + + maxArea = (logSigma[1]**2)*3/4 + minArea = (logSigma[0]**2)*3/4 + + foregroundMask = np.array( + Parallel(n_jobs=6)(delayed(contour_pm_watershed)( + img, sigma=logSigma[1]/30, h=logSigma[1]/30, tissue_mask=tm, + padding_mask=m, min_area=minArea, max_area=maxArea + ) for img, tm, m in zip(nucleiContours, mask, padding_mask)) + ) + + del nucleiContours, mask + + foregroundMask = win_view_setting.reconstruct(foregroundMask) + foregroundMask = label(foregroundMask, connectivity=1).astype(np.int32) + + if nucleiFilter == 'IntPM': + int_img = nucleiCenters + elif nucleiFilter == 'Int': + int_img = nucleiImage + + print(' ', datetime.datetime.now(), 'regionprops') + P = regionprops(foregroundMask, int_img) + + def props_of_keys(prop, keys): + return [prop[k] for k in keys] + + prop_keys = ['mean_intensity', 'area', 'solidity', 'label'] + mean_ints, areas, solidities, labels = np.array( + Parallel(n_jobs=6)(delayed(props_of_keys)(prop, prop_keys) + for prop in P + ) + ).T + del P + + MITh = threshold_otsu(mean_ints) + + minSolidity = 0.8 + + passed = np.logical_and.reduce(( + np.greater(mean_ints, MITh), + np.logical_and(areas > minArea, areas < maxArea), + np.greater(solidities, minSolidity) + )) + + # set failed mask label to zero + foregroundMask *= np.isin(foregroundMask, labels[passed]) + + np.greater(foregroundMask, 0, out=foregroundMask) + foregroundMask = label(foregroundMask, connectivity=1).astype(np.int32) + + return foregroundMask + +def bwmorph(mask,radius): + mask = np.array(mask,dtype=np.uint8) + #labels = label(mask) + background = nucleiMask == 0 + distances, (i, j) = distance_transform_edt(background, return_indices=True) + cellMask = nucleiMask.copy() + finalmask = background & (distances <= radius) + cellMask[finalmask] = nucleiMask[i[finalmask], j[finalmask]] + +# imshowpair(cellMask,mask) + return cellMask +# imshow(fg) +# fg = cv2.dilate(mask,ndimage.generate_binary_structure(2, 2)) +# bg = 1-fg-mask +# imshowpair(bg,mask) + +def S3CytoplasmSegmentation(nucleiMask,cyto,mask,cytoMethod='distanceTransform',radius = 5): + mask = (nucleiMask + resize(mask,(nucleiMask.shape[0],nucleiMask.shape[1]),order=0))>0 + gdist = distance_transform_edt(1-(nucleiMask>0)) + if cytoMethod == 'distanceTransform': + mask = np.array(mask,dtype=np.uint32) + markers= nucleiMask + elif cytoMethod == 'hybrid': + cytoBlur = gaussian(cyto,2) + c1 = uniform_filter(cytoBlur, 3, mode='reflect') + c2 = uniform_filter(cytoBlur*cytoBlur, 3, mode='reflect') + grad = np.sqrt(c2 - c1*c1)*np.sqrt(9./8) + grad[np.isnan(grad)]=0 + gdist= np.sqrt(np.square(grad) + 0.000001*np.amax(grad)/np.amax(gdist)*np.square(gdist)) + bg = binary_erosion(np.invert(mask),morphology.selem.disk(radius, np.uint8)) + markers=nucleiMask.copy() + markers[bg==1] = np.amax(nucleiMask)+1 + markers = label(markers>0,connectivity=1) + mask = np.ones(nucleiMask.shape) + del bg + elif cytoMethod == 'ring': +# mask =np.array(bwmorph(nucleiMask,radius)*mask,dtype=np.uint32)>0 + mask =np.array(bwmorph(nucleiMask,radius),dtype=np.uint32)>0 + markers= nucleiMask + + cellMask =clear_border(watershed(gdist,markers,watershed_line=True)) + del gdist, markers, cyto + cellMask = np.array(cellMask*mask,dtype=np.uint32) + + finalCellMask = np.zeros(cellMask.shape,dtype=np.uint32) + P = regionprops(label(cellMask>0,connectivity=1),nucleiMask>0,cache=False) + count=0 + for props in P: + if props.max_intensity>0 : + count += 1 + yi = props.coords[:, 0] + xi = props.coords[:, 1] + finalCellMask[yi, xi] = count + nucleiMask = np.array(nucleiMask>0,dtype=np.uint32) + nucleiMask = finalCellMask*nucleiMask + cytoplasmMask = np.subtract(finalCellMask,nucleiMask) + return cytoplasmMask,nucleiMask,finalCellMask + +def exportMasks(mask,image,outputPath,filePrefix,fileName,saveFig=True,saveMasks = True): + outputPath =outputPath + os.path.sep + filePrefix + if not os.path.exists(outputPath): + os.makedirs(outputPath) + if saveMasks ==True: + kwargs={} + kwargs['bigtiff'] = True + kwargs['photometric'] = 'minisblack' + resolution = np.round(1) + kwargs['resolution'] = (resolution, resolution, 'cm') + kwargs['metadata'] = None + kwargs['description'] = '!!xml!!' + imsave(outputPath + os.path.sep + fileName + 'Mask.tif',mask, plugin="tifffile") + + if saveFig== True: + mask=np.uint8(mask>0) + edges = find_boundaries(mask,mode = 'outer') + stacked_img=np.stack((np.uint16(edges)*np.amax(image),image),axis=0) + tifffile.imsave(outputPath + os.path.sep + fileName + 'Outlines.tif',stacked_img) + +def S3punctaDetection(spotChan,mask,sigma,SD): + Ilog = -gaussian_laplace(np.float32(spotChan),sigma) + fgm=peak_local_max(Ilog, indices=False,footprint=np.ones((3, 3))) + test=Ilog[fgm==1] + med = np.median(test) + mad = np.median(np.absolute(test - med)) + thresh = med + 1.4826*SD*mad + return (Ilog>thresh)*fgm*(mask>0) + +# stacked_img=np.stack((spots*4095,nucleiCrop),axis=0) +# tifffile.imsave('D:/Seidman/ZeissTest Sets/registration/spottest.tif',stacked_img) + + + # assign nan to tissue mask + + +if __name__ == '__main__': + parser=argparse.ArgumentParser() + parser.add_argument("--imagePath") + parser.add_argument("--contoursClassProbPath",default ='') + parser.add_argument("--nucleiClassProbPath",default ='') + parser.add_argument("--stackProbPath",default ='') + parser.add_argument("--outputPath") + parser.add_argument("--dearrayPath") + parser.add_argument("--maskPath") + parser.add_argument("--probMapChan",type = int, default = -1) + parser.add_argument("--mask",choices=['TMA', 'tissue','none'],default = 'tissue') + parser.add_argument("--crop",choices=['interactiveCrop','autoCrop','noCrop','dearray','plate'], default = 'noCrop') + parser.add_argument("--cytoMethod",choices=['hybrid','distanceTransform','bwdistanceTransform','ring'],default = 'distanceTransform') + parser.add_argument("--nucleiFilter",choices=['IntPM','LoG','Int','none'],default = 'IntPM') + parser.add_argument("--nucleiRegion",choices=['watershedContourDist','watershedContourInt','watershedBWDist','dilation','localThreshold','bypass','pixellevel'], default = 'watershedContourInt') + parser.add_argument("--pixelThreshold",type = float, default = -1) + parser.add_argument("--segmentCytoplasm",choices = ['segmentCytoplasm','ignoreCytoplasm'],default = 'ignoreCytoplasm') + parser.add_argument("--cytoDilation",type = int, default = 5) + parser.add_argument("--logSigma",type = int, nargs = '+', default = [3, 60]) + parser.add_argument("--CytoMaskChan",type=int, nargs = '+', default=[1]) + parser.add_argument("--pixelMaskChan",type=int, nargs = '+', default=[1]) + parser.add_argument("--TissueMaskChan",type=int, nargs = '+', default=-1) + parser.add_argument("--detectPuncta",type=int, nargs = '+', default=[-1]) + parser.add_argument("--punctaSigma", nargs = '+', type=float, default=[1]) + parser.add_argument("--punctaSD", nargs = '+', type=float, default=[4]) + parser.add_argument("--saveMask",action='store_false') + parser.add_argument("--saveFig",action='store_false') + args = parser.parse_args() + + # gather filename information + #exemplar001 +# imagePath = 'D:/LSP/cycif/testsets/exemplar-001/registration/exemplar-001small.ome.tif' +# outputPath = 'D:/LSP/cycif/testsets/exemplar-001/segmentation' +## nucleiClassProbPath = 'D:/LSP/cycif/testsets/exemplar-001/probability_maps/exemplar-001_NucleiPM_0.tif' +## contoursClassProbPath = 'D:/LSP/cycif/testsets/exemplar-001/probability_maps/exemplar-001_ContoursPM_0.tif' +# contoursClassProbPath ='' +# stackProbPath = 'D:/LSP/cycif/testsets/exemplar-001_Probabilities_0.tif' +# maskPath = 'D:/LSP/cycif/testsets/exemplar-001/dearray/masks/A1_mask.tif' +# args.cytoMethod = 'hybrid' +# args.nucleiRegion = 'localThreshold' +# args.segmentCytoplasm = 'segmentCytoplasm' + +# exemplar002 +# imagePath = 'D:/LSP/cycif/testsets/exemplar-002/dearray/1.tif' +# outputPath = 'D:/LSP/cycif/testsets/exemplar-002/segmentation' +# nucleiClassProbPath = ''#'D:/LSP/cycif/testsets/exemplar-002/prob_map/1_NucleiPM_1.tif' +# contoursClassProbPath = ''#'D:/LSP/cycif/testsets/exemplar-002/prob_map/1_ContoursPM_1.tif' +# stackProbPath = 'D:/LSP/cycif/testsets/exemplar-002/probability-maps/unmicst_1new_Probabilities_1.tif' +# maskPath = 'D:/LSP/cycif/testsets/exemplar-002/dearrayPython/masks/1_mask.tif' +# args.probMapChan =1 +# args.cytoMethod = 'hybrid' +# args.mask = 'TMA' +# args.crop = 'dearray' +# args.crop = 'autoCrop' +# args.segmentCytoplasm = 'segmentCytoplasm' + #PTCL +# imagePath = 'D:/LSP/cycif/testsets/PTCL/dearrayPython/1.tif' +# outputPath = 'D:/LSP/cycif/testsets/PTCL/dearrayPython/segmentation' +# nucleiClassProbPath = ''#'D:/LSP/cycif/testsets/exemplar-002/prob_map/1_NucleiPM_1.tif' +# contoursClassProbPath = ''#'D:/LSP/cycif/testsets/exemplar-002/prob_map/1_ContoursPM_1.tif' +# stackProbPath = 'D:/LSP/cycif/testsets/PTCL/prob_maps/1_Probabilities_40.tif' +# maskPath = 'D:/LSP/cycif/testsets/exemplar-002/dearrayPython/masks/1_mask.tif' +# args.crop = 'dearray' +# args.segmentCytoplasm = 'segmentCytoplasm' +# + #punctatest +# imagePath = 'Z:/IDAC/Clarence/Seidman/ZeissMouseTestSet/31082020_XXWT_Txnip550_Ddx3x647_WGA488_40x_1.tif' +# outputPath = 'Z:/IDAC/Clarence/Seidman/ZeissMouseTestSet/segmentation' +## nucleiClassProbPath = 'Z:/IDAC/Clarence/Seidman/ZeissMouseTestSet/probability-maps/test_NucleiPM_1.tif' +## contoursClassProbPath = 'Z:/IDAC/Clarence/Seidman/DanMouse/probability-maps/test_ContoursPM_1.tif' +# contoursClassProbPath ='' +# stackProbPath = 'Z:/IDAC/Clarence/Seidman/ZeissMouseTestSet/probability-maps/31082020_XXWT_Txnip550_Ddx3x647_WGA488_40x_1_Probabilities_2.tif' +# maskPath = 'D:/Seidman/ZeissTest Sets/segmentation/13042020_15AP_FAP488_LINC550_DCN647_WGA_40x_1/cellMask.tif' +# args.nucleiRegion = 'localThreshold' +# args.logSigma = [45, 300] +# args.segmentCytoplasm = 'ignoreCytoplasm' +# args.detectPuncta = [1] +# args.punctaSigma = [1] +# args.punctaSD = [3] + + + #plate +# imagePath = 'Y:/sorger/data/computation/Jeremy/caitlin-ddd-cycif-registered/Plate1/E3_fld_1/registration/E3_fld_1.ome.tif' +# outputPath = 'Y:/sorger/data/computation/Jeremy/caitlin-ddd-cycif-registered/Plate1/E3_fld_1/segmentation' +# nucleiClassProbPath = 'Y:/sorger/data/computation/Jeremy/caitlin-ddd-cycif-registered/Plate1/E3_fld_1/prob_maps/E3_fld_1_NucleiPM_1.tif' +# contoursClassProbPath = 'Y:/sorger/data/computation/Jeremy/caitlin-ddd-cycif-registered/Plate1/E3_fld_1/prob_maps/E3_fld_1_ContoursPM_1.tif' +# maskPath = 'D:/LSP/cycif/testsets/exemplar-001/dearray/masks/A1_mask.tif' +# args.crop = 'plate' +# args.cytoMethod ='hybrid' + + #large tissue +# imagePath = 'D:/WD-76845-097.ome.tif' +# outputPath = 'D:/' +## nucleiClassProbPath = 'D:/WD-76845-097_NucleiPM_28.tif' +# contoursClassProbPath = ""#'D:/WD-76845-097_ContoursPM_28.tif' +# stackProbPath = 'D:/ilastik/WD-76845-097_Probabilities_0.tif' +# maskPath = 'D:/LSP/cycif/testsets/exemplar-001/dearray/masks/A1_mask.tif' +# args.nucleiRegion = 'localThreshold' +# args.crop = 'autoCrop' +# args.TissueMaskChan =[0] + + #maskRCNN +# imagePath = 'D:/Seidman/s3segtest/registration/1.tif' +# outputPath = 'D:/Seidman/s3segtest/segmentation' +# stackProbPath = 'D:/Seidman/s3segtest/1_Probabilities_0.tif' +# contoursClassProbPath = '' +# maskPath = 'D:/LSP/cycif/testsets/exemplar-001/dearray/masks/A1_mask.tif' +# args.nucleiRegion = 'bypass' +# args.crop = 'noCrop' +# args.TissueMaskChan =[0] +# args.logSigma = [45,300] + +# #pixellevel +# imagePath = 'D:/Olesja/D2107_spleen_DAPI-EdU_01/Layer0/D2107_spleen_DAPI-EdU_01.btf' +# outputPath = 'D:/Olesja/D2107_spleen_DAPI-EdU_01/segmentation' +# stackProbPath = 'D:/Seidman/s3segtest/1_Probabilities_0.tif' +# contoursClassProbPath = 'D:/Olesja/D2107_spleen_DAPI-EdU_01/prob_maps3Class/D2107_spleen_DAPI-EdU_01_ContoursPM_0.tif' +# nucleiClassProbPath = 'D:/Olesja/D2107_spleen_DAPI-EdU_01/prob_maps3Class/D2107_spleen_DAPI-EdU_01_NucleiPM_0.tif' +# maskPath = 'D:/LSP/cycif/testsets/exemplar-001/dearray/masks/A1_mask.tif' +# args.nucleiRegion = 'pixellevel' +# args.crop = 'noCrop' +# args.TissueMaskChan =[0] +# args.pixelThreshold = 0.06 + + imagePath = args.imagePath + outputPath = args.outputPath + nucleiClassProbPath = args.nucleiClassProbPath + contoursClassProbPath = args.contoursClassProbPath + stackProbPath = args.stackProbPath + maskPath = args.maskPath + + fileName = os.path.basename(imagePath) + filePrefix = fileName[0:fileName.index('.')] + + if not os.path.exists(outputPath): + os.makedirs(outputPath) + + # get channel used for nuclei segmentation + + if len(contoursClassProbPath)>0: + legacyMode = 1 + probPrefix = os.path.basename(contoursClassProbPath) + nucMaskChan = int(probPrefix.split('ContoursPM_')[1].split('.')[0]) + elif len(stackProbPath)>0: + legacyMode = 0 + probPrefix = os.path.basename(stackProbPath) + index = re.search('%s(.*)%s' % ('Probabilities', '.tif'), stackProbPath).group(1) + if len(index)==0: + nucMaskChan = args.probMapChan + else: + nucMaskChan = int(re.sub("\D", "", index)) + else: + print('NO PROBABILITY MAP PROVIDED') + if args.probMapChan ==-1: + if nucMaskChan ==-1: + sys.exit('INVALID NUCLEI CHANNEL SELECTION. SELECT CHANNEL USING --probMapChan') + else: + print('extracting nuclei channel from filename') + else: + nucMaskChan = args.probMapChan + + if args.TissueMaskChan==-1: + TissueMaskChan = copy.copy(args.CytoMaskChan) + TissueMaskChan.append(nucMaskChan) + else: + TissueMaskChan = args.TissueMaskChan[:] + TissueMaskChan.append(nucMaskChan) + + #crop images if needed + if args.crop == 'interactiveCrop': + nucleiCrop = tifffile.imread(imagePath,key = nucMaskChan) + r=cv2.selectROI(resize(nucleiCrop,(nucleiCrop.shape[0] // 30, nucleiCrop.shape[1] // 30))) + cv2.destroyWindow('select') + rect=np.transpose(r)*30 + PMrect= [rect[1], rect[0], rect[3], rect[2]] + nucleiCrop = nucleiCrop[int(rect[1]):int(rect[1]+rect[3]), int(rect[0]):int(rect[0]+rect[2])] + elif args.crop == 'noCrop' or args.crop == 'dearray' or args.crop == 'plate': + nucleiCrop = tifffile.imread(imagePath,key = nucMaskChan) + rect = [0, 0, nucleiCrop.shape[0], nucleiCrop.shape[1]] + PMrect= rect + elif args.crop == 'autoCrop': + nucleiCrop = tifffile.imread(imagePath,key = nucMaskChan) + 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)] + PMrect= rect + nucleiCrop = nucleiCrop[int(rect[0]):int(rect[0]+rect[2]), int(rect[1]):int(rect[1]+rect[3])] + + if legacyMode ==1: + nucleiProbMaps = tifffile.imread(nucleiClassProbPath,key=0) + nucleiPM = nucleiProbMaps[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])] + nucleiProbMaps = tifffile.imread(contoursClassProbPath,key=0) + PMSize = nucleiProbMaps.shape + nucleiPM = np.dstack((nucleiPM,nucleiProbMaps[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])])) + else: + nucleiProbMaps = imread(stackProbPath) + nucleiPM = nucleiProbMaps[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3]),0:2] + PMSize = nucleiProbMaps.shape + + # mask the core/tissue + if args.crop == 'dearray': + TMAmask = tifffile.imread(maskPath) + elif args.crop =='plate': + TMAmask = np.ones(nucleiCrop.shape) + + else: + tissue = np.empty((len(TissueMaskChan),nucleiCrop.shape[0],nucleiCrop.shape[1]),dtype=np.uint16) + count=0 + if args.crop == 'noCrop': + for iChan in TissueMaskChan: + tissueCrop =tifffile.imread(imagePath,key=iChan) + tissue_gauss = gaussian(tissueCrop,1) + #tissue_gauss[tissue_gauss==0]=np.nan + tissue[count,:,:] =np.log2(tissue_gauss+1)>threshold_otsu(np.log2(tissue_gauss+1)) + count+=1 + else: + for iChan in TissueMaskChan: + tissueCrop = tifffile.imread(imagePath,key=iChan) + tissue_gauss = gaussian(tissueCrop[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])],1) + tissue[count,:,:] = np.log2(tissue_gauss+1)>threshold_otsu(np.log2(tissue_gauss+1)) + count+=1 + TMAmask = np.max(tissue,axis = 0) + + # tissue_gauss = tissueCrop +# tissue_gauss1 = tissue_gauss.astype(float) +# tissue_gauss1[tissue_gauss>np.percentile(tissue_gauss,99)]=np.nan +# TMAmask = np.log2(tissue_gauss+1)>threshold_otsu(np.log2(tissue_gauss+1)) + #imshow(TMAmask) + del tissue_gauss, tissue + + # nuclei segmentation + if args.nucleiRegion == 'pixellevel': + pixelTissue = np.empty((len(args.pixelMaskChan),nucleiCrop.shape[0],nucleiCrop.shape[1]),dtype=np.uint16) + count=0 + for iChan in args.pixelMaskChan: + pixelCrop = tifffile.imread(imagePath,key=iChan) + pixelTissue[count,:,:] = pixelCrop[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])] + count+=1 + nucleiMask = S3AreaSegmenter(nucleiPM, pixelTissue, TMAmask,args.pixelThreshold,outputPath) + else: + nucleiMask = S3NucleiSegmentationWatershed(nucleiPM,nucleiCrop,args.logSigma,TMAmask,args.nucleiFilter,args.nucleiRegion) + del nucleiPM + # cytoplasm segmentation + if args.segmentCytoplasm == 'segmentCytoplasm': + count =0 + if args.crop == 'noCrop' or args.crop == 'dearray' or args.crop == 'plate': + cyto=np.empty((len(args.CytoMaskChan),nucleiCrop.shape[0],nucleiCrop.shape[1]),dtype=np.uint16) + for iChan in args.CytoMaskChan: + cyto[count,:,:] = tifffile.imread(imagePath, key=iChan) + count+=1 + elif args.crop =='autoCrop': + cyto=np.empty((len(args.CytoMaskChan),int(rect[2]),int(rect[3])),dtype=np.int16) + for iChan in args.CytoMaskChan: + cytoFull= tifffile.imread(imagePath, key=iChan) + cyto[count,:,:] = cytoFull[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])] + count+=1 + else: + cyto=np.empty((len(args.CytoMaskChan),rect[3],rect[2]),dtype=np.int16) + for iChan in args.CytoMaskChan: + cytoFull= tifffile.imread(imagePath, key=iChan) + cyto[count,:,:] = cytoFull[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])] + count+=1 + cyto = np.amax(cyto,axis=0) + cytoplasmMask,nucleiMaskTemp,cellMask = S3CytoplasmSegmentation(nucleiMask,cyto,TMAmask,args.cytoMethod,args.cytoDilation) + exportMasks(nucleiMaskTemp,nucleiCrop,outputPath,filePrefix,'nuclei',args.saveFig,args.saveMask) + exportMasks(cytoplasmMask,cyto,outputPath,filePrefix,'cyto',args.saveFig,args.saveMask) + exportMasks(cellMask,cyto,outputPath,filePrefix,'cell',args.saveFig,args.saveMask) + + cytoplasmMask,nucleiMaskTemp,cellMask = S3CytoplasmSegmentation(nucleiMask,cyto,TMAmask,'ring',args.cytoDilation) + exportMasks(nucleiMaskTemp,nucleiCrop,outputPath,filePrefix,'nucleiRing',args.saveFig,args.saveMask) + exportMasks(cytoplasmMask,cyto,outputPath,filePrefix,'cytoRing',args.saveFig,args.saveMask) + exportMasks(cellMask,cyto,outputPath,filePrefix,'cellRing',args.saveFig,args.saveMask) + + elif args.segmentCytoplasm == 'ignoreCytoplasm': + exportMasks(nucleiMask,nucleiCrop,outputPath,filePrefix,'nuclei') + cellMask = nucleiMask + exportMasks(nucleiMask,nucleiCrop,outputPath,filePrefix,'cell') + cytoplasmMask = nucleiMask + + + if (np.min(args.detectPuncta)>-1): + if len(args.detectPuncta) != len(args.punctaSigma): + args.punctaSigma = args.punctaSigma[0] * np.ones(len(args.detectPuncta)) + + + if len(args.detectPuncta) != len(args.punctaSD): + args.punctaSD = args.punctaSD[0] * np.ones(len(args.detectPuncta)) + + counter=0 + for iPunctaChan in args.detectPuncta: + punctaChan = tifffile.imread(imagePath,key = iPunctaChan) + punctaChan = punctaChan[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])] + spots=S3punctaDetection(punctaChan,cellMask,args.punctaSigma[counter],args.punctaSD[counter]) + cellspotmask = nucleiMask#tifffile.imread(maskPath) #REMOVE THIS LATER + P = regionprops(cellspotmask,intensity_image = spots ,cache=False) + numSpots = [] + for prop in P: + numSpots.append(np.uint16(np.round(prop.mean_intensity * prop.area))) + np.savetxt(outputPath + os.path.sep + 'numSpots_chan' + str(iPunctaChan) +'.csv',(np.transpose(np.asarray(numSpots))),fmt='%10.5f') + edges = 1-(cellMask>0) + stacked_img=np.stack((np.uint16((spots+edges)>0)*np.amax(punctaChan),punctaChan),axis=0) + tifffile.imsave(outputPath + os.path.sep + filePrefix + os.path.sep + 'punctaChan'+str(iPunctaChan) + 'Outlines.tif',stacked_img) + counter=counter+1 + #fix bwdistance watershed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/macros.xml Fri Mar 12 00:18:40 2021 +0000 @@ -0,0 +1,24 @@ +<?xml version="1.0"?> +<macros> + <xml name="requirements"> + <requirements> + <requirement type="package" version="3.6">python</requirement> + <requirement type="package">scikit-learn</requirement> + <requirement type="package">scikit-image</requirement> + <requirement type="package">matplotlib</requirement> + <requirement type="package">tifffile</requirement> + <requirement type="package">opencv</requirement> + </requirements> + </xml> + + <xml name="version_cmd"> + <version_command>echo @VERSION@</version_command> + </xml> + <xml name="citations"> + <citations> + </citations> + </xml> + + <token name="@VERSION@">1.2.1</token> + <token name="@CMD_BEGIN@">python3 $__tool_directory__/S3segmenter.py</token> +</macros>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rowit.py Fri Mar 12 00:18:40 2021 +0000 @@ -0,0 +1,76 @@ +import numpy as np +from skimage.util import view_as_windows, montage + + +class WindowView(object): + + def __init__( + self, img_shape, block_size, overlap_size + ): + self.img_shape = img_shape + self.block_size = block_size + self.overlap_size = overlap_size + + self.step_size = block_size - overlap_size + + def window_view_list(self, img, pad_mode='constant'): + half = int(self.overlap_size / 2) + img = np.pad(img, ( + (half, self.padded_shape[0] - self.img_shape[0] - half), + (half, self.padded_shape[1] - self.img_shape[1] - half), + ), mode=pad_mode) + + return self._window_view_list(img) + + def padding_mask(self): + half = int(self.overlap_size / 2) + padding_mask = np.ones(self.img_shape, dtype=np.bool) + padding_mask = np.pad(padding_mask, ( + (half, self.padded_shape[0] - self.img_shape[0] - half), + (half, self.padded_shape[1] - self.img_shape[1] - half), + ), mode='constant', constant_values=0) + return self._window_view_list(padding_mask) + + def reconstruct(self, img_window_view_list): + grid_shape = self.window_view_shape[:2] + + start = int(self.overlap_size / 2) + end = int(self.block_size - start) + + img_window_view_list = img_window_view_list[..., start:end, start:end] + + return montage( + img_window_view_list, grid_shape=grid_shape + )[:self.img_shape[0], :self.img_shape[1]] + + @property + def padded_shape(self): + padded_shape = np.array(self.img_shape) + self.overlap_size + n = np.ceil((padded_shape - self.block_size) / self.step_size) + padded_shape = (self.block_size + (n * self.step_size)).astype(np.int) + return tuple(padded_shape) + + @property + def window_view_shape(self): + return view_as_windows( + np.empty(self.padded_shape), + self.block_size, self.step_size + ).shape + + def _window_view_list(self, img): + return ( + view_as_windows(img, self.block_size, self.step_size) + .reshape(-1, self.block_size, self.block_size) + ) + +def crop_with_padding_mask(img, padding_mask, return_mask=False): + if np.all(padding_mask == 1): + return (img, padding_mask) if return_mask else img + (r_s, r_e), (c_s, c_e) = [ + (i.min(), i.max() + 1) + for i in np.where(padding_mask == 1) + ] + padded = np.zeros_like(img) + img = img[r_s:r_e, c_s:c_e] + padded[r_s:r_e, c_s:c_e] = 1 + return (img, padded) if return_mask else img \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/s3segmenter.xml Fri Mar 12 00:18:40 2021 +0000 @@ -0,0 +1,155 @@ +<tool id="s3segmenter" name="s3segmenter" version="@VERSION@.3" profile="17.09"> + <description>S3segmenter is a Python-based set of functions that generates single cell (nuclei and cytoplasm) label masks.</description> + <macros> + <import>macros.xml</import> + </macros> + + <expand macro="requirements"/> + @VERSION_CMD@ + + <command detect_errors="exit_code"><![CDATA[ + + ln -s '${imagePath}' ./image.tif; + + #if $contoursClassProbPath + ln -s ${contoursClassProbPath} ./ContoursPM.tif; + #end if + + #if $nucleiClassProbPath + ln -s ${nucleiClassProbPath} ./NucleiPM.tif; + #end if + + #if $stackProbPath + ln -s ${stackProbPath} ./Probabilities.tif; + #end if + + + @CMD_BEGIN@ + --imagePath ./image.tif + + #if $contoursClassProbPath + --contoursClassProbPath ./ContoursPM.tif + #end if + + #if $nucleiClassProbPath + --nucleiClassProbPath ./NucleiPM.tif + #end if + + #if $stackProbPath + --stackProbPath ./Probabilities.tif + #end if + + --mask $mask + --probMapChan $probMapChan + --crop $crop + --cytoMethod $cytoMethod + --nucleiFilter $nucleiFilter + --nucleiRegion $nucleiRegion + --segmentCytoplasm $segmentCytoplasm + --cytoDilation $adv.cytoDilation + --logSigma $adv.logSigma + --CytoMaskChan $adv.CytoMaskChan + ##--TissueMaskChan $adv.TissueMaskChan + --detectPuncta $adv.detectPuncta + --punctaSigma $adv.punctaSigma + --punctaSD $adv.punctaSD + + #if not $saveMask + --saveMask + #end if + + #if not $saveFig + --saveFig + #end if + + --outputPath . + ]]></command> + + + <inputs> + + <param name="imagePath" type="data" format="tiff" label="Image File"/> + <param name="contoursClassProbPath" type="data" format="tiff" optional="true" label="Contours Class Probabilities"/> + <param name="nucleiClassProbPath" type="data" format="tiff" optional="true" label="Nuclei Class Probabilities"/> + <param name="stackProbPath" type="data" format="tiff" optional="true" label="Stack Probabilities"/> + <param name="mask" type="select" label="Choose mask: TMA, tissue, none."> + <option selected="true" value="tissue">tissue</option> + <option value="TMA">TMA</option> + <option value="none">none</option> + </param> + <param name="probMapChan" type="integer" value="-1" label="Probability Maps Channel"/> + <param name="crop" type="select" label="Crop Strategy"> + <option selected="true" value="noCrop">No Crop</option> + <option value="autoCrop">Automatic Crop</option> + <option value="dearray">De-array</option> + <option value="plate">Plate</option> + </param> + <param name="cytoMethod" type="select" label="Cyto Method"> + <option value="hybrid">Hybrid</option> + <option selected="true" value="distanceTransform">Distance Transform</option> + <option value="bwdistanceTransform">BW Distance Transform</option> + <option value="ring">Ring</option> + </param> + <param name="nucleiFilter" type="select" label="Nuclei Filter"> + <option selected="true" value="IntPM">IntPM</option> + <option value="LoG">LoG</option> + <option value="Int">Int</option> + <option value="none">none</option> + </param> + <param name="nucleiRegion" type="select" label="Nuclei Region"> + <option value="watershedContourDist">watershedContourDist</option> + <option selected="true" value="watershedContourInt">watershedContourInt</option> + <option value="watershedBWDist">watershedBWDist</option> + <option value="dilation">dilation</option> + <option value="localThreshold">localThreshold</option> + </param> + <param name="segmentCytoplasm" type="select" label="Segment Cytoplasm"> + <option value="segmentCytoplasm">segmentCytoplasm</option> + <option selected="true" value="ignoreCytoplasm">ignoreCytoplasm</option> + </param> + <param name="saveMask" type="boolean" checked="true" label="Save Mask"/> + <param name="saveFig" type="boolean" checked="true" label="Save Figure"/> + + <section name="adv" title="Advanced Options" expanded="false"> + <param name="cytoDilation" type="integer" value="5" label="Cyto Dilation"/> + <param name="logSigma" type="text" value="3 60" label="logSigma"/> + <param name="CytoMaskChan" type="text" value="1" label="Cyto Mask Channel"/> + <!-- Bug with S3Segmenter code, expects int not list + <param name="TissueMaskChan" type="text" value="-1" label="Tissue Mask Channel"/> + --> + <param name="detectPuncta" type="text" value="-1" label="Detect Puncta"/> + <param name="punctaSigma" type="text" value="1" label="Puncta Sigma"/> + <param name="punctaSD" type="text" value="4" label="Puncta SD"/> + </section> + + + </inputs> + <outputs> + <data format="tiff" name="cell_mask" from_work_dir="*/cellMask.tif" label="cellMasks"> + <filter>saveMask is True</filter> + </data> + <data format="tiff" name="nuclei_mask" from_work_dir="*/nucleiMask.tif" label="nucleiMasks"> + <filter>saveMask is True</filter> + </data> + <data format="tiff" name="cell_outlines" from_work_dir="*/cellOutlines.tif" label="cellOutlines"> + <filter>saveFig is True</filter> + </data> + <data format="tiff" name="nuclei_outlines" from_work_dir="*/nucleiOutlines.tif" label="nucleiOutlines"> + <filter>saveFig is True</filter> + </data> + </outputs> + <help><![CDATA[ +Inputs are: + +1. an .ome.tif (preferably flat field corrected) +2. a 3-class probability maps derived from a deep learning model such as UNet. Classes include background, nuclei contours, and nuclei foreground. + +The centers of each nuclei are obtained by finding local maxima from the nuclei foreground. These are used for marker-controlled watershed constrained by the nuclei contours. + +To segment cytoplasm, the nuclei are in turn used for a marker-controlled watershed segmentation constrained by a cytoplasmic marker such as B-catenin. The channel number of this marker must be specified. A 3-pixel annulus around each nucleus will also be used to segment cytoplasm. + +The source repository can be found here: https://github.com/HMS-IDAC/S3segmenter +OHSU Wrapper Repo: https://github.com/ohsu-comp-bio/S3segmenter + ]]></help> + <expand macro="citations" /> +</tool>