//Macro for segmenting neurons //Need to choose the right model //Accommodates for a scaling factor //******* // Author: Pradeep Rajasekhar // March 2021 // License: BSD3 // // Copyright 2021 Pradeep Rajasekhar, Monash Institute of Pharmaceutical Sciences // // 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; //consider moving this into plugins folder so can use getDirectory("plugins") fiji_dir=getDirectory("imagej"); gat_dir=fiji_dir+"scripts"+fs+"GAT"+fs+"Other"+fs+"commands"; //nos_processing_macro nos_processing_dir=gat_dir+fs+"NOS_processing.ijm"; if(!File.exists(nos_processing_dir)) exit("Cannot find NOS processing macro. Returning: "+nos_processing_dir); //check_plugin_installation 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 is installed label_roi_dir=gat_dir+fs+"Convert_Label_to_ROIs.ijm"; if(!File.exists(label_roi_dir)) exit("Cannot find label to roi script. Returning: "+label_roi_dir); //check if label to roi is installed 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); //rename ROIs as consecutive numbers function rename_roi() { for (i=0; i1) scale_factor=1; dir=File.directory; 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 1500 micron in area neuron_area_limit=1500; //microns pixel_area_limit=neuron_area_limit/pixelWidth; //convert micron to pixels series_name=getTitle(); Stack.getDimensions(width, height, sizeC, sizeZ, frames); table_name="Morphology_"+cell_type+"_"+file_name; Table.create(table_name);//Final Results Table row=0; //row counter for the table image_counter=0; waitForUser("Note the channels for Hu, and other markers if needed"); if(sizeC>1) { //if(Normalise_to_ganglia==true && get_nos==true && marker_type_2==true) //{ // Dialog.create("Choose channels for "+cell_type); // Dialog.addNumber("Enter "+cell_type+" channel", 4); // Dialog.addNumber("Enter NOS channel", 4); // Dialog.addNumber("Enter channel for segmenting ganglia", 4); // //Dialog.addNumber("Enter channel for "+Marker_Name, 4); // Dialog.show(); // cell_channel= Dialog.getNumber(); // nos_channel=Dialog.getNumber(); //ganglia_channel=Dialog.getNumber(); //marker_channel=Dialog.getNumber(); //} if(Normalise_to_ganglia==true && get_nos==true) { Dialog.create("Choose channels for "+cell_type); Dialog.addNumber("Enter "+cell_type+" channel", 4); Dialog.addNumber("Enter NOS channel", 4); Dialog.addNumber("Enter channel for segmenting ganglia", 4); Dialog.show(); cell_channel= Dialog.getNumber(); nos_channel=Dialog.getNumber(); ganglia_channel=Dialog.getNumber(); } // else if (Normalise_to_ganglia==true && marker_type_2==true) // { // Dialog.create("Choose channels for "+cell_type); // Dialog.addNumber("Enter "+cell_type+" channel", 4); // Dialog.addNumber("Enter channel for segmenting ganglia", 4); // Dialog.addNumber("Enter channel for "+Marker_Name, 4); // Dialog.show(); // cell_channel= Dialog.getNumber(); // ganglia_channel=Dialog.getNumber(); // marker_channel=Dialog.getNumber(); // } // else if (get_nos==true && marker_type_2==true) // { // Dialog.create("Choose channels for "+cell_type); // Dialog.addNumber("Enter "+cell_type+" channel", 4); // Dialog.addNumber("Enter NOS channel", 4); // Dialog.addNumber("Enter channel for "+Marker_Name, 4); // Dialog.show(); // cell_channel= Dialog.getNumber(); // nos_channel=Dialog.getNumber(); // marker_channel=Dialog.getNumber(); // } else if (Normalise_to_ganglia==true) { 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(get_nos==true) { Dialog.create("Choose channels for "+cell_type); Dialog.addNumber("Enter "+cell_type+" channel", 3); Dialog.addNumber("Enter NOS channel", 2); Dialog.show(); cell_channel= Dialog.getNumber(); nos_channel=Dialog.getNumber(); Stack.setChannel(cell_channel); resetMinAndMax(); Stack.setChannel(nos_channel); resetMinAndMax(); } // else if(marker_type_2==true) // { // // Dialog.create("Choose channels for "+cell_type); //// Dialog.addNumber("Enter "+cell_type+" channel", 3); // Dialog.addNumber("Enter channel for "+Marker_Name, 4); // Dialog.show(); // cell_channel= Dialog.getNumber(); // marker_channel=Dialog.getNumber(); //// Stack.setChannel(cell_channel); // resetMinAndMax(); // Stack.setChannel(marker_channel); // resetMinAndMax(); // } else { 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(); } } if(sizeZ>1) { print(series_name+" is a stack"); roiManager("reset"); 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 { print(series_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 if(sizeC>1) { Stack.setChannel(cell_channel); } roiManager("show none"); run("Duplicate...", "title="+cell_type+"_segmentation"); seg_image=getTitle(); roiManager("reset"); n_tiles=2; //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(); if(new_width>1200) n_tiles=4; else if(new_width>4000) n_tiles=8; } roiManager("UseNames", "false"); selectWindow("Log"); print("*********Segmenting Neurons using StarDist********"); //segment neurons segment_cells(max_projection, seg_image,neuron_model_path,Modify_StarDist_Values,n_tiles,width,height); close(seg_image); //manually correct or verify if needed waitForUser("Correct "+cell_type+" ROIs if needed"); cell_count=roiManager("count"); rename_roi(); roiManager("deselect"); selectWindow(max_projection); runMacro(roi_to_label); wait(5); neuron_label_image=getTitle(); selectWindow(neuron_label_image); saveAs("Tiff", results_dir+"Neuron_label_"+max_save_name); //using this image window to get ROI names for results table rename("Neuron"); neuron_label=getTitle(); //run("Close"); 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(table_name); Table.set("File name",row,file_name); if(get_nos==false) Table.set("Total "+cell_type, row, cell_count); //set total count of neurons after nos analysis if nos selected Table.update; //decide if we need to generate a matrix for +ve or negative neurons neuron_subtype_matrix=0; if(get_nos==true) { selectWindow(max_projection); Stack.setChannel(nos_channel); run("Select None"); run("Remove Overlay"); run("Duplicate...", "title=NOS_segmentation"); nos_image=getTitle(); selectWindow(max_projection); run("Select None"); run("Remove Overlay"); Stack.setChannel(cell_channel); run("Duplicate...", "title="+cell_type+"_segmentation"); hu_image=getTitle(); roiManager("reset"); nos=segment_neuron_subtype(hu_image,nos_image,training_pixel_size,roi_location,nos_processing_dir,"NOS","",false); //noregex so left as empty "" cell_count=roiManager("count"); // in case any neurons added after nos analysis selectWindow(table_name); Table.set("Total "+cell_type, row, cell_count); Table.set("NOS "+cell_type, row, nos); Table.set("NOS/Hu "+cell_type, row, nos/cell_count); Table.update; roiManager("deselect"); roi_location=results_dir+cell_type+"_ROIs_"+file_name+".zip"; roiManager("save",roi_location ); regex=".*nos.*"; neuron_subtype_matrix+=1; } no_markers=0; if(marker_type_2==true) { no_markers=getNumber("How many markers would you like to analyse?", 1); arr=Array.getSequence(sizeC); //Array.print(arr); arr=add_value_array(arr,1); string=getString("Enter names of markers separated by comma (,)", "Names"); channel_names=split(string, ","); methods=newArray("Method 1","Method 2"); if(channel_names.length!=no_markers) exit("Channel names do not match the no of markers"); channel_options=newArray(sizeC); method_choice=newArray(sizeC); hi_lo=newArray(sizeC); Dialog.create("Select Channels and Classification Method"); for(i=0;i=2) { if(get_nos==true) { channel_names[channel_names.length]="NOS"; //add NOS to the end no_markers+=1; //if more than 1 marker, likely that no_markers is >1 print("NO markers matrix "+no_markers); } for (i = 0; i < no_markers; i++) { roi_name_table(channel_names[i],table_name); } } selectWindow(table_name); Table.save(results_dir+cell_type+"_"+file_name+".csv"); //save max projection if its scaled image, can use this for further processing later selectWindow(max_projection); saveAs("Tiff", results_dir+max_save_name); //run("Close"); roiManager("deselect"); roi_location=results_dir+cell_type+"_ROIs_"+file_name+".zip"; roiManager("save",roi_location ); roiManager("UseNames", "false"); close("*"); exit("Neuron analysis complete"); close("Image correlation. Local region size = 3 pixels"); function segment_cells(max_projection, img_seg,model_file,modify_stardist,n_tiles,width,height) { //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; roiManager("reset"); do{ if(modify_stardist==false) { //model_file="D:\\\\Google Drive\\\\ImageJ+Python scripts\\\\Gut analysis toolbox\\\\models\\\\2d_enteric_neuron_aug (1)\\\\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':'0.5', 'nmsThresh':'0.45', '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"); resetMinAndMax(); //convert the labels to ROIs runMacro(label_roi_dir,"label_original"); wait(10); close(label_image); selectWindow(max_projection); roiManager("show all"); waitForUser("Check segmentation"); choice=getBoolean("Are you happy with segmentation? If not, adjust probability and/or overlap in StarDist", "Yes", "No, try again"); if(choice==0) { roiManager("reset"); close("label_original"); } } else { waitForUser("Segmenting "+cell_type+" now. Select appropriate model file in next prompt"); print("Select Label Image option as output type in the StarDist 2D window"); run("StarDist 2D"); wait(50); temp=getTitle(); run("Duplicate...", "title=label_image"); label_image=getTitle(); run("Remove Overlay"); close(temp); if(isOpen(label_image)) { roiManager("reset"); // In case "Both Option" was selected in StarDist 2D, i.e, get both Label and ROIs //remove border labels only works on 8 bit 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 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"); } selectWindow("label_original"); resetMinAndMax(); //convert the labels to ROIs runMacro(label_roi_dir,"label_original"); wait(20); close(label_image); selectWindow(max_projection); roiManager("show all"); waitForUser("Check segmentation"); choice=getBoolean("Are you happy with segmentation? If not, adjust probability and/or overlap in StarDist", "Yes", "No, try again"); if(choice==0) { roiManager("reset"); close("label_original"); } } else { waitForUser("Error! Please select Label Image in the StarDist 2D interface instead of ROI Manager"); roiManager("reset"); } } } while(choice==0) close("label_original"); } //segment_nos(nos_image,training_pixel_size,roi_location,nos_processing_dir) function segment_neuron_subtype(hu_image,nos_image,training_pixel_size,roi_neurons,nos_processing_dir,marker_name,regex,hi_lo) { //threshold_methods=getList("threshold.methods"); //Dialog.create("NOS parameters"); //Dialog.addChoice("Choose Threshold determined from before", threshold_methods); ////Dialog.addNumber("Area fraction NOS within Hu -> NOS neuron", 40); //Dialog.show(); //nos_threshold_method=Dialog.getChoice(); //nos_value=Dialog.getNumber(); nos_value=getNumber("Enter a value for "+marker_name+" correlation coefficient.", 80); if(hi_lo==true) { hi_lo_threshold=getNumber("Enter a corr coeff for distinguishing high and low expressing "+marker_name, 80); } //arg_string=nos_image+","+d2s(training_pixel_size,3); //pass image name and decimal size arg_string=nos_image+","+hu_image+","+d2s(training_pixel_size,3); //selectWindow(nos_image); //use NOS_processing macro runMacro(nos_processing_dir,arg_string); wait(10); correlation_map=getTitle();//output from macro above roiManager("reset"); run("Enhance Contrast", "saturated=0.35"); waitForUser; //get neuron ROIs again roiManager("open", roi_neurons); roiManager("show none"); wait(5); neuron_count=roiManager("count"); //selectWindow(nos_diff_gauss); nos=0; setOption("ExpandableArrays", true); nos_array=newArray(); //setting ROIs to use names roiManager("UseNames", "true"); //roiManager("UseNames", "false"); selectWindow("Log"); print("*********Detecting "+marker_name+" neurons********"); run("Set Measurements...", "mean redirect=None decimal=2"); mName = toLowerCase(marker_name); //if(mName=="nos") regex=".*nos.*"; //else //regex=channel_regex; //count NOS neurons //regex=".*nos.*chat.*as.*" temp=split(regex, ".*"); if(temp.length==0) replace_marker_name=""; else if(temp.length==1 ) replace_marker_name=temp[0]; else replace_marker_name=String.join(temp, "_"); //replace_marker_name=toLowerCase(replace_marker_name); print("REPLACE_"+replace_marker_name); for(i=0;i=nos_value) //Provide separate macros to assess this value; default 80 { //print("Renaming"); //if it already is a NOS neuron; append nos to the end rName = toLowerCase(Roi.getName()); if (matches(rName, regex)) { if(hi_lo==true) { if(corr_coeff>hi_lo_threshold) nos_name=marker_name+"_HIGH"+replace_marker_name+"_"+(nos+1); else nos_name=marker_name+"_LOW"+replace_marker_name+"_"+(nos+1); } else nos_name=marker_name+"_"+replace_marker_name+"_"+(nos+1); } else { if(hi_lo==true) { if(corr_coeff>hi_lo_threshold) nos_name=marker_name+"_HIGH"+(nos+1); else nos_name=marker_name+"_LOW"+(nos+1); } else nos_name=marker_name+"_"+(nos+1); } roiManager("Rename",nos_name); //print("Neuron "+(i+1)+" is NOS"); nos_array[nos]=i; nos+=1; } } //wait(100); run("Clear Results"); //option to correct if a Neuron or add a NOS neuron has been erroneously identified as NOS //nos=0; //used as a counter below selectWindow(max_projection); response=1; do { roiManager("deselect"); selectWindow(max_projection); roiManager("Show All with labels"); //Stack.setDisplayMode("composite"); Stack.setDisplayMode("color"); //run("Channels Tool..."); //Stack.setChannel(nos_channel); run("Grays"); //bring ROI Manager to the front selectWindow("ROI Manager"); //need to make adding neurons more intuitive or efficient waitForUser("Verify "+marker_name+" ROIs and changes can be made in next prompt"); items = newArray("Correct to "+marker_name+" positive", "Convert to "+marker_name+" negative", "Exit/Done"); Dialog.create("Correct "+marker_name+" classification"); Dialog.addRadioButtonGroup("Choose to", items, 3, 1, "Exit/Done"); Dialog.show(); choice=Dialog.getRadioButton(); if(choice=="Convert to "+marker_name+" negative") { waitForUser("You have chosen to correct erroneously labelled "+marker_name+" neurons. Select the ROI in ROI Manager or multiple ROIs by pressing Ctrl and selecting ROIs. Press OK when done.\nHowever, if everything looks good, do not select an ROI, just press OK. Press Deselect if you have selected an ROI by mistake"); not_NOS=split(call("ij.plugin.frame.RoiManager.getIndexesAsString")); //get multiple rois if selected if(not_NOS.length==0) { print("Nothing selected"); } else { for(r=0;rneuron_count) roiManager("select", (cell_count_updated-1)); nos_select=split(call("ij.plugin.frame.RoiManager.getIndexesAsString")); //get multiple rois if selected if(nos_select.length==0) { print("Nothing selected"); } else { for(r=0;r0) { clippedIdx = Array.trim(roiIdx,k); //roiManager("select", clippedIdx); } //else roiManager("deselect"); return k; } //add a value to every element of an array function add_value_array(arr,val) { for (i = 0; i < arr.length; i++) { temp=arr[i]+val; arr[i]=parseInt(temp); } return arr; } //set if a neuron is positive or negative based on name of the roi //merge into find_ROI_name function function roi_name_table(ROI_NAME,table) { //ROINAME is normal sentence case roiName=toLowerCase(ROI_NAME); nR = roiManager("Count"); roiIdx = newArray(nR); k=0; clippedIdx = newArray(0); regex=".*"+roiName+".*"; for (i=0; i0) { clippedIdx = Array.trim(roiIdx,k); //roiManager("select", clippedIdx); } //else roiManager("deselect"); return k; Table.update; }