I don't know how to write plugins for imagej, but I did write two macros to import SEQ files (converged on the same solution to Bud).
The first macro imports the SEQ into ImageJ (using defaults that work for me) and the second macro converts the raw into temperature, according to equations developed by tomas123 and Phil Harvey. The first macro calls Exiftool and extracts the calibration constants.
But if someone can put this together as an ImageJ plugin (ie. faster performance than macro), I would be grateful. Now I just need to get this to work for FLIR JPG and FLIR CSQ and I'm set.
BTW, this code is still a work in progress, I only share it in response to jeffB who might dust off his plug-in skills!
First macro:
// offsetbyte is 1372 for SEQ files recorded to computer (works for at least two diff cameras)
// offsetbyte is 1540528 for SEQ files recorded to a FLIR SC660
// offsetbyte is 1542956 or 1540480 for SEQ files recorded to a FLIR SC640
// gapbytes is 3020 for direct SEQ recorded files
// gapbytes is 1424 for thermacam researcher pro captured seq files
var offsetbyte = 1372;
var gapbytes = 1424;
var nframes = 10000;
var imagewidth = 640;
var imageheight = 480;
var converttotemperature = 1;
var usevirtual = 1;
var minpix=1;
var maxpix=65535;
macro "Import FLIR SEQ" {
//Create Dialog Box
Dialog.create("Information for Thermal Video Import");
Dialog.addNumber("Offset Bytes", offsetbyte, 0, 8, "bytes");
Dialog.addNumber("Gaps Between Frames:", gapbytes, 0, 8, "bytes");
Dialog.addNumber("Number of Frames: ", nframes, 0, 8, "frames");
Dialog.addNumber("Image Width:", imagewidth, 0, 6, "pixels");
Dialog.addNumber("Image Height:", imageheight, 0, 6, "pixels");
Dialog.addCheckbox("Convert to Temperature on Import", converttotemperature);
Dialog.addCheckbox("Use Virtual Stack", usevirtual);
Dialog.show();
//Define Variable for Import
offsetbyte = Dialog.getNumber();
gapbytes = Dialog.getNumber();
nframes = Dialog.getNumber();
imagewidth = Dialog.getNumber();
imageheight = Dialog.getNumber();
converttotemperature = Dialog.getCheckbox();
usevirtual = Dialog.getCheckbox();
filepath=File.openDialog("Select a File");
file=File.openAsString(filepath);
print("Loading: ", filepath);
print("\n");
run("Raw...", "open=filepath image=[16-bit Unsigned] width=imagewidth height=imageheight offset=offsetbyte number=nframes gap=gapbytes little-endian use=usevirtual");
OS=getInfo("os.name");
if(OS=="Mac OS X"){
flirvals=exec("/usr/local/bin/exiftool", "-Planck*", "-*Emissivity", "-*Distance", "-*Temperature", "-*Transmission", "-*Humidity", "-*Height", "-*Width", "-*Original", "-*Date", filepath);
}
if(substring(OS, 0, 7)=="Windows"){
flirvals=exec("c:/Windows/exiftool.exe", "-Planck*", "-*Emissivity", "-*Distance", "-*Temperature", "-*Transmission", "-*Humidity", "-*Height", "-*Width", "-*Original", "-*Date", filepath);
print(flirvals);
}
var PR1 = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "Planck R1"))+1, indexOf(flirvals, "\n", indexOf(flirvals, "Planck R1")) ));
var PB = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "Planck B"))+1, indexOf(flirvals, "\n", indexOf(flirvals, "Planck B")) ));
var PF = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "Planck F"))+1, indexOf(flirvals, "\n", indexOf(flirvals, "Planck F")) ));
var PO = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "Planck O"))+1, indexOf(flirvals, "\n", indexOf(flirvals, "Planck O")) ));
var PR2 = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "Planck R2"))+1, indexOf(flirvals, "\n", indexOf(flirvals, "Planck R2")) ));
var E = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "Emissivity"))+1, indexOf(flirvals, "\n", indexOf(flirvals, "Emissivity")) ));
var OD = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "Object Distance"))+1, indexOf(flirvals, "\n", indexOf(flirvals, "Object Distance")) ));
var RTemp = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "Reflected Apparent Temperature"))+1, indexOf(flirvals, "C\n", indexOf(flirvals, "Reflected Apparent Temperature")) ));
var ATemp = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "Atmospheric Temperature"))+1, indexOf(flirvals, "C\n", indexOf(flirvals, "Atmospheric Temperature")) ));
var IRWTemp = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "IR Window Temperature"))+1, indexOf(flirvals, "C\n", indexOf(flirvals, "IR Window Temperature")) ));
var IRT = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "IR Window Transmission"))+1, indexOf(flirvals, "\n", indexOf(flirvals, "IR Window Transmission")) ));
var RH = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "Relative Humidity"))+1, indexOf(flirvals, "%\n", indexOf(flirvals, "Relative Humidity")) ));
var imagewidth = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "Width"))+1, indexOf(flirvals, "\n", indexOf(flirvals, "Width")) ));
var imageheight = parseFloat(substring(flirvals, indexOf(flirvals, ":", indexOf(flirvals, "Height"))+1, indexOf(flirvals, "\n", indexOf(flirvals, "Height")) ));
print("Camera Calibration Constants:");
setFont("SansSerif", 12);
print("Planck R1: ", d2s(PR1,9));
print("Planck B: ", d2s(PB,9));
print("Planck F: ", d2s(PF,0));
print("Planck O: ", d2s(PO,0));
print("Planck R2: ", d2s(PR2,9));
print("Thermal Image Width: ", imagewidth);
print("Thermal Image Height: ", imageheight);
print("\n");
print("Default Object Parameters:");
print("Emissivity: ", d2s(E,2));
print("Object Distance: ", d2s(OD,2));
print("Reflected Apparent Temperature: ", d2s(RTemp,2));
print("Atmospheric Temperature: ", d2s(ATemp,2));
print("IR Window Temperature: ", d2s(IRWTemp,2));
print("IR Window Transmission: ", d2s(IRT,3));
print("Relative Humidity: ", d2s(RH,2));
print("\n");
Stack.getStatistics(count, mean, min, max, std);
var minpix=min;
var maxpix=max;
setMinAndMax(minpix, maxpix);
if(converttotemperature) {
run("Raw2Temp");
}
run("Calibration Bar...", "location=[Upper Right] fill=White label=Black number=5 decimal=1 font=12 zoom=1 bold overlay");
}
Second macro:
var PR1=21106.77;
var PR2=0.012545258;
var PB=1501;
var PF=1;
var PO=-7340;
var E = 0.95;
var OD = 1.0;
var RTemp = 20.0;
var ATemp = 20.0;
var IRWTemp = 20.0;
var IRT = 1.0;
var RH = 50.0;
macro "Raw2Temp" {
Dialog.create("Object Parameters to Calculate Temperature");
Dialog.addMessage("Camera Calibration Constants:");
Dialog.addNumber("Planck R1:", PR1, 2, 12, "unitless"); //21106.77 //21546.203
Dialog.addNumber("Planck R2:", PR2, 8, 12, "unitless"); //0.012545258 //0.016229488
Dialog.addNumber("Planck B:", PB, 0, 5, "unitless"); //1501 //1507.2
Dialog.addNumber("Planck F:", PF, 0, 2, "unitless");//1
Dialog.addNumber("Planck O:", PO, 0, 5, "unitless"); //-7340 //-6331
Dialog.addMessage("Object Parameters:");
Dialog.addNumber("Object Emissivity:", E, 3, 6, "unitless");
Dialog.addNumber("Object Distance:", OD, 1, 6, "m");
Dialog.addNumber("Reflected Temperature (C):", RTemp, 2, 6, "C");
Dialog.addNumber("Atmospheric Temperature (C):", ATemp, 2, 6, "C");
Dialog.addNumber("Window Temperature (C):", IRWTemp, 2, 6, "C");
Dialog.addNumber("Window Transmittance:", IRT, 3, 6, "unitless");
Dialog.addNumber("Relative Humidity:", RH, 2, 6, "%");
Dialog.show();
var PR1 = Dialog.getNumber();
var PR2 = Dialog.getNumber();
var PB = Dialog.getNumber();
var PF = Dialog.getNumber();
var PO = Dialog.getNumber();
var E = Dialog.getNumber();
var OD = Dialog.getNumber();
var RTemp = Dialog.getNumber();
var ATemp = Dialog.getNumber();
var IRWTemp = Dialog.getNumber();
var IRT = Dialog.getNumber();
var RH = Dialog.getNumber();
ATA1 = 0.006569; //Atmospheric Trans Alpha 1
ATA2 = 0.012620; //Atmospheric Trans Alpha 2
ATB1 = -0.002276; //Atmospheric Trans Beta 1
ATB2 = -0.006670; //Atmospheric Trans Beta 2
ATX = 1.900000; //Atmospheric Trans X
emisswind = 1- IRT;
reflwind = 0 // anti-reflective coating on window
h2o = (RH/100)*exp(1.5587+0.06939*(ATemp)-0.00027816*(ATemp)^2+0.00000068455*(ATemp)^3); // converts relative humidity into water vapour pressure (I think in units mmHg)
tau1 = ATX*exp(-sqrt(OD/2)*(ATA1+ATB1*sqrt(h2o)))+(1-ATX)*exp(-sqrt(OD/2)*(ATA2+ATB2*sqrt(h2o))); // atmos transmittance from object to window
tau2 = ATX*exp(-sqrt(OD/2)*(ATA1+ATB1*sqrt(h2o)))+(1-ATX)*exp(-sqrt(OD/2)*(ATA2+ATB2*sqrt(h2o))); // atmos transmittance from window to camera
rawrefl1= PR1/(PR2*(exp(PB/(RTemp+273.15))-PF))-PO // radiance reflecting off the object before the window
rawrefl1attn = (1-E)/E*rawrefl1 // attn = the attenuated radiance (in raw units)
rawatm1 = PR1/(PR2*(exp(PB/(ATemp+273.15))-PF))-PO // radiance from the atmosphere (before the window)
rawatm1attn = (1-tau1)/E/tau1*rawatm1 // attn = the attenuated radiance (in raw units)
rawwind = PR1/(PR2*(exp(PB/(IRWTemp+273.15))-PF))-PO
rawwindattn = emisswind/E/tau1/IRT*rawwind
rawrefl2 = PR1/(PR2*(exp(PB/(RTemp+273.15))-PF))-PO
rawrefl2attn = reflwind/E/tau1/IRT*rawrefl2
rawatm2 = PR1/(PR2*(exp(PB/(ATemp+273.15))-PF))-PO
rawatm2attn = (1-tau2)/E/tau1/IRT/tau2*rawatm2
rawsubtract = (rawatm1attn + rawatm2attn + rawwindattn + rawrefl1attn + rawrefl2attn)
rawdivisor = E*tau1*IRT*tau2
run("32-bit", "stack");
run("Macro...", "code=v=" +PB+ "/log(" +PR1+ "/(" +PR2+ "*(v/" +rawdivisor+ "-" +rawsubtract+ "+" +PO+ "))+" +PF+ ")-273.15 stack");
mintemp=PB/log(PR1/(PR2*(minpix/rawdivisor-rawsubtract+PO))+PF)-273.15;
maxtemp=PB/log(PR1/(PR2*(maxpix/rawdivisor-rawsubtract+PO))+PF)-273.15;
setMinAndMax(mintemp, maxtemp);
}