Export movie of Orthogonal projections with crosshair

Hi,

I would like to make a movie from a z-stack with it’s corresponding orthogonal projections (+ crosshairs) moving from one extremity to the other.

I found the following macro written by [Martin Höhne] (http://imagej.1557.x6.nabble.com/Orthogonal-Views-td5005236.html)

/*
 *  Macro to fullfill this task I asked for in the ImageJ-list:
 *  
 *  ... when I have a z-stack (RGB) and view this stack with the orthogonal views switched on,
 *  I can play that stack, i.e. move through the z-stack and the yellow cross-hair is
 *  moving in parallel in the yz and xz view.
 *  Is there a way of saving this whole view, i.e. xy, yz and xz including the moving
 *  yellow crosshair in one series/avi?
 *
 * The idea how to solve this (use reslice and combine stacks) is from the reply from Tiago Ferreira.
 *
 * Martin Hoehne 2012-10-08
 *
 * - added color variables 2013-10-21
 *
 */

// define z-color globally. Needs to be available in the macro and in a function
var colZ_arr=newArray(255,255,255); //=same color as colZ in RGB

macro "Ortho view movie" {
run("Overlay Options...", " "); // this ensures that the xy-crosshair and labels are shown in all stacks. Removes the
// Overlay option "Set stack position"

//colors

//color for X view
colX="red";
colX_arr=newArray(255,0,0); //=same color as colX in RGB

//color for Y view
colY="cyan";
colY_arr=newArray(0,255,255); //=same color as colY in RGB

//color for Z view (xy-view)
colZ="white";
//colZ_arr=newArray(0,255,0); //=same color as colX in RGB //already defined globally

//color for background canvas
colCanvas_arr=newArray(40,40,40); //=dark grey


if (nImages()!=0) exit ("please close all other images before running the macro");

//open file and create results dir
        path = File.openDialog("Select the file");
        open(path);  
//run("Bat Cochlea Volume (19K)"); //works as a testfile
 
getDimensions(width, height, channels, slices, frames);
getVoxelSize(voxwidth, voxheight, depth, unit);

run("Orthogonal Views");

msg1 = "\nPostion the crosshair in the xy window."+
        "\n(original stack)"+
        "\n \nWhen done, continue with <OK>";
waitForUser(msg1);

setBatchMode(true);

// get the position where the user has put the yellow cross-hair in the xy-stack. The position is read from the titles
// of the YZ and XZ images using the function "ccord" --> see below
for (i=1; i<=3; i++){
        img=getTitle();
       
        if (substring(img,0,2)=="YZ") {
                yzID = getImageID();
                x=coord(img);
                rename("YZ_"+x); //no space in title allowed to combine stacks later in macro
                yztitle=getTitle();
        }
        if (substring(img,0,2)=="XZ") {
                xzID = getImageID();
                y=coord(img);
                rename("XZ_"+y); //no space in title allowed to combine stacks later in macro
                xztitle=getTitle();
        }
        close();
        // in the first round the original image is still selected and closed
        // in the next two round the YZ and XZ views are selected, resp.
        // I could not find another way of getting hold of the titles of the
        // XZ and YZ windows
}


// image has to be reopened
//("Bat Cochlea Volume (19K)");
open(path);

run("RGB Color");
imgID=getImageID();

setLineWidth(20);


// reslice YZ
// produce a single y-slice at the x-position chosen by the user in the xy view by placing the crosshair
        selectImage(imgID);
        makeLine(x,0,x,height);
        run("Add Selection...", "stroke="+colY+" width=1"); //add the vertical crosshair line to the selection
      run("Reslice [/]...", "output="+ depth+" slice_count=1 rotate");
      getDimensions(w,h,c,s,f); //dimensions are dependent on depth, i.e. slice thickness
      // colY-colored frame around the image
      setBackgroundColor(colY_arr[0],colY_arr[1],colY_arr[2]);
      run("Canvas Size...","width="+w+4+" height="+h+4+" position=Center");
        rename("YZ_"+ x);
        orthoYZ=getImageID();
        makestack(orthoYZ, slices, width, height, voxwidth, voxheight, depth, 1); //makestack function see below

// reslice XZ
// produce a single x-slice at the y-position chosen by the user in the xy view by placing the crosshair
        selectImage(imgID);
        makeLine(0,y,width,y);
        run("Add Selection...", "stroke="+colX+" width=1"); //add the horizontal crosshair line to the selection
      run("Reslice [/]...", "output="+ depth+" slice_count=1");
    getDimensions(w,h,c,s,f); //dimensions are dependent on depth, i.e. slice thickness
      // colX-colored frame around the image
      setBackgroundColor(colX_arr[0],colX_arr[1],colX_arr[2]);
     
      run("Canvas Size...","width="+w+4+" height="+h+4+" position=Center");
        rename("XZ_"+ y);
        orthoXZ=getImageID();
        makestack(orthoXZ, slices, width, height, voxwidth, voxheight, depth, 2); //makestack function see below
       
       
// flatten the crosshair onto the original xy-stack
        selectImage(imgID);
        run("Flatten", "stack");
       
// white frame around the xy image
        setBackgroundColor(colZ_arr[0],colZ_arr[1],colZ_arr[2]);
        run("Canvas Size...", "width="+width+4+" height="+height+4+" position=Center");

// combine views
        stk1=getTitle();
        setBackgroundColor(colCanvas_arr[0],colCanvas_arr[1],colCanvas_arr[2]);
        run("Combine...", "stack1="+ stk1 +" stack2="+ yztitle);
        rename("combi1");
        run("Combine...", "stack1=combi1" +" stack2="+ xztitle +" combine");
        setBatchMode(false);

// grey frame around the image + labeling
        getDimensions(w,h,c,s,f); //dimensions of the combined stacks
        run("Canvas Size...", "width="+w+20+" height="+h+40+" position=Center");
        setFont("Sanserif", 14);
        makeText("xy view", 20, 0);
        run("Add Selection...", "stroke="+colZ);
        makeText("xz view", 20, h+18);
        run("Add Selection...", "stroke="+colX);
        makeText("yz view", w-44,0);
        run("Add Selection...", "stroke="+colY);

// "stupid" command to unselect the "yz view" text -- most likely there are more elegant ways
        makePoint(w*2, h);

}
       
//-FUNCTIONS-----------------------------------------------------------------------------------//

function coord(imgname) {
        // function returns the x or y value of the selected section. Extracted from the image title
        //(e.g. title = YZ 123) --> 123 is returned

        string=substring(imgname,3,lengthOf(imgname));
        return parseInt(string);//Converts string to an integer and returns it. Returns NaN if the string cannot be converted into a integer.
}




function makestack(imgID, slices, xmax, ymax, voxwidth, voxheight, depth, orient) {
        // duplicate the YZ or the XZ image resp. z-times (i.e. make a stack with
        // identical number of slices as the original x-y-z stack.
        // Draw a line in each of the images indicating a different z-position. If the z-slice
        // is thicker than 1 pixel (interpolated) the line is drawn in the middle of the slice position
        //
        // The last argument of the function (orient) is needed for the orientation. I.e. to differentiate between x and y view
       
        setForegroundColor(colZ_arr[0],colZ_arr[1],colZ_arr[2]);
        neuID = getImageID();
        titel = getTitle();
        run("Select All");
        run("Copy");
        run("Duplicate...", "title=["+titel+"]");
        dup=getImageID();
        selectImage(neuID);
        close();
        selectImage(dup);
        for (i=0; i<slices; i++) {
                run("Paste");
                // draw line indicating the z-plane
                        if (orient==1) {
                                zspace=depth/voxwidth; //take thickness of z-slices into account
                                drawLine(i*zspace+zspace/2,2,i*zspace+zspace/2,ymax+1);
                        }
                        if (orient==2) {
                                zspace=depth/voxheight;
                                drawLine(2,i*zspace+zspace/2,xmax+1,i*zspace+zspace/2);
                        }
                run("Add Slice");
        }
        run("Delete Slice");
} 

I also found the following but it has no crosshairs and doesn’t include the original XY images. ( [Tiago Ferreira-2] (http://imagej.1557.x6.nabble.com/Orthogonal-Views-td5005236.html)

// ---- ---
run("T1 Head (2.4M, 16-bits)");
img = getImageID();
getVoxelSize(width, height, depth, unit);
vws = newArray("Top", "Left", "Bottom", "Right");

setBatchMode(true);
  // Create projections
  for (i=0; i<vws.length; i++) {
      selectImage(img);
      rtt = "";
      if (i%2) rtt = " rotate";
      run("Reslice [/]...", "output="+ depth +" start="+ vws[i] + rtt);
      rename(vws[i]);
  }

  // combine top views
  run("Combine...", "stack1="+ vws[0] +" stack2="+ vws[2] +" combine");
  rename("TB");

  // Append side views
  run("Combine...", "stack1="+ vws[1] +" stack2=TB");
  rename("LTB");
  run("Combine...", "stack1=LTB stack2="+ vws[3]);

setBatchMode("exit & display");
doCommand("Start Animation [\\]");
// ---- --- 

But sadly I cant make it work. Would anyone know how to fix the macro or have an alternative ?

I know that I could export the projections but I would need to synchronise them and also they wouldn’t have the cross-hairs.

Thank you very much