Next changeset 1:41e8efe8df43 (2022-03-11) |
Commit message:
"planemo upload for repository https://github.com/ohsu-comp-bio/S3segmenter commit d89a61efd4c465a1e6bf5b99b0f78fb19be5bdea-dirty" |
added:
S3segmenter.py macros.xml rowit.py s3segmenter.xml |
b |
diff -r 000000000000 -r 37acf42a824b S3segmenter.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/S3segmenter.py Fri Mar 12 00:18:40 2021 +0000 |
[ |
b"@@ -0,0 +1,629 @@\n+import matplotlib.pyplot as plt\n+import tifffile\n+import os\n+import numpy as np\n+from skimage import io as skio\n+from scipy.ndimage import *\n+import scipy.ndimage as ndi\n+from skimage.morphology import *\n+from skimage.morphology import extrema\n+from skimage import morphology\n+from skimage.measure import regionprops\n+from skimage.transform import resize\n+from skimage.filters import gaussian, threshold_otsu, threshold_local\n+from skimage.feature import peak_local_max\n+from skimage.color import label2rgb\n+from skimage.io import imsave,imread\n+from skimage.segmentation import clear_border, watershed, find_boundaries\n+from scipy.ndimage.filters import uniform_filter\n+from os.path import *\n+from os import listdir, makedirs, remove\n+import pickle\n+import shutil\n+import fnmatch\n+import cv2\n+import sys\n+import argparse\n+import re\n+import copy\n+import datetime\n+from joblib import Parallel, delayed\n+from rowit import WindowView, crop_with_padding_mask\n+\n+\n+def imshowpair(A,B):\n+ plt.imshow(A,cmap='Purples')\n+ plt.imshow(B,cmap='Greens',alpha=0.5)\n+ plt.show()\n+\n+ \n+def imshow(A):\n+ plt.imshow(A)\n+ plt.show()\n+ \n+def overlayOutline(outline,img):\n+ img2 = img.copy()\n+ stacked_img = np.stack((img2,)*3, axis=-1)\n+ stacked_img[outline > 0] = [65535, 0, 0]\n+ imshowpair(img2,stacked_img)\n+ \n+def normI(I):\n+ Irs=resize(I,(I.shape[0]//10,I.shape[1]//10) );\n+ p1 = np.percentile(Irs,10);\n+ J = I-p1;\n+ p99 = np.percentile(Irs,99.99);\n+ J = J/(p99-p1);\n+ return J\n+\n+def contour_pm_watershed(\n+ contour_pm, sigma=2, h=0, tissue_mask=None,\n+ padding_mask=None, min_area=None, max_area=None\n+):\n+ if tissue_mask is None:\n+ tissue_mask = np.ones_like(contour_pm)\n+ padded = None\n+ if padding_mask is not None and np.any(padding_mask == 0):\n+ contour_pm, padded = crop_with_padding_mask(\n+ contour_pm, padding_mask, return_mask=True\n+ )\n+ tissue_mask = crop_with_padding_mask(\n+ tissue_mask, padding_mask\n+ )\n+ \n+ maxima = peak_local_max(\n+ extrema.h_maxima(\n+ ndi.gaussian_filter(np.invert(contour_pm), sigma=sigma),\n+ h=h\n+ ),\n+ indices=False,\n+ footprint=np.ones((3, 3))\n+ )\n+ maxima = label(maxima).astype(np.int32)\n+ \n+ # Passing mask into the watershed function will exclude seeds outside\n+ # of the mask, which gives fewer and more accurate segments\n+ maxima = watershed(\n+ contour_pm, maxima, watershed_line=True, mask=tissue_mask\n+ ) > 0\n+ \n+ if min_area is not None and max_area is not None:\n+ maxima = label(maxima, connectivity=1).astype(np.int32)\n+ areas = np.bincount(maxima.ravel())\n+ size_passed = np.arange(areas.size)[\n+ np.logical_and(areas > min_area, areas < max_area)\n+ ]\n+ maxima *= np.isin(maxima, size_passed)\n+ np.greater(maxima, 0, out=maxima)\n+\n+ if padded is None:\n+ return maxima.astype(np.bool)\n+ else:\n+ padded[padded == 1] = maxima.flatten()\n+ return padded.astype(np.bool)\n+\n+def S3AreaSegmenter(nucleiPM, images, TMAmask, threshold,outputPath):\n+ nucleiCenters = nucleiPM[:,:,0]\n+ TMAmask= (nucleiCenters>np.amax(nucleiCenters)*0.8)*TMAmask\n+ area = []\n+ area.append(np.sum(np.sum(TMAmask)))\n+ for iChan in range(len(images)):\n+ image_gauss = gaussian(resize(images[iChan,:,:],(int(0.25*images.shape[1]),int(0.25*images.shape[2]))),1)\n+ if threshold ==-1:\n+ threshold = threshold_otsu(image_gauss)\n+ mask=resize(image_gauss>threshold,(images.shape[1],images.shape[2]),order = 0)*TMAmask\n+ area.append(np.sum(np.sum(mask)))\n+ np.savetxt(outputPath + os.path.sep + 'area.csv',(np.transpose(np.asarray(area))),fmt='%10.5f') \n+ return TMAmask\n+ \n+\n+def S3NucleiSegmentationWatershed(nucleiPM,nucleiImage,logSigma,TMAmask,nucleiFilter,nucleiRegion):\n+ nucleiContours = nucleiPM[:,:,1"..b",args.logSigma,TMAmask,args.nucleiFilter,args.nucleiRegion)\n+ del nucleiPM\n+ # cytoplasm segmentation\n+ if args.segmentCytoplasm == 'segmentCytoplasm':\n+ count =0\n+ if args.crop == 'noCrop' or args.crop == 'dearray' or args.crop == 'plate':\n+ cyto=np.empty((len(args.CytoMaskChan),nucleiCrop.shape[0],nucleiCrop.shape[1]),dtype=np.uint16) \n+ for iChan in args.CytoMaskChan:\n+ cyto[count,:,:] = tifffile.imread(imagePath, key=iChan)\n+ count+=1\n+ elif args.crop =='autoCrop':\n+ cyto=np.empty((len(args.CytoMaskChan),int(rect[2]),int(rect[3])),dtype=np.int16)\n+ for iChan in args.CytoMaskChan:\n+ cytoFull= tifffile.imread(imagePath, key=iChan)\n+ cyto[count,:,:] = cytoFull[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])]\n+ count+=1 \n+ else:\n+ cyto=np.empty((len(args.CytoMaskChan),rect[3],rect[2]),dtype=np.int16)\n+ for iChan in args.CytoMaskChan:\n+ cytoFull= tifffile.imread(imagePath, key=iChan)\n+ cyto[count,:,:] = cytoFull[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])]\n+ count+=1\n+ cyto = np.amax(cyto,axis=0)\n+ cytoplasmMask,nucleiMaskTemp,cellMask = S3CytoplasmSegmentation(nucleiMask,cyto,TMAmask,args.cytoMethod,args.cytoDilation)\n+ exportMasks(nucleiMaskTemp,nucleiCrop,outputPath,filePrefix,'nuclei',args.saveFig,args.saveMask)\n+ exportMasks(cytoplasmMask,cyto,outputPath,filePrefix,'cyto',args.saveFig,args.saveMask)\n+ exportMasks(cellMask,cyto,outputPath,filePrefix,'cell',args.saveFig,args.saveMask)\n+ \n+ cytoplasmMask,nucleiMaskTemp,cellMask = S3CytoplasmSegmentation(nucleiMask,cyto,TMAmask,'ring',args.cytoDilation)\n+ exportMasks(nucleiMaskTemp,nucleiCrop,outputPath,filePrefix,'nucleiRing',args.saveFig,args.saveMask)\n+ exportMasks(cytoplasmMask,cyto,outputPath,filePrefix,'cytoRing',args.saveFig,args.saveMask)\n+ exportMasks(cellMask,cyto,outputPath,filePrefix,'cellRing',args.saveFig,args.saveMask)\n+ \n+ elif args.segmentCytoplasm == 'ignoreCytoplasm':\n+ exportMasks(nucleiMask,nucleiCrop,outputPath,filePrefix,'nuclei')\n+ cellMask = nucleiMask\n+ exportMasks(nucleiMask,nucleiCrop,outputPath,filePrefix,'cell')\n+ cytoplasmMask = nucleiMask\n+ \n+ \n+ if (np.min(args.detectPuncta)>-1):\n+ if len(args.detectPuncta) != len(args.punctaSigma):\n+ args.punctaSigma = args.punctaSigma[0] * np.ones(len(args.detectPuncta))\n+ \n+ \n+ if len(args.detectPuncta) != len(args.punctaSD):\n+ args.punctaSD = args.punctaSD[0] * np.ones(len(args.detectPuncta))\n+ \n+ counter=0\n+ for iPunctaChan in args.detectPuncta:\n+ punctaChan = tifffile.imread(imagePath,key = iPunctaChan)\n+ punctaChan = punctaChan[int(PMrect[0]):int(PMrect[0]+PMrect[2]), int(PMrect[1]):int(PMrect[1]+PMrect[3])]\n+ spots=S3punctaDetection(punctaChan,cellMask,args.punctaSigma[counter],args.punctaSD[counter])\n+ cellspotmask = nucleiMask#tifffile.imread(maskPath) #REMOVE THIS LATER\n+ P = regionprops(cellspotmask,intensity_image = spots ,cache=False)\n+ numSpots = []\n+ for prop in P:\n+ numSpots.append(np.uint16(np.round(prop.mean_intensity * prop.area)))\n+ np.savetxt(outputPath + os.path.sep + 'numSpots_chan' + str(iPunctaChan) +'.csv',(np.transpose(np.asarray(numSpots))),fmt='%10.5f') \n+ edges = 1-(cellMask>0)\n+ stacked_img=np.stack((np.uint16((spots+edges)>0)*np.amax(punctaChan),punctaChan),axis=0)\n+ tifffile.imsave(outputPath + os.path.sep + filePrefix + os.path.sep + 'punctaChan'+str(iPunctaChan) + 'Outlines.tif',stacked_img)\n+ counter=counter+1 \n+ #fix bwdistance watershed\n" |
b |
diff -r 000000000000 -r 37acf42a824b macros.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/macros.xml Fri Mar 12 00:18:40 2021 +0000 |
b |
@@ -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> |
b |
diff -r 000000000000 -r 37acf42a824b rowit.py --- /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 |
b |
diff -r 000000000000 -r 37acf42a824b s3segmenter.xml --- /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> |