Chapter outline
Processing & analysis steps can be automated by writing macros
Straightforward macros can be produced without any programming using the Macro Recorder
Recorded macros can be modified to make them more robust & suitable for a wider range of images
Introduction
It is one thing to figure out steps that enable you to analyze an image,it is quite another to implement these steps for several – and perhapsmany – different images. Without automation, the analysis might neverhappen; all the mouse-moving and clicking would just be tootime-consuming, error-prone or boring, and momentarily lapses inconcentration could require starting over again.
Even a brief effort to understand how to automate analysis can producevast, long-lasting improvements in personal productivity and sanity byreducing the time spent on mind-numbingly repetitive tasks. In somestraightforward cases (e.g. converting file formats, applyingprojections or filters, or making measurements across entire images),this can already be done in ImageJ using the commands in theProcess▸ Batch▸ submenu and no programming whatsoever. But it is alsovery worthwhile to get some experience in producing macros, scripts orplugins, after which you can add your own new commands to the menus andcarry out customized algorithms with a single click of a button or pressof a key.
Macros are basically sequences of commands, written in some programminglanguage (here ImageJ’s own macro language), which can be runautomatically to make processing faster and easier. This chapter is farfrom an extensive introduction to macro-writing, but rather aims tointroduce the main ideas quickly using a worked example. Should you wishto delve deeper into the subject, there is anintroduction to thelanguage on the ImageJwebsite[1], and averyhelpful tutorial on the Fijiwiki[2],while the list ofbuilt-in macrofunctions is an indispensablereference[3].Once confident with macros, the next step would be to enter the world ofscripts and plugins. These can be somewhat more difficult to learn, butreward the effort with the ability to do more complicated things. Linksto help with this are available at https://imagej.net/Scripting.
Finally, although it is possible to use ImageJ rather than Fiji tocreate macros, Fiji’s script editor makes the process much easier bycoloring text according to what it does, so I will assume you are usingthis.
A Difference of Gaussians filter
Difference of Gaussians (DoG) filtering was introduced inFilters as a technique to enhance the appearanceof small spots and edges in an image. It is quite straightforward, buttime consuming to apply manually very often – and you might need toexperiment with the filter sizes a bit to get good results. This makesit an excellent candidate for a macro.
Recording a macro
Rather than diving into writing the code, the fastest way to get startedis to have ImageJ do most of the hard work itself. Then you only need tofix up the result. The procedure is as follows:
Open up an example (2D, non-color) image to use, ideally oneincluding small spot-like or otherwise round objects. I am using theimage found under File▸ Open Samples▸ HeLa Cells, after extracting thered channel only.
Start the Macro Recorder by choosing Plugins▸ Macros▸ Record. Makesure that
Record: Macro
appears at the top of this window (see thedrop-down list). Every subsequent click you make that has acorresponding macro command will result in the command being added tothe window.Convert your image to 32-bit. This will reduce inaccuracies due torounding whenever the filtering is applied.
Duplicate the image.
Apply Process▸ Filters▸ Gaussian Blur… to one of the images (it doesnot matter if it is the original or the duplicate), using a small sigma(e.g. 1) for noise suppression.
Apply Process▸ Filters▸ Gaussian Blur… to the other image, using a larger sigma (e.g.2).
Run Process▸ Image Calculator… and subtract the second filteredimage from the first. This produces the 'difference of Gaussians'filtered image, in which small features should appear prominently andthe background is removed.
Press the
Create
button on the macro recorder. This should cause atext file containing the recorded macro to be opened in Fiji’sScript Editor
(which you can find underFile▸ New▸ Script…).Save the text file in the plugins folder of Fiji. The file name shouldend with the extension
.ijm
(for 'ImageJ Macro'), and include anunderscore character somewhere within it.
Now you have a macro! To try it out, close Fiji completely, then startit again and reopen the original image you used. There should be a newcommand in the Plugins
menu for the macro you have justcreated[4]. Running this new command on yourexample image should give you the same result as when you applied thecommands manually. (If not, keep reading anyway and the following stepsshould fix it.)
Cleaning up
Now reopen your macro in the Script Editor. It should look somethinglike mine:
run("Find Commands...");run("32-bit");//run("Brightness/Contrast...");run("Enhance Contrast", "saturated=0.35");run("Duplicate...", "title=C1-hela-cells-1.tif");run("Find Commands...");run("Gaussian Blur...", "sigma=1");selectWindow("C1-hela-cells.tif");run("Find Commands...");run("Gaussian Blur...", "sigma=2");run("Find Commands...");imageCalculator("Subtract create", "C1-hela-cells-1.tif","C1-hela-cells.tif");selectWindow("Result of C1-hela-cells-1.tif");
Your code is probably not identical, and may well be better. One problemwith automatically generated macros is that they contain (almost)everything – often including a lot of errant clicking, or othernon-essential steps. For example, I am particularly fond of pressing Lto bring up the Find Commands
box, but these references should beremoved from the macro[5].I also changed the contrast of an image, butthis was only to look at it – and it does not need to be included in themacro. After deleting the unnecessary lines, I get:
run("32-bit");run("Duplicate...", "title=C1-hela-cells-1.tif");run("Gaussian Blur...", "sigma=1");selectWindow("C1-hela-cells.tif");run("Gaussian Blur...", "sigma=2");imageCalculator("Subtract create", "C1-hela-cells-1.tif","C1-hela-cells.tif");
Understanding the code
You can most likely work out what the macro is doing, if not necessarilythe terminology, just by looking at it. Taking the first line, run
isa function that tells ImageJ to execute a command, while 32-bit
is apiece of text (called a string) that tells it which command. Functionsalways tell ImageJ to do something or give you information, and can berecognized because they are normally followed by parentheses. Stringsare recognizable both because they are inside double inverted commas andthe script editor shows them in a different color. Notice also thateach line needs to end with a semicolon so that the macro interpreterknows the line is over.
Functions can require different numbers of pieces of information to dotheir work. At a minimum, run
needs to know the name of the commandand the image to which it should be applied – which here is taken to bewhichever image is currently active, i.e. the one that was selected mostrecently. But if the command being used by run
requires extrainformation of its own, then this is included as an extra string.Therefore
run("Duplicate...", "title=C1-hela-cells-1.tif");
informs the Duplicate
command that the image it creates should becalled C1-hela-cells-1.tif
, and
run("Gaussian Blur...", "sigma=1");}
ensures that Gaussian Blur…
is executed with a sigma value of 1.
selectWindow
is another function, added to the macro whenever youclick on a particular window to activate it, and which requires the nameof the image window to make active. From this you can see that myexample file name was C1-hela-cells.tif
. Without this line, theduplicated image would be filtered twice – and the original not at all.
Finally, the Image Calculator
command is special enough to get its ownfunction in the macro language, imageCalculator
. The first string itis given tells it both what sort of calculation to do, and that itshould create
a new image for the result – rather than replacing oneof the existing images. The next two strings give it the titles of theimages needed for the calculation.
Removing title dependancies
The fact that the original image title appears in the above macro is aproblem: if you try to run it on another image, you are likely to findthat it does not work because selectWindow
cannot find what it islooking for. So the next step is to remove this title dependency so thatthe macro can be applied to any (2D) image.
There are two ways to go about this. One is to insert a line that tellsthe macro the title of the image being processed at the start, e.g.
titleOrig = getTitle();
where getTitle()
is an example of a function that asks forinformation. The result is then stored as a variable, so that any timewe type titleOrig
later this will be replaced by the stringcorresponding to the original title[6]. Then we just find anywhere the title appears and replacethe text with our new variable name, i.e. in this case by writing
selectWindow(titleOrig);
If we do this, the window we want will probably be activated asrequired. However, there is a subtle potential problem.It is possible that we have two images open at thesame time with identical titles – in which case it is not clear whichwindow should be selected, and so the results could be unpredictable. Asafer approach is to get a reference to the image ID rather than itstitle. The ID is a number that should be unique for each image, which isuseful for ImageJ internally but which we do not normally care aboutunless we are programming. Using IDs, the updated macro code thenbecomes:
idOrig = getImageID();run("32-bit");run("Duplicate...", "title=[My duplicated image]");idDuplicate = getImageID();run("Gaussian Blur...", "sigma=1");selectImage(idOrig);run("Gaussian Blur...", "sigma=2");imageCalculator("Subtract create", idDuplicate, idOrig);
We had to change selectWindow
to selectImage
for the IDs to work. Ialso changed the title of the duplicated image to something moremeaninglessly general – which required square brackets, because itincludes spaces that would otherwise mess things up[7]. Also, because the duplicated image will be activeimmediately after it was created, I ask ImageJ for its ID at that point.This lets me then pass the two IDs (rather than titles) to theimageCalculator
command when necessary.
Adding comments
Whenever macros become more complicated, it can be hard to rememberexactly what all the parts do and why. It is then a very good idea toadd in some extra notes and explanations. This is done by prefixing aline with //
, after which we can write whatever we like because themacro interpreter will ignore it. These extra notes are calledcomments, and I will add them from now on.
Customizing sigma values
By changing the size of the Gaussian filters, the macro can be tailoredto detecting structures of different sizes. It would be relatively easyto find the Gaussian Blur
lines and change the sigma valuesaccordingly here, but adjusting settings like this in longer, morecomplex macros can be awkward. In such cases, it is helpful to extractthe settings you might wish to change and include them at the start ofthe macro.
To do this here, insert the following lines at the very beginning:
// Store the Gaussian sigma values -// sigma1 should be less than sigma2sigma1 = 1.5;sigma2 = 2;
Then, update the later commands to:
run("Gaussian Blur...", "sigma="+sigma1);selectImage(idOrig);run("Gaussian Blur...", "sigma="+sigma2);
This creates two new variables, which represent the sigma values to use.Now any time you want to change sigma1
or sigma2
you do not need tohunt through the macro for the correct lines: you can just update thelines at the top[8].
Adding interactivity
Usually I would stop at this point. Still, you might wish to share yourmacro with someone lacking your macro modification skills, in which caseit would be useful to give this person a dialog box into which theycould type the Gaussian sigma values that they wanted. An easy way to dothis is to remove the sigma value information from the run
commandlines, giving
run("Gaussian Blur...");
Since Gaussian Blur
will not then know what size of filters to use, itwill ask. The disadvantage of this is that the user is prompted to entersigma values at two different times as the macro runs, which is slightlymore annoying than necessary.
The alternative is to create a dialog box that asks for all the requiredsettings in one go. To do this, update the beginning of your macro toinclude something like the following:
Dialog.create("Choose filter sizes for DoG filtering");Dialog.addNumber("Gaussian sigma 1", 1);Dialog.addNumber("Gaussian sigma 2", 2);Dialog.show();sigma1 = Dialog.getNumber();sigma2 = Dialog.getNumber();
The first line generates a dialog box with the title you specify. Eachof the next two lines state that the required user input should be anumber with the specified prompts and default values. The other linessimply show the dialog box and then read out whatever the user typed andputs it into variables. This is documented in ImageJ’slist of built-inmacro functions.
You can download the complete example macro here.
Suggested improvements
You should now have a macro that does something vaguely useful, andwhich will work on most 2D images. It could nevertheless still beenhanced in many ways. For example,
You could close any unwanted images (e.g. the original and itsduplicate) by selecting their IDs, and then inserting
close();
commands afterwards.You could make the macro work on entire image stacks. If you want itto process each plane separately, this involves only inserting the words
stack
andduplicate
in several places – by recording a new macro inthe same way, but using a stack as your example image, you can see whereto do this. If you want the filtering to be applied in 3D, you can usetheGaussian Blur 3D…
command instead ofGaussian Blur…
You could create a log of which images you have processed, possiblyincluding the settings used. The log is output by including a
log(text);
line, wheretext
is some string you have created, e.g.text = Image name: + getTitle()
.More impressively, you could turn the macro into a full spot-detectorby thresholding the DoG filtered image, and then running theAnalyze▸ Analyze Particles… command. If you want to measure originalspot intensities, you should remember to go toAnalyze▸ Set Measurements… to make sure the measurements areredirected to the original image – which you should possibly haveduplicated at the beginning, since otherwise it will have been Gaussianfiltered by the time your macro reaches the measurement stage.
In any case, the process of developing a macro is usually the same:
Record a macro that does basically the right thing
Remove all the superfluous lines (contrast adjustment, errantclicking etc.)
Replace the image titles with image ID references
Add comments to describe what the macro is doing
Track down bugs and make improvements
4. Without an underscore in the file name, the command will not be added to the menu.
5. Actually, this has been fixed in more recent versions of ImageJ - Find Commands
is no longer included in a recorded macro. However, it remains in this tutorial to show the process of cleaning up unnecessary lines anyway.
6. There is nothing special about titleOrig
– this text can be changed to any variable name you like, so long as it is one word and does not contain special characters.
7. In ImageJ’s macro language, spaces in the string telling a command what to do are used to indicate that a separate piece of information is being given. So titles or file names that require spaces need to be put inside square brackets.
8. Note that +
is used to join multiple strings into one, converting numbers into strings as needed. Therefore in this case the lines sigma=+2
and sigma=+sigma2
would each give us the same result: one longer string with the extra part appended at the end, i.e. sigma=2.