Repository 's3segmenter'
hg clone https://toolshed.g2.bx.psu.edu/repos/perssond/s3segmenter

Changeset 0:37acf42a824b (2021-03-12)
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>