How to save my counter results on my image?

Good day Coral,

after having struggled with some aspects that may be less important for you but essential for a more general user group I’m able to provide an ImageJ-macro that writes a table to an image. The number of parameters has been minimized, i.e. one could perhaps think of colour choices for text and background …

To get an idea of what is provided, here is a screen shot of the macro-dialog:

  1. The first drop-down menu lists all tables that are open in ImageJ:
    Choose the source table!
    (The macro can deal with standard tables that are not titled “Results” such as the “Counts”-table.)
  2. The second drop-down menu lists all images that are open in ImageJ:
    Choose the destination image!
  3. The third drop-down menu lists five schemes for the table position in the image:
    Choose the destination position!
    (Four corners are possible and a free position that must be set by hand.)
  4. The desired font size can be set.
    Set the size of the table font!
  5. The opacity of the table can be set.
    Set the opacity of the table!

Here is what your table looks like:

And here is how it looks like when positioned in your image at the top-right position:

Please note that the table data is written to the image as an overlay which means that you can hide and show or remove it (“Image >> Overlay >> Hide/Show/Remove Overlay”). However, this is only possible if you save the image with its overlay in TIF-format. If you want to make the overlay permanent, you need to flatten the image (“Image >> Overlay >> Flatten”). Then you can save it in arbitrary formats (e.g. the image above in JPG-format) but you lose the ability to hide or remove the overlay.

Here is your image with the overlay in TIF-format: (2.4 MB)

Finally, the macro code:

/* imagej-macro "table2image" (Herbie G., 21. April 2018) */
wnds = getList("window.titles");
wnds = Array.trim(wnds, listResultWnds(wnds));
if (wnds.length<1) exit("No table!");
imgs = getList("image.titles");
if (imgs.length<1) exit("No image!");
pos = newArray("Top-Left","Top-Right","Bottom-Left","Bottom-Right","Manually");
fntSz = 18;
opac = 80;
	Dialog.addChoice("Overlay table", wnds, wnds[0]);
	Dialog.addChoice("to Image", imgs, imgs[0]);
	Dialog.addChoice("at Position", pos, pos[0]);
	Dialog.addNumber("Font size", fntSz, 0, 2, "points");
	Dialog.addNumber("Opacity (0...100)", opac, 0, 3, "%");;
table = Dialog.getChoice();
imge = Dialog.getChoice();
posit = Dialog.getChoice();
fntSz = round(Dialog.getNumber());
opac = round(Dialog.getNumber());
rslts = isOpen("Results");
if (table!="Results") { renameTables(table, rslts, true); re=true; } else re=false;
run("Input/Output...", "jpeg=85 gif=-1 file=.txt copy_column copy_row save_column save_row");
setFont("Monospaced", fntSz);
chrW = getStringWidth("0");
colsCnt = columnCount();
tbl = String.paste;
rows = split(tbl,"\n");
rowCnt = rows.length;
if (re) renameTables(table, rslts, false);
mxLen = newArray(colsCnt);
for ( j=0; j<rowCnt; j++ ) {
	vals = split(rows[j],"\t");
	for ( i=0; i<colsCnt; i++ ) {
		len = getStringWidth(vals[i]);
		if ( len>mxLen[i] ) mxLen[i]=len;
tblStr = " ";
for ( j=0; j<rowCnt; j++ ) {
	vals = split(rows[j],"\t");
	for ( i=0; i<colsCnt; i++ ) {
		len = getStringWidth(vals[i]);
		tblStr += vals[i];
		blnkCnt = round(mxLen[i] - len)/chrW + 2;
		for ( k=1; k<blnkCnt; k++ ) tblStr += " ";
	if (j<rowCnt-1) tblStr += "\n ";
rows = split(tblStr,"\n");
w = getStringWidth(rows[0]) + 20;
newImage("data", "8-bit black", w, 1.2*rowCnt*fntSz, 1);
drawString(tblStr, 10, round(1.2*fntSz), "white");
doWand(0, 15);
run("Restore Selection");
coord = newArray(2);
tablePosition( coord, posit, pos );
run("Add Image...", "image=data x="+coord[0]+" y="+coord[1]+" opacity="+opac);
run("Select None");
function listResultWnds( lst ) {
	k = 0;
	for ( i=0; i<lst.length; i++ ) {
		if (getInfo("window.type")=="ResultsTable") { 
			lst[k] = lst[i];
	return k;
function renameTables( nme, rslt, dir ) {
	r = "Results";
	z = "ResultsZ";
	if (dir) {
		if (rslt) IJ.renameResults(z);
		IJ.renameResults(nme, r);
	} else {
		if (rslt) IJ.renameResults(z, r);
function columnCount() {
	str = String.getResultsHeadings;
	str = split(str, "\t");
	return str.length;
function tablePosition( crd, p, pA ) {
	getSelectionBounds(x, y, wS, hS);
	b = 5;
	w = getWidth();
	h = getHeight();
	if (p== pA[0]) {
		crd[0] = b; crd[1] = b;
	} else {
		if (p== pA[1]) {
			crd[0] = w-wS-b; crd[1] = b;
		} else {
			if (p== pA[2]) {
				crd[0] = b; crd[1] = h-hS-b;
			} else {
				if (p== pA[3]) {
					crd[0] = w-wS-b; crd[1] = h-hS-b;
				} else {
					setSelectionLocation((w-wS)*0.5, (h-hS)*0.5);
					waitForUser("Position the selection.");
					getSelectionBounds(x, y, wS, hS);
					crd[0]=x; crd[1]=y;
/* imagej-macro "table2image" (Herbie G., 21. April 2018) */

Paste the above macro code to an empty macro window (Plugins >> New >> Macro) and run it. You may install the macro, after saving it as “_table2image.ijm”, by “Plugins >> Macros >> Install…”

Please tell me if you encounter any problems.