// Time-Lapse Video Macros
//
// Based onto the Time-lapse_Capture macro, written by Wayne Rasband and available at the
// http://rsb.info.nih.gov/ij/macros/Time-lapse_Capture.txt
 
// Author: Gilles Carpentier, Faculte des Sciences et Technologie, Universite Paris 12 Val de Marne

// Uses the QuickTime Capture plugin to capture a sequence. Requires a version of the plugin at
// <http://rsb.info.nih.gov/ij/plugins/qt-capture.html>
// dated 2007/05/22 or later.

// Notice:
// Using another macro during time-lapse capture may cause 
// an error unless you are running ImageJ 1.39q or later.
//
// This toolset proposes some functionalities around the video quicktime resources of ImageJ.
// The image source is the Live ImageJ window (invisible in batch mode) and available
// from the File->Import->Video menu.

// It is advised to first start the live video, using the "Live Preview [l]" macro, before to use the
// other macros of this macroset.

// Press space bar to stop the image preview and live histogram. 
// Live histogram can be temporarely supended by using the "Stop Live Histogram [s]" macro menu
// (use this function before handling acquired images or stacks).
// Capture (as blind capture) can be aborted using the "Abort Timelapse Capturing [a]" macro menu
// (the acquired stack becomesTimeLapseVideoMacros.txt visible in its states).

// - Live grey levels or color histogram can be visualised by "Live Histogram macro" (shortcut h)
// and "Live RGB Histogram [r]" menus. It is possible to switch from one to the other,
// keeping the live histogram viewing. 
// - The Y scale of the histogram can be adjusted by the "Increase Live Histogram Atenuation"
// and the "Decrease Live Histogram Atenuation" macro menus (respectively shorcuts "i" and "d"),
// to dinamicaly optimize the live histogram visualisation.
// - The interval between two calculated histograms, can be set by the
// "Increase Live Histogram Interval" and the "Decrease Live Histogram Interval" macro menus
// (respectively shorcuts "m" and "q"). This function can be used during live hitogram visualisation.
// Some statistical infomations are given into the status area of the ImageJ toolbar (min and max level, 
// highest value of the histogram, and the correponding level, visualisation parameters)

// - Time Lapse image acquisition can be performed with a live preview by the
// "Timelapse Capture using "Live" ImageJ window" macro menu, or in batch with the
// "Blind Timelapse Capture using "Live" ImageJ window" macro menu.
// The live histogram viewing, if activated, will be interrupted during the image acquisitions.

// Use macro menus to animate the acquired time lapse image stacks.
// Change the default time lapse settings with the "Timelapse Capture Settings" macro menu.

var histo=0, yHistoAtenuation=40,HistoInterval = 0.1;
var TimeLapseFrames = 20, TimeLapseInterval = 0.5, abortTimeLapse=0; // seconds
var delais=1000,stopHisto=0,restartHisto=0,acqui=0,RGBhistoSet=0;
var greyHisto=newArray(256);
var redHisto=newArray(256);
var greenHisto=newArray(256);
var blueHisto=newArray(256);

macro "Live Preview [l]" {
	if (! isOpen("Live (press space bar to stop)"))
           doCommand("Video"); 
}

macro "-" {}

macro "Timelapse Capture using \"Live\" ImageJ window [c]" {
	TimeLapseConditions ();
	TimeLapseCapture (0,TimeLapseInterval,TimeLapseFrames);
}

macro "Blind Timelapse Capture using \"Live\" ImageJ window [b]" {
	TimeLapseConditions ();
	TimeLapseCapture (1,TimeLapseInterval,TimeLapseFrames);
}

macro "Timelapse Capture Settings [t]" {
	TimeLapseConditions ();
	Dialog.create("Timelapse Capture Settings");
	Dialog.addMessage("Choose the number of frames \nand frame interval \(sec\)");
	Dialog.addNumber("Nb frames", TimeLapseFrames, 0, 3, " ");
	Dialog.addNumber("Nb frames", TimeLapseInterval, 2, 4, "sec");
	Dialog.show();
	TimeLapseFrames=Dialog.getNumber();
	TimeLapseInterval=Dialog.getNumber();
}

macro "Abort Timelapse Capturing [a]" {
	if (call("ij.macro.Interpreter.isBatchMode")=="true") {
		beep();
		if (acqui==1) abortTimeLapse=1;
	}
}

macro "-" {}
	
macro "Animate" {
	if (call("ij.macro.Interpreter.isBatchMode")=="true") {beep();exit;}
	doCommand("Start Animation [\\]");
}

macro "Animate Settings" {
	run("Animation Options...");
}

macro "-" {}

macro "Get a Single Image using \"Live\" ImageJ window [g]" {
	TimeLapseConditions ();
	if (! isOpen("Live (press space bar to stop)")) {doCommand("Video"); wait(2*delais); }
	acqui=1;abortTimeLapse=0;
	setBatchMode(true);
	selectWindow ("Live (press space bar to stop)");
	newImage("Grab Image","RGB",getWidth,getHeight,1);
	grabima = getImageID;
	selectWindow ("Live (press space bar to stop)");
	run("Copy");
	selectImage(grabima);
	run("Paste");
	run("Select None");
 	setBatchMode(false);
	acqui=0;
	if (histo==1 && restartHisto== 1) LiveRGBHisto (RGBhistoSet);restartHisto=0;
}

macro "-" {}

macro "Live Grey Level Histogram [h]" {
	if (call("ij.macro.Interpreter.isBatchMode")=="true") {
		beep();
		if (acqui==1) exit;
		RGBhistoSet=0;exit;
	}
	LiveRGBHisto (0);
}

macro "Live RGB Histogram [r]" {
	if (call("ij.macro.Interpreter.isBatchMode")=="true") {
		beep();
		if (acqui==1) exit;
		RGBhistoSet=1;exit;
	}
	LiveRGBHisto (1);
}
macro "Stop Live Histogram [s]" {
	if (call("ij.macro.Interpreter.isBatchMode")=="true") {
		beep();
		if (acqui==1) exit;
		stopHisto=1; wait (delais);stopHisto=0;
	}
}

macro "-" {}

macro "Increase Live Histogram Atenuation [i]" {
	yHistoAtenuation= yHistoAtenuation+5;
}

macro "Decrease Live Histogram Atenuation [d]" {
	yHistoAtenuation= yHistoAtenuation-5;
	if (yHistoAtenuation < 0) yHistoAtenuation=0;
}

macro "Increase Live Histogram Interval [m]" {
	HistoInterval = HistoInterval+0.05;
}

macro "Decrease Live Histogram Interval [q]" {
	HistoInterval =HistoInterval-0.05;
	if (HistoInterval < 0.05) HistoInterval = 0.05;
}

// functions //

function TimeLapseConditions () {
	if (call("ij.macro.Interpreter.isBatchMode")=="true") {
		beep();
		if (acqui==1) exit;
		if (histo==1) stopHisto=1; restartHisto=1;wait (delais);
	}
}

// Timelapse function
function TimeLapseCapture (blind,interval,frames) {
	setBatchMode(true);
	acqui=1; abortTimeLapse=0;
	setPasteMode("Copy");
	if (blind == 0) {if (! isOpen("Live (press space bar to stop)")) {doCommand("Video"); wait(2*delais); }}
	// check for the video window status
	if (blind == 1) {
		if (isOpen("Live (press space bar to stop)")) {
			selectWindow ("Live (press space bar to stop)");
			close (); wait(delais);
		} 
		doCommand("Video"); wait(2*delais);
	}
	start = getTime;
	// acquisition
	for (i=1; i<=frames; i++) {
		showProgress(i, frames);
		selectWindow ("Live (press space bar to stop)"); run("Copy");
		if (i==1) {
			newImage("Video","RGB",getWidth,getHeight,frames);
			stack = getImageID;
		}
		selectImage(stack);
		setSlice(i);
		run("Paste");
		setMetadata(d2s((getTime-start)/1000,2));
		while (getTime-start<interval*i*1000) wait(1);
		if (abortTimeLapse==1) {abortTimeLapse=0;acqui=0;histo=0;stopHisto=0;setBatchMode("exit and display");exit;}
	}
	run("Select None");
	setBatchMode(false);
	acqui=0;
	if (histo==1 && restartHisto==1) LiveRGBHisto (RGBhistoSet);restartHisto=0;
}

// live histogram printing function
function LiveRGBHisto (RGBhisto) {
	histo=1;stopHisto=0;RGBhistoSet=RGBhisto;
	xhisto=256; yhisto=240;
	HistoInterval = 0.05; // seconds
	if (! isOpen("Live (press space bar to stop)")) {doCommand("Video"); wait(2*delais); }
	if (! isOpen("Live Histogram")) {
		newImage("Live Histogram", "RGB White", xhisto, yhisto, 1);
		setLocation(0, (screenHeight-yhisto-40));
		name=getTitle();
	}
	if (RGBhistoSet==1 &&  bitDepth() != 24) {RGBhistoSet=0; showMessage ("Video is 8-bit encoded, RGB histogram will not be printed.");}

	while (isOpen("Live (press space bar to stop)") && stopHisto != 1) {
		start = getTime;
		setBatchMode(true);
		selectWindow ("Live (press space bar to stop)");
		getStatistics(area, mean, min, max, std, histogram);
		greyHisto=histogram;
		if (RGBhistoSet == 1) {
			run("Duplicate...", "title=H");
			run("RGB Stack");pile=getTitle();//wait (10);
 			if (isOpen(pile)) {setSlice(1); getStatistics(area, mean, min, max, std, histogram);redHisto=histogram;}
			if (isOpen(pile)) {setSlice(2); getStatistics(area, mean, min, max, std, histogram);greenHisto=histogram;}
			if (isOpen(pile)) {setSlice(3); getStatistics(area, mean, min, max, std, histogram);blueHisto=histogram;}
			close ();
		}
		scale= (area/yhisto)/yHistoAtenuation;
		if (isOpen("Live Histogram")) {
			selectImage ("Live Histogram");
			for (i=0; i<=255; i++) {
				setColor(0,0,0);
				drawLine(i, yhisto , i,  0);
				setColor(128,128,128);
				drawLine(i, (yhisto-1), i,  (yhisto-1- (greyHisto[i])/scale));
				// rgb as line
				if (RGBhistoSet == 1) {
					if (i<255) {setColor(255,0,0); drawLine(i, (yhisto-1- (redHisto[i+1])/scale), i,  (yhisto-1- (redHisto[i])/scale));}
					if (i<255) {setColor(0,255,0); drawLine(i, (yhisto-1- (greenHisto[i+1])/scale), i,  (yhisto-1- (greenHisto[i])/scale));}
					if (i<255) {setColor(0,0,255); drawLine(i, (yhisto-1- (blueHisto[i+1])/scale), i,  (yhisto-1- (blueHisto[i])/scale));}
				}
			}
		} else { histo=0; exit ("\"Live Histogram\" window can\'t be found");}
		while (getTime-start<HistoInterval*1000) wait(1);
		maxFreqLevel=FindMinAndMaxFreq (greyHisto);
		histoStatus= " "+d2s(HistoInterval,2) + "s Ate:"+yHistoAtenuation+" Min="+min+ " max="+max + " highest="+histogram [lengthOf(greyHisto)-1] + " \(level "+maxFreqLevel+"\)"                   ;
		if (RGBhistoSet ==0) showStatus(histoStatus);
  		setBatchMode(false);
	}
	if (stopHisto==0) histo=0;
	if (stopHisto==1 && restartHisto ==1) histo=1;
	if (histo==0) {
		selectImage ("Live Histogram");
		rename("Histogram");
	}
}

// Histogram analysis function. Derived the SortDemo macro from Wayne Rasband available at the
// "/ij/macros/SortDemo.txt" 

function FindMinAndMaxFreq (a) {
	b=newArray (lengthOf(a));
	for (i=0; i<lengthOf(a); i++) {b[i]=i;}
	quickSort(a,b, 0, (lengthOf(a)-1));
	return b[lengthOf(b)-1];
}

function quickSort(a,b, from, to) {
      i = from; j = to;
      center = a[(from+to)/2];
      do {
		while (i<to && center>a[i]) i++;
		while (j>from && center<a[j]) j--;
		if (i<j) {
			temp=a[i]; a[i]=a[j]; a[j]=temp;
			temp=b[i];b[i]=b[j]; b[j]=temp;
		}
		if (i<=j) {i++; j--;}
      } while(i<=j);
	if (from<j) quickSort(a,b,from, j);
	if (i<to) quickSort(a,b, i, to);
}