/* Michael Cammer mcammer@gmail.com cammem01@nyumc.org Macros for ImageJ Java 1.8 This collection of macros are the ones I like to have on hand most of the time. More at http://microscopynotes.com/imagej/index.html 20180411 In subtract BG in ROI macro, with IJ 1.51 PlotProfile no longer send results to the Results window so rewritten with a measurements loop. 20200108 added scalebar dialogue to simple merge macros */ var clipsize = 150; // edge size of the box for clipping cells for montage var snipwindow = -1; var projectiondepth = 16; // default depth for projection of variable depth var tab = " \t"; var scalebarSize = 10; // Scalebar size for simple merge with options /* ============================================================= Should be updated each time macros are edited */ function checkCurrentVersion() { requires('1.52f'); } /* ============================================================= Standard ImageJ allows for installing macros from the macro editor text window by typing ctrl i. Fiji does not. Therefore, to easily update macros in Fiji, change the path of the current macro file being edited in this macro and use ctrl s followed by F4 to reinstall the macros from the Fiji text editor. */ macro "Install Macros [F4]" { run("Install...", "install=D:\\20171214\\Macro.txt"); } /* ============================================================ WITH OPTIONS SIMPLE FIGURE BASED ON MULTICHANNEL IMAGE DISPLAYED AS IS version 20200108 Makes a montage figure based on a composite image as displayed. The required input is a composite color image. Use adjusts the channels to desied colors and contrast. Running the macro either merges an entire image or the region of interest selected by user. Uses existing contrast without further prompts. But there is a prompt when there are 3, or more channels to find out whether all images in one row or two rows. */ macro "Simple montage figure with options" { requires("1.52f"); Stack.getDimensions(width, height, channels, slices, frames); getVoxelSize(width, height, depth, unit); if ( (slices>1) || (frames>1) ) exit("Only works for single Z, time, or position images.\nAsk for modification if you need implemented."); if ( (slices > 1) || (frames > 1) ) exit("Requires single Z and single timepoint image"); if (channels < 2) exit("Requires composite color image."); realtitle = getTitle; run("Duplicate...", "duplicate"); title = getTitleStripExtension(); rename(title); if (channels < 2) exit("Not a composite image with 2 or more channels."); if (channels == 2) { Dialog.create("get montage info"); Dialog.addNumber("Size of scalebar ("+unit+"): ", scalebarSize); Dialog.show; scalebarSize = Dialog.getNumber(); montageX=3; montageY=1; } if (channels == 3) { Dialog.create("get montage info"); //Dialog.addMessage(); Dialog.create("Radio Buttons"); items = newArray("One row of four panels", "2 X 2 panels"); Dialog.addRadioButtonGroup("Three channels will result in four panels.\nClick which layout preferred.", items, 2, 1, ""); Dialog.addNumber("Size of scalebar ("+unit+"): ", scalebarSize); Dialog.show; if ( Dialog.getRadioButton == "One row of four panels") { montageX=4; montageY=1; } else { montageX=2; montageY=2; } scalebarSize = Dialog.getNumber(); } if ((channels==4) || (channels==5)) { Dialog.create("get montage info"); Dialog.create("Radio Buttons"); items = newArray("One row of panels", "3 X 2 panels"); Dialog.addRadioButtonGroup("Click which layout preferred.", items, 2, 1, ""); Dialog.addNumber("Size of scalebar ("+unit+"): ", scalebarSize); Dialog.show; if ( Dialog.getRadioButton == "One row of panels") { montageX=channels+1; montageY=1; } else { montageX=3; montageY=2; } scalebarSize = Dialog.getNumber(); } if (channels > 5) exit("Not programmed for more than 5 channels; ask for modification if you need this."); simpleMergeAndMontageWithOptions (title, montageX, montageY); } /* ========================================= WITH OPTIONS This function does the work of montaging. It takes a number of arguments. title = text Title of the image, will have montage added to it. montageX = integer How many columns in montage. montageY = integer How many rows in montage. To be implemented in future: grayscale = true/false To be coded someday. This directs the function whether to use LUTs as is or make individual panels in grayscale. */ function simpleMergeAndMontageWithOptions (title, montageX, montageY) { run("Duplicate...", "duplicate"); rename(title); original = getImageID; run("RGB Color"); // This should make a new window run("Select All"); run("Copy"); setPasteMode("Copy"); close(); selectImage(original); Stack.setDisplayMode("color"); run("RGB Color"); run("Hyperstack to Stack"); setSlice(nSlices); run("Add Slice"); run("Paste"); run("Scale Bar...", "width="+scalebarSize+" height=4 font=14 color=[White] background=None location=[Lower Right] bold"); run("Colors...", "foreground=white background=black selection=yellow"); if ((nSlices==5) & (montageY == 2)) { // Make the last panel white if 3 x 2 with only five panels. run("Add Slice"); run("Select All"); run("Fill", "slice"); } run("Select None"); run("Make Montage...", "columns="+montageX+" rows="+montageY+" scale=1 border=2 use"); rename(title+"_montage"); close(title); close(title+" (RGB)"); //Following steps copy the result to the system clipboard for pasting into another application. Only tested on win7. run("Select All"); run("Copy to System"); run("Select None"); } /* ============================================================ SIMPLE FIGURE BASED ON MULTICHANNEL IMAGE DISPLAYED AS IS version 20180930 Makes a montage figure based on a composite image as displayed. The required input is a composite color image. Use adjusts the channels to desied colors and contrast. Running the macro either merges an entire image or the region of interest selected by user. Uses existing contrast without further prompts. But there is a prompt when there are 3, or more channels to find out whether all images in one row or two rows. */ macro "Simple montage figure" { requires("1.52f"); Stack.getDimensions(width, height, channels, slices, frames); if ( (slices>1) || (frames>1) ) exit("Only works for single Z, time, or position images.\nAsk for modification if you need implemented."); if ( (slices > 1) || (frames > 1) ) exit("Requires single Z and single timepoint image"); if (channels < 2) exit("Requires composite color image."); title = getTitleStripExtension(); if (channels < 2) exit("Not a composite image with 2 or more channels."); if (channels == 2) { montageX=3; montageY=1; } if (channels == 3) { Dialog.create("get montage info"); //Dialog.addMessage(); Dialog.create("Radio Buttons"); items = newArray("One row of four panels", "2 X 2 panels"); Dialog.addRadioButtonGroup("Three channels will result in four panels.\nClick which layout preferred.", items, 2, 1, ""); Dialog.show; if ( Dialog.getRadioButton == "One row of four panels") { montageX=4; montageY=1; } else { montageX=2; montageY=2; } } if ((channels==4) || (channels==5)) { Dialog.create("get montage info"); Dialog.create("Radio Buttons"); items = newArray("One row of panels", "3 X 2 panels"); Dialog.addRadioButtonGroup("Click which layout preferred.", items, 2, 1, ""); Dialog.show; if ( Dialog.getRadioButton == "One row of panels") { montageX=channels+1; montageY=1; } else { montageX=3; montageY=2; } } if (channels > 5) exit("Not programmed for more than 5 channels; ask for modification if you need this."); simpleMergeAndMontage (title, montageX, montageY); } /* ========================================= This function does the work for Simple merge as is. It takes a number of arguments. title = text Title of the image, will have montage added to it. montageX = integer How many columns in montage. montageY = integer How many rows in montage. To be implemented in future: grayscale = true/false To be coded someday. This directs the function whether to use LUTs as is or make individual panels in grayscale. */ function simpleMergeAndMontage (title, montageX, montageY) { run("Duplicate...", "duplicate"); rename(title); original = getImageID; run("RGB Color"); // This should make a new window run("Select All"); run("Copy"); setPasteMode("Copy"); close(); selectImage(original); Stack.setDisplayMode("color"); run("RGB Color"); run("Hyperstack to Stack"); setSlice(nSlices); run("Add Slice"); run("Paste"); run("Scale Bar...", "width=10 height=4 font=14 color=[White] background=None location=[Lower Right] bold"); run("Colors...", "foreground=white background=black selection=yellow"); if ((nSlices==5) & (montageY == 2)) { // Make the last panel white if 3 x 2 with only five panels. run("Add Slice"); run("Select All"); run("Fill", "slice"); } run("Select None"); run("Make Montage...", "columns="+montageX+" rows="+montageY+" scale=1 border=2 use"); rename(title+"_montage"); close(title); close(title+" (RGB)"); //Following steps copy the result to the system clipboard for pasting into another application. Only tested on win7. run("Select All"); run("Copy to System"); run("Select None"); } //===================================================================================== // Macro for ImageJ by Michael Cammer michael@coxcammer.com // A roundish (or circular, but with pixel edges, not vectors) ROI is drawn on a timelapse stack such that the // center of the circle is the centroid of the feature to be measured over time and that the entire object at every // time is contained within the perimeter. // Then radii spaced by step size perimeter / n step around and are resampled in the time axis // and pasted into a new window. // After I wrote this, somebody posted a similar plugin version at http://rsbweb.nih.gov/ij/plugins/radial-reslice/ // This macro produces radial reslices from the center out. The original version of this reslicing went from the // periphery such that the alignment was along the traced outer edge and could be used for the annuluar // intensity and spatial distribution in relation to the outer edge of spreading cells instead of in relation to the center // and some slices never even went to the very center. /* To make wedges instead of single slices, overshoot the rotation (more than 360 degrees number_of_radii) and use the project variable depth function projectStackVariableWidth("Max Intensity", 4); to combine slices such as 10 degrees wedges. Warning: as the images get larger, the further fromt he center, slices will be missed. To catch every pixel, as the images get larger, the angular step size has to get smaller, which slows things down. The macro could be modified to use the straighten command with lines of thicker pixels, but this would require a projection step for every slice and would get evern slower. Implementation 9 Feb 2020 adds spatial scale to properties. X axis in in distance units of original image and Y axis is in radians (which could easily be changed to degrees). */ //===================================================================================== macro"Radial Sweep as a circle [q]"{ number_of_radii = 720; // number of pixels on perimeter of circle to be sampled. extrasteps = 16; // overshooting to adjust for when slices are projected together maxradius = 5 + round(calculateMaximumRadius()/2); // this is to find the longest radius to make a new window this width or to rescale the shorter lines this length anglestep = 2 * PI / number_of_radii; requires("1.52"); getVoxelSize(pixelwidth, pixelheight, depth, distanceunit); // will be used to reassign scale at the end // Gets the center of the area to be resliced. Could be recoded shorter with new get list functions. run("Set Scale...", "distance=1 known=1 pixel=1 unit=px"); run("Set Measurements...", " centroid redirect=None decimal=0"); run("Measure"); xc = getResult("X", nResults-1); yc = getResult("Y", nResults-1); selectWindow("Results"); run("Close"); Stack.getDimensions(width, height, channels, slices, frames); //channels and slices will be swapped if ( (slices > 1) || (frames > 1) ) exit("Requires multichannel image with no Z or T."); //Save the LUTs to apply later. Stack.setChannel(1); getLut(reds1, greens1, blues1); if (channels > 1) { Stack.setChannel(2); getLut(reds2, greens2, blues2); } if (channels > 2) { Stack.setChannel(3); getLut(reds3, greens3, blues3); } if (channels > 3) { Stack.setChannel(4); getLut(reds4, greens4, blues4); } if (channels > 4) { waitForUser("Warning: LUTs only operate correctly on maximum of 4 channels."); } // Manage the image data type. run("Select None"); run("Duplicate...", "title=tempimage duplicate"); run("Restore Selection"); run("Interpolate", "interval=1"); // If a vector ROI, need to convert to rasters ROI run("Hyperstack to Stack"); slices = nSlices(); if (slices != channels) exit("Something wrong with number of channels."); realtitle = getTitle; originaltitle = getTitleStripExtension(); bits = bitDepth(); temptitle = originaltitle + "_reslice"; newtitle = originaltitle + "_radial"; newslices = 1 + number_of_radii + extrasteps; newbits = "" + bits + "-bit Black"; setBatchMode(true); // uncomment for faster execution newImage(temptitle, newbits, maxradius, slices, newslices); stream = getImageID(); setPasteMode("Copy"); s=1; for (i=0; i<2*PI+(anglestep*extrasteps); i=i+anglestep) { edgex = xc + maxradius * sin(i); edgey = yc + maxradius * cos(i); selectImage("tempimage"); makeLine(xc, yc, edgex, edgey); run("Reslice [/]...", "input=1.000 output=1.000 start=Top"); run("Select All"); run("Copy"); run("Close"); selectImage(stream); setSlice(s); s++; run("Paste"); } //for i setBatchMode("exit and display"); // uncomment for faster execution //projectStackVariableWidth("Max Intensity", 4); //Reorient into XY run("Select None"); run("Reslice [/]...", "output=1.000 start=Top avoid"); rename(newtitle); run("Stack to Hyperstack...", "order=xyczt(default) channels=" + slices +" slices=1 frames=1 display=Composite"); setVoxelSize(pixelwidth, anglestep, depth, distanceunit); //Apply the original LUTs Stack.setChannel(1); setLut(reds1, greens1, blues1); if (channels > 1) { Stack.setChannel(2); setLut(reds2, greens2, blues2); } if (channels > 2) { Stack.setChannel(3); setLut(reds3, greens3, blues3); } if (channels > 3) { Stack.setChannel(4); setLut(reds4, greens4, blues4); } rename(newtitle); close(temptitle); // closes the reslice image close("tempimage"); } //end macro("Radial Sweep") macro "Test calculateMaximumRadius()" { print(calculateMaximumRadius()); selectWindow("Log"); } //========================================================= // This function is required by the macro "Diameter Sweep" to calculate the // longest possible radius if the selection is non-circular. // Uses Feret's diameter. function calculateMaximumRadius(){ run("Clear Results"); run("Set Measurements...", " mean feret's redirect=None decimal=3"); run("Measure"); diameter = getResult("Feret",(0)); selectWindow("Results"); run("Close"); return (diameter); } // function calculateMaximumRadius //=========================================================================== macro "Change Values (works on stacks)"{ // as implemented, does not work on multichannel hyperstacks for individual channels run("32-bit"); for (i=1; i<=nSlices(); i++){ setSlice(i); changeValues(0,128,NaN); } } //=========================================================================== // This macro takes all open stacks and projects them. Depending on the filename, in this case // "C=#", the projection method may be different. The projection command automatically names // the new windows with the type of projection followed by the original filename. //=========================================================================== macro "Project all open stacks" { projectAllOpenStacks(); } // "Project all open stacks" function projectAllOpenStacks(){ n = nImages(); for (i=1; i<=n; i++) { selectImage(i); t = getTitleStripExtension(); if ( endsWith(getTitle(), "C=2") ) run("Z Project...", "start=1 stop="+nSlices+" projection=[Min Intensity]"); else run("Z Project...", "start=1 stop="+nSlices+" projection=[Max Intensity]"); rename(t+"_proj"); } // for i } // end projectAllOpenStacks() macro "=========================="{} //============================================================================= /* Framework for iterating through ROI Manager list and spitting out measurments in the Log window. In this version, intensity and area are measured. A band is drawn arounf the outside of the area and this mean intensity is treated as the background which is subtracted from the mean of the ROI. */ macro "measurements in ROI Manager list"{ run("Set Measurements...", "area mean standard shape redirect=None decimal=3"); numberCells = roiManager("count"); print(getTitle); print("Area \t Mean"); for (i=0; iOptions>Colors... //4. Run this macro. // macro 'draw ROI on range of slices in stack'{ checkCurrentVersion(); image_to_process = getImageID(); Dialog.create("draw on slices..."); Dialog.addNumber("first slice to draw on", 1); Dialog.addNumber("last slice to draw on", nSlices()); Dialog.show(); first = Dialog.getNumber(); last = Dialog.getNumber(); if (last < first) { temp=first; first=last; last=temp; } if (first < 1) exit("first slice must be 1 or greater"); if (last > nSlices()) exit("last slice must be the stack size or smaller"); n = roiManager("count"); // get the last ROI in the manager selectImage(image_to_process); roiManager('select', n-1); for (i=first; i<=last; i++){ setSlice(i); run("Draw", "slice"); } // for loop run("Select None"); resetMinAndMax(); } // draw ROI on range of slices in stack /* This macro requires a grayscale stack any bit depth as input. It duplicates this stack twice, thus requiring sufficient RAM. The output is 8 bit per channel RGB. */ macro "RGB merge adjacent slices into a new stack"{ //checkCurrentVersion(); original = getImageID(); t = getTitleStripExtension(); run("Select None"); rename("red"); run("Duplicate...", "title=green duplicate"); setSlice(1); run("Select All"); run("Copy"); run("Add Slice"); run("Paste"); run("Set...", "value=0 slice"); run("Select None"); run("Duplicate...", "title=blue duplicate"); run("Add Slice"); selectWindow("red"); setSlice(nSlices); run("Add Slice"); run("Add Slice"); selectWindow("green"); setSlice(nSlices); run("Add Slice"); run("Merge Channels...", "c1=red c2=green c3=blue"); rename(t+"_runRGB"); } //RGB merge adjacent slices into a new stack //================================================================== /* This macro is for making a movie of a stack that loops, especially useful for animated GIFs of 3D projections that rock back and forth. Downside: doubles the size of the stack. */ macro "Loop stack" { t = getTitle(); run("Duplicate...", "title=xyztemp duplicate"); run("Reverse"); run("Concatenate...", " title="+t+"_3D image1=["+t+"] image2=[xyztemp] image3=[-- None --]"); } macro "make test stack for Projections of variable width" { size = 25; slices = 50; width = 256; newImage("Untitled", "8-bit black", width, width, slices); setColor(255, 255, 255); for (i=1; i