//*******
// Author: Pradeep Rajasekhar
// March 2021
// License: BSD3
//
// Copyright 2021 Pradeep Rajasekhar, Walter and Eliza Hall Institute of Medical Research, Melbourne, Australia
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
var fs=File.separator;
setOption("ExpandableArrays", true);
print("\\Clear");
var fiji_dir=getDirectory("imagej");
var gat_dir=fiji_dir+"scripts"+fs+"GAT"+fs+"Tools"+fs+"commands";
//settings for GAT
gat_settings_path=gat_dir+fs+"gat_settings.ijm";
if(!File.exists(gat_settings_path)) exit("Cannot find settings file. Check: "+gat_settings_path);
run("Results... ", "open="+gat_settings_path);
training_pixel_size=parseFloat(Table.get("Values", 0)); //0.7;
neuron_area_limit=parseFloat(Table.get("Values", 1)); //1500
neuron_seg_lower_limit=parseFloat(Table.get("Values", 2)); //90
neuron_lower_limit=parseFloat(Table.get("Values", 3)); //160
probability=parseFloat(Table.get("Values", 5)); //prob neuron
overlap= parseFloat(Table.get("Values", 7));
run("Close");
//specify directory where StarDist models are stored
var models_dir=fiji_dir+"scripts"+fs+"GAT"+fs+"Models"+fs;
//Neuron segmentation model
neuron_model_path=models_dir+"2D_enteric_neuron_v2.zip";
//check if required plugins are installed
var check_plugin=gat_dir+fs+"check_plugin.ijm";
if(!File.exists(check_plugin)) exit("Cannot find check plugin macro. Returning: "+check_plugin);
runMacro(check_plugin);
//check if label to roi macro is present
var label_to_roi=gat_dir+fs+"Convert_Label_to_ROIs.ijm";
if(!File.exists(label_to_roi)) exit("Cannot find label to roi script. Returning: "+label_to_roi);
//check if roi to label macro is present
var roi_to_label=gat_dir+fs+"Convert_ROI_to_Labels.ijm";
if(!File.exists(roi_to_label)) exit("Cannot find roi to label script. Returning: "+roi_to_label);
//check if ganglia cell count is present
var ganglia_cell_count=gat_dir+fs+"Calculate_Neurons_per_Ganglia.ijm";
if(!File.exists(ganglia_cell_count)) exit("Cannot find ganglia cell count script. Returning: "+ganglia_cell_count);
//check if ganglia prediction post processing macro present
var deepimagej_post_processing=gat_dir+fs+"Ganglia_prediction_post_processing.ijm";
if(!File.exists(deepimagej_post_processing)) exit("Cannot find roi to label script. Returning: "+deepimagej_post_processing);
//check if ganglia prediction post processing macro present
var segment_ganglia=gat_dir+fs+"Segment_Ganglia.ijm";
if(!File.exists(segment_ganglia)) exit("Cannot find segment ganglia script. Returning: "+segment_ganglia);
#@ File (style="open", label="Choose the image to segment.
Enter NA if image is open.") path
#@ boolean image_already_open
#@ String(value="If image is already open, tick above box.", visibility="MESSAGE") hint1
#@ String(label="Enter channel number for Hu if you know. Enter NA if not using.", value="NA") cell_channel
// File (style="open", label="Choose the StarDist model file if segmenting neurons.
Enter NA if empty",value="NA", description="Enter NA if nothing") neuron_model_path
cell_type="Neuron";
#@ String(value="----------------------------------------------------------------------------------------------------------------------------------------",visibility="MESSAGE") hint_star
#@ String(value="
DETERMINE GANGLIA OUTLINE ",visibility="MESSAGE") hint_ganglia
#@ String(value=" Cell counts per ganglia will be calculated
Requires a neuron channel & second channel that labels the neuronal fibres.",visibility="MESSAGE") hint4
#@ boolean Cell_counts_per_ganglia (description="Use a pretrained deepImageJ model to predict ganglia outline")
#@ String(choices={"DeepImageJ","Manually draw ganglia"}, style="radioButtonHorizontal") Ganglia_detection
#@ String(label=" Enter the channel NUMBER for segmenting ganglia.
Preferably a bright marker that labels most neuronal fibres.
Enter NA if not using. ", value="NA") ganglia_channel
#@ String(value="---------------------------------------------------------******ADVANCED PARAMETERS******-------------------------------------------",visibility="MESSAGE") hint_adv
#@ String(value="Finetune cell detection by changing parameters below. ",visibility="MESSAGE") hint_stardist
#@ boolean Change_pixel_size_segmentation (description="Change the pixel size of the scaled image thats used to detect neurons")
#@ Float(label="Enter pixel size for segmenting neurons.", value=0.70) training_pixel_size_custom
if(Change_pixel_size_segmentation==true) training_pixel_size=training_pixel_size_custom;
#@ boolean Finetune_detection
// String(value=" Probability",visibility="MESSAGE", required=false) hint34
#@ Double (label="Probability of detecting neuron ", style="slider", min=0, max=1, stepSize=0.05,value=0.55) probability_manual
#@ Double (label="Overlap threshold", style="slider", min=0, max=1, stepSize=0.05,value=0.5) overlap_manual
if(Finetune_detection==true)
{
print("Using manual probability and overlap threshold for detection");
probability=probability_manual;
overlap=overlap_manual;
}
//listing parameters being used for GAT
print("Using parameters\nSegmentation pixel size:"+training_pixel_size+"\nMax neuron area (microns): "+neuron_area_limit+"\nMin Neuron Area (microns): "+neuron_seg_lower_limit+"\nMin marker area (microns): "+neuron_lower_limit);
print("**Neuron\nProbability: "+probability+"\nOverlap threshold: "+overlap);
if(image_already_open==true)
{
waitForUser("Select Image and choose output folder in next prompt");
file_name_full=getTitle(); //get file name without extension (.lif)
dir=getDirectory("Choose Output Folder");
}
else
{
if(endsWith(path, ".czi")|| endsWith(path, ".lif")) run("Bio-Formats", "open=["+path+"] color_mode=Composite rois_import=[ROI manager] view=Hyperstack stack_order=XYCZT");
else if (endsWith(path, ".tif")|| endsWith(path, ".tiff")) open(path);
else exit("File type not recognised. Tif, Lif and Czi files supported.");
dir=File.directory;
file_name_full=File.nameWithoutExtension; //get file name without extension (.lif)
}
//file_name=File.nameWithoutExtension;
file_name_length=lengthOf(file_name_full);
if(file_name_length>50) file_name=substring(file_name_full, 0, 39); //Restricting file name length as in Windows long path names can cause errors
else file_name=file_name_full;
//print(file_name);
img_name=getTitle();
Stack.getDimensions(width, height, sizeC, sizeZ, frames);
run("Select None");
run("Remove Overlay");
getPixelSize(unit, pixelWidth, pixelHeight);
//Training images were pixelsize of ~0.568, 0.7 is default value that works
scale_factor=pixelWidth/training_pixel_size;
if(scale_factor<1.001 && scale_factor>1) scale_factor=1;
print("Analysing: "+file_name);
analysis_dir= dir+"Analysis"+fs;
if (!File.exists(analysis_dir)) File.makeDirectory(analysis_dir);
print("Files will be saved at: "+analysis_dir);
print("Analysing: "+file_name);
//Create results directory with file name in "analysis"
results_dir=analysis_dir+file_name+fs; //directory to save images
if (!File.exists(results_dir)) File.makeDirectory(results_dir); //create directory to save results file
//do not include cells greater than 1000 micron in area
//neuron_area_limit=1500; //microns
neuron_max_pixels=neuron_area_limit/pixelWidth; //convert micron to pixels
//using limit when segmenting neurons
//neuron_seg_lower_limit=90;//microns
neuron_seg_lower_limit=neuron_seg_lower_limit/pixelWidth;
table_name="Analysis_"+cell_type+"_"+file_name;
Table.create(table_name);//Final Results Table
row=0; //row counter for the table
image_counter=0;
//parse cell and ganglia channels and check if value is Integer
if(cell_channel!="NA")
{
cell_channel=parseInt(cell_channel);
if(isNaN(cell_channel)) exit("Enter channel number for cell. If leaving empty, type NA in the value");
}
//if more than one channel , check if appropriate values entered
if(sizeC>1)
{
if (Cell_counts_per_ganglia==true && cell_channel=="NA" && ganglia_channel=="NA") //count cells per ganglia but don't know channels for ganglia or neuron
{
waitForUser("Note the channels for neuron and ganglia and enter in the next box.");
Dialog.create("Choose channels for "+cell_type);
Dialog.addNumber("Enter "+cell_type+" channel", 3);
Dialog.addNumber("Enter channel for segmenting ganglia", 2);
Dialog.show();
cell_channel= Dialog.getNumber();
ganglia_channel=Dialog.getNumber();
Stack.setChannel(cell_channel);
resetMinAndMax();
Stack.setChannel(ganglia_channel);
resetMinAndMax();
}
else if(Cell_counts_per_ganglia==true && cell_channel!="NA" && ganglia_channel=="NA") //count cells per ganglia but don't know channels for ganglia
{
waitForUser("Note the channels for ganglia and enter in the next box.");
Dialog.create("Choose channel for ganglia");
Dialog.addNumber("Enter channel for segmenting ganglia", 2);
Dialog.show();
//cell_channel= Dialog.getNumber();
ganglia_channel=Dialog.getNumber();
//Stack.setChannel(cell_channel);
//resetMinAndMax();
Stack.setChannel(ganglia_channel);
resetMinAndMax();
}
else if(Cell_counts_per_ganglia==true && cell_channel=="NA" && ganglia_channel!="NA") //count cells per ganglia but don't know channels for neuron
{
waitForUser("Note the channels for "+cell_type+" and enter in the next box.");
Dialog.create("Choose channel for "+cell_type);
Dialog.addNumber("Enter "+cell_type+" channel", 3);
Dialog.show();
cell_channel= Dialog.getNumber();
Stack.setChannel(cell_channel);
resetMinAndMax();
}
else if(Cell_counts_per_ganglia==true && cell_channel!="NA" && ganglia_channel!="NA")
{
ganglia_channel=parseInt(ganglia_channel);
if(isNaN(ganglia_channel)) exit("Enter channel number for Ganglia. If leaving empty, type NA in the value");
}
}
//add option for extended depth of field projection for widefield images
if(sizeZ>1)
{
print(img_name+" is a stack");
roiManager("reset");
waitForUser("Verify the type of image projection you'd like (MIP or Extended depth of field\nYou can select in the next prompt.");
projection_method=getBoolean("3D stack detected. Which projection method would you like?", "Maximum Intensity Projection", "Extended Depth of Field (Variance)");
if(projection_method==1)
{
waitForUser("Note the start and end of the stack.\nPress OK when done");
Dialog.create("Choose slices");
Dialog.addNumber("Start slice", 1);
Dialog.addNumber("End slice", sizeZ);
Dialog.show();
start=Dialog.getNumber();
end=Dialog.getNumber();
run("Z Project...", "start="+start+" stop="+end+" projection=[Max Intensity]");
max_projection=getTitle();
}
else
{
max_projection=extended_depth_proj(img_name);
}
}
else
{
print(img_name+" has only one slice, using as max projection");
max_projection=getTitle();
}
max_save_name="MAX_"+file_name;
//Segment Neurons
selectWindow(max_projection);
run("Select None");
run("Remove Overlay");
//if more than one channel, set on cell_channel or reference channel
if(sizeC>1)
{
Stack.setChannel(cell_channel);
}
roiManager("show none");
run("Duplicate...", "title="+cell_type+"_segmentation");
seg_image=getTitle();
roiManager("reset");
n_tiles=2;
new_width=round(width*scale_factor);
if(new_width>1200) n_tiles=4;
else if(new_width>4000) n_tiles=10;
//scale image if scaling factor is not equal to 1
if(scale_factor!=1)
{
selectWindow(seg_image);
new_width=round(width*scale_factor);
new_height=round(height*scale_factor);
run("Scale...", "x=- y=- width="+new_width+" height="+new_height+" interpolation=None create title=img_resize");
close(seg_image);
selectWindow("img_resize");
seg_image=getTitle();
}
roiManager("UseNames", "false");
selectWindow("Log");
print("*********Segmenting cells using StarDist********");
//segment neurons using StarDist model
segment_cells(max_projection,seg_image,neuron_model_path,n_tiles,width,height,scale_factor,neuron_seg_lower_limit,probability,overlap);
close(seg_image);
//manually correct or verify if needed
waitForUser("Correct "+cell_type+" ROIs if needed. You can delete or add ROIs using ROI Manager");
cell_count=roiManager("count");
rename_roi(); //rename ROIs
roiManager("deselect");
print("No of "+cell_type+" in "+max_projection+" : "+cell_count);
roiManager("deselect");
roi_location=results_dir+cell_type+"_ROIs_"+file_name+".zip";
roiManager("save",roi_location );
selectWindow(max_projection);
//uses roi to label macro code; clij is a dependency
runMacro(roi_to_label);
wait(5);
neuron_label_image=getTitle();
selectWindow(table_name);
Table.set("File name",row,file_name_full);
Table.set("Total "+cell_type, row, cell_count); //set total count of neurons after nos analysis if nos selected
Table.update;
selectWindow(max_projection);
run("Select None");
run("Remove Overlay");
if (Cell_counts_per_ganglia==true)
{
roiManager("reset");
if(Ganglia_detection=="DeepImageJ")
{
args=max_projection+","+cell_channel+","+ganglia_channel;
//get ganglia outline
runMacro(segment_ganglia,args);
wait(5);
ganglia_binary=getTitle();
draw_ganglia_outline(ganglia_binary,true);
}
else ganglia_binary=draw_ganglia_outline(ganglia_img,false);
args=neuron_label_image+","+ganglia_binary;
//get cell count per ganglia
runMacro(ganglia_cell_count,args);
//make ganglia binary image with ganglia having atleast 1 neuron
selectWindow("label_overlap");
//getMinAndMax(min, max);
setThreshold(1, 65535);
run("Convert to Mask");
resetMinAndMax;
close(ganglia_binary);
selectWindow("label_overlap");
rename("ganglia_binary");
selectWindow("ganglia_binary");
ganglia_binary=getTitle();
selectWindow("cells_ganglia_count");
cell_count_per_ganglia=Table.getColumn("Cell counts");
roiManager("deselect");
ganglia_number=roiManager("count");
roi_location=results_dir+"Ganglia_ROIs_"+file_name+".zip";
roiManager("save",roi_location );
roiManager("reset");
selectWindow(table_name);
Table.set("No of ganglia",0, ganglia_number);
Table.setColumn("Neuron counts per ganglia", cell_count_per_ganglia);
Table.update;
selectWindow("cells_ganglia_count");
run("Close");
}
//update table
Table.update;
Table.save(results_dir+cell_type+"_"+file_name+".csv");
selectWindow(neuron_label_image);
saveAs("Tiff", results_dir+"Neuron_label_"+max_save_name);
//using this image to detect neuron subtypes by label overlap
rename("Neuron_label");
neuron_label_image=getTitle();
selectWindow(neuron_label_image);
run("Select None");
roiManager("UseNames", "false");
close("*");
exit("Neuron analysis complete");
//function to segment cells using max projection, image to segment, model file location
//no of tiles for stardist, width and height of image
//returns the ROI manager with ROIs overlaid on the image.
function segment_cells(max_projection,img_seg,model_file,n_tiles,width,height,scale_factor,neuron_seg_lower_limit,probability,overlap)
{
//need to have the file separator as \\\\ in the file path when passing to StarDist Command from Macro.
//regex uses \ as an escape character, so \\ gives one backslash \, \\\\ gives \\.
//Windows file separator \ is actually \\ as one backslash is an escape character
//StarDist command takes the escape character as well, so pass 16 backlash to get 4xbackslash in the StarDIst macro command (which is then converted into 2)
model_file=replace(model_file, "\\\\","\\\\\\\\\\\\\\\\");
choice=0;
print(img_seg);
print(max_projection);
roiManager("reset");
//model_file="D:\\\\Gut analysis toolbox\\\\models\\\\2d_enteric_neuron\\\\TF_SavedModel.zip";
selectWindow(img_seg);
run("Command From Macro", "command=[de.csbdresden.stardist.StarDist2D],args=['input':'"+img_seg+"', 'modelChoice':'Model (.zip) from File', 'normalizeInput':'true', 'percentileBottom':'1.0', 'percentileTop':'99.8', 'probThresh':'"+probability+"', 'nmsThresh':'"+overlap+"', 'outputType':'Label Image', 'modelFile':'"+model_file+"', 'nTiles':'"+n_tiles+"', 'excludeBoundary':'2', 'roiPosition':'Automatic', 'verbose':'false', 'showCsbdeepProgress':'false', 'showProbAndDist':'false'], process=[false]");
wait(50);
temp=getTitle();
run("Duplicate...", "title=label_image");
label_image=getTitle();
run("Remove Overlay");
close(temp);
roiManager("reset");
selectWindow(label_image);
wait(20);
//remove all labels touching the borders
run("Remove Border Labels", "left right top bottom");
wait(10);
rename("Label-killBorders"); //renaming as the remove border labels gives names with numbers in brackets
//revert labelled image back to original size
if(scale_factor!=1)
{
selectWindow("Label-killBorders");
//run("Duplicate...", "title=label_original");
run("Scale...", "x=- y=- width="+width+" height="+height+" interpolation=None create title=label_original");
close("Label-killBorders");
}
else
{
selectWindow("Label-killBorders");
rename("label_original");
}
wait(10);
//rename("label_original");
//size filtering
selectWindow("label_original");
run("Label Size Filtering", "operation=Greater_Than_Or_Equal size="+neuron_seg_lower_limit);
label_filter=getTitle();
resetMinAndMax();
close("label_original");
//convert the labels to ROIs
runMacro(label_to_roi,label_filter);
wait(10);
close(label_image);
selectWindow(max_projection);
roiManager("show all");
close(label_filter);
}
//rename ROIs as consecutive numbers
function rename_roi()
{
for (i=0; i1)
{
for(ch=1;ch<=channels;ch++)
{
selectWindow(img);
Stack.setChannel(ch);
getLut(reds, greens, blues);
Ext.CLIJ2_push(img);
radius_x = 2.0;
radius_y = 2.0;
sigma = 10.0;
proj_img="proj_img"+ch;
Ext.CLIJ2_extendedDepthOfFocusVarianceProjection(img, proj_img, radius_x, radius_y, sigma);
Ext.CLIJ2_pull(proj_img);
setLut(reds, greens, blues);
//Ext.CLIJ2_pull(img);
concat_ch=concat_ch+"c"+ch+"="+proj_img+" ";
}
Ext.CLIJ2_clear();
//print(concat_ch);
run("Merge Channels...", concat_ch+" create");
Stack.setDisplayMode("color");
}
else
{
selectWindow(img);
getLut(reds, greens, blues);
Ext.CLIJ2_push(img);
radius_x = 2.0;
radius_y = 2.0;
sigma = 10.0;
proj_img="proj_img";
Ext.CLIJ2_extendedDepthOfFocusVarianceProjection(img, proj_img, radius_x, radius_y, sigma);
Ext.CLIJ2_pull(proj_img);
setLut(reds, greens, blues);
}
max_name="MAX_"+img;
rename(max_name);
close(img);
return max_name;
}