From d7e508644b0f579dd9a0c1f09baa7143b0fcbca6 Mon Sep 17 00:00:00 2001 From: Glenn Tattersall Date: Sat, 6 Jul 2019 22:37:14 +0100 Subject: [PATCH] Update toolset to v1.4. includes fast raw2temp calc --- scripts/split.pl | 44 +- toolsets/ThermImageJ.ijm | 1206 ++++++++++++++++++++++++++------------ 2 files changed, 860 insertions(+), 390 deletions(-) diff --git a/scripts/split.pl b/scripts/split.pl index 1136a49..e50d712 100644 --- a/scripts/split.pl +++ b/scripts/split.pl @@ -8,7 +8,7 @@ #use warnings; use Getopt::Std; -our($opt_i, $opt_o, $opt_b, $opt_p, $opt_x, $opt_v); +our($opt_i, $opt_o, $opt_b, $opt_p, $opt_x, $opt_s, $opt_v); my $n=0; my $folder = "temp"; @@ -25,18 +25,20 @@ my $patjpg = "\xff\xd8\xff\xe1"; # split out jpg headers my $outext = "fff"; my $file = ""; +my $skip = "n"; my (%opt)=(); -getopts("h:i:o:b:p:x:v:",\%opt); +getopts("h:i:o:b:p:x:s:v:",\%opt); if ($opt{h}){ print qq( - Usage: perl split -i filename -o outputfoldername -b basename -p splitpattern -x outputfileextension -v verbose + Usage: perl split -i filename -o outputfoldername -b basename -p splitpattern -x outputfileextension -s skip -v verbose options: -i input filename -o output folder -b base output name -p split pattern (fff, fcf, seq, csq, jpegls, tiff) -x output extension (fff, jpegls, tiff) + -s skip first part of split content -v verbose (y=yes, leave blank for no feedback on success) \n.); exit } @@ -54,6 +56,7 @@ $infilename = $opt{i}; $outfilebase = $opt{b}; $outext = ".$opt{x}"; +$skip = $opt{s}; if ($opt{p} eq "fff"){ $pat = $patfff; @@ -90,15 +93,42 @@ $file = do { local $/; }; close F; - -# Split infilename based on $pat -for my $content (split(/(?=$pat)/, $file)) { +# If skip=y, then we will operate the split function, but discard the first portion of the split, +# and only save the rawdata following and including the pattern +# Only real application is for splitting fff files into fffheader + jpeglsrawdata +# This code should allow for a more generic skipping of every other split result + +if ($skip eq "y"){ + my @content = split /(?=$pat)/, $file; + my $len = ($#content + 1)/2; + my @ind = (1); # skip the first index + + # create an index of numbers that should be odd (1,3,5,7...up to the # of images) + for (my $i = 1; $i<$len; $i++) { + @ind[$i] = @$ind[($i-1)]+ 2; + } + # then save + for (my $i = 0; $i<$len; $i++) { + $outfilename = ">$folder/$outfilebase" . sprintf("%05d",++$n) . $outext; + open(OUT, $outfilename); + binmode(OUT, ":raw"); + print OUT $content[$ind[$i]]; + close(OUT); + } + } + + # this is the default split functioning; + else { + + # Split infilename based on $pat + for my $content (split(/(?=$pat)/, $file)) { $outfilename = ">$folder/$outfilebase" . sprintf("%05d",++$n) . $outext; open(OUT, $outfilename); binmode(OUT, ":raw"); print OUT $content; close(OUT); -} + } + } # If verbose=y, then print the following update: if ($opt{v} eq "y"){ diff --git a/toolsets/ThermImageJ.ijm b/toolsets/ThermImageJ.ijm index 4bdce9d..f88ba85 100644 --- a/toolsets/ThermImageJ.ijm +++ b/toolsets/ThermImageJ.ijm @@ -4,22 +4,26 @@ //// Main Features: import, conversion, and transformation of thermal images. //// //// Requires: exiftool, ffmpeg, perl //// //// Glenn J. Tattersall //// -//// April, 2019 - Version 1.0 //// +//// April, 2019 - Version 1.4 //// //// //// /////////////////////////////////////////////////////////////////////////////////////////////////////// + var luts = getLutMenu(); var lCmds = newMenu("LUT Menu Tool", luts); var palettetypes=newArray("Grays", "Ironbow", "Rainbow", "Spectrum", "Thermal", "Yellow", "Yellow Hot", "Green Fire Blue", "Red/Green", "5 Ramps", "6 Shades"); var defaultpalette="Grays"; var thermlCmds = newMenu("Thermal LUT Menu Tool", palettetypes); +var ImportCmds = newMenu("Import Menu Tool", + newArray("Raw Import RTV", "Raw Import FLIR SEQ", "Import FLIR JPG", "Import FLIR SEQ", "Import FLIR CSQ", "Import 16-bit AVI")); var lut = -1; var lutdir = getDirectory("luts"); var list; var color = 0; var colors = newArray("Red", "Green", "Blue", "Cyan", "Magenta", "Yellow"); -// the following persistent variable are updated on the user's ImageJ once Raw2Temp is performed on a file +// the following persistent variable are updated on the user's ImageJ once Raw2Temp or FlirValues is performed on a file +// This will help for continuity when analysing files in between imagej sessions var PR1 = parseFloat(call("ij.Prefs.get", "PR1.persistent","17998.529")); var PR2 = parseFloat(call("ij.Prefs.get", "PR2.persistent","015145967")); var PB = parseFloat(call("ij.Prefs.get", "PB.persistent","1453.1")); @@ -36,6 +40,8 @@ var imagewidth=parseInt(call("ij.Prefs.get", "imagewidth.persistent","640")); var imageheight=parseInt(call("ij.Prefs.get", "imageheight.persistent","480")); var magicbyte=call("ij.Prefs.get", "magicbyte.persistent","02008002e001"); var frameinterval=parseFloat(call("ij.Prefs.get", "frameinterval.persistent", "0.03333333333")); +var imagetemperaturemin=parseInt(call("ij.Prefs.get", "imagetemperaturemin.persistent","-20")); +var imagetemperaturemax=parseInt(call("ij.Prefs.get", "imagetemperaturemax.persistent","60")); /////////////////////////////////////////////////////////////////////////////////////////////////////// //// //// @@ -83,11 +89,11 @@ function cycleLUTs(inc) { createLutList(); if (nImages==0) { call("ij.gui.ImageWindow.centerNextImage"); - newImage("LUT", "8-bit ramp", 480, 64, 1); + newImage("LUT", "8-bit ramp", 750, 32, 1); run("Rotate 90 Degrees Left"); setColor(0); setLineWidth(2); - drawRect(0, 0, 64, 480); + drawRect(0, 0, 32, 750); } if (bitDepth==24) exit("RGB images do not have LUTs"); @@ -276,11 +282,101 @@ function cumsum(ArrayX){ return csum; } +// faster calculation of median - approximate +function MedianFast(){ + getStatistics(area, mean, min, max, std, histogram); + pixelmin=min; + pixelhist=histogram; + length=pixelhist.length; + binwidth=(max-min)/256; + vals=Array.getSequence(length); + + cumulsumhist=cumsum(pixelhist); + Array.getStatistics(cumulsumhist, min, max, mean, stdDev); + midpoint=floor(max/2); + + for(i=0; i=midpoint){ + med=vals[i]; + i=1000; + } + } + return med; +} + + +// calculates median by sorting the data and taking the middle point +function Median(ArrayX){ + len=ArrayX.length; + x=Array.sort(ArrayX); + middleindex=floor(len/2); + med=ArrayX[middleindex]; + return med; +} + +// root mean square of an Array: +function RMS(ArrayX){ + len=ArrayX.length; + d2=newArray(len); + sum=0; + for(i=0; i jpegls and remove extra files approach ////////////////////// + +///// After creating the fff files, an alternative to using exiftool combining to thermalvid.raw is to re-split the fff files and then filter +///// out the extra generated files as shown in the next ~20 lines +//// The reason for this is with large CSQ files (>5000 frames) - exiftool can't handle the stream for some reason, so the perl split.pl script is the +//// only viable solution. +//// The split.pl script simply splits a file whereever the magic byte sequence occurs, which for an FFF file means that a short file is created that is +//// mostly header info about the image, and the second file split off is the jpegls image data + + if(vidtype=="csq"){ + + fff_files = getFileList(tempfolder); + + print("Splitting fff files into jpegls files by looping through all files similar to:"); + print(perl + perlsplit + "-i " + tempfolder + File.separator + fff_files[0] + " -o " + filedir + File.separator + "temp " + "-b " + fff_files[0] + ". " + "-p " + "jpegls " + "-x " + "jpegls " + "-s " + "y"); + + + for(i=0; i jpegls or tiff and remove extra files approach ^ ////////////////////// - print("Video frame time difference is: " + meanframediff + " seconds"); - + + + +////////////////// Thermalvid.raw approach ////////////////////// + +// SEQ files may be oddly formatted, so we'll use Exiftool to generate the thermalvid.raw file. +// Note: may not work on files larger than 6000 frames + + if(vidtype=="seq"){ + // Combine fff files into thermalvid.raw using exiftool raw binary extraction function // Difficulty getting the piping (> or |) to work with the default exec command. // See: http://imagej.1557.x6.nabble.com/macro-Redirection-in-exec-UNIX-binary-td3687463.html - - rawcombinecmd = exiftoolpath + exiftool + " -b -RawThermalImage " + tempfolder + File.separator + "*.fff > " + filedir + File.separator + "thermalvid.raw"; - print("Combine the fff files into a thermalvid.raw file with: "); + //rawcombinecmd = exiftoolpath + exiftool + " -b -RawThermalImage " + tempfolder + File.separator + "*.fff > " + filedir + File.separator + "thermalvid.raw"; + + rawcombinecmd = exiftoolpath + exiftool + " -b -r -fast -P -progress -sort -RawThermalImage " + tempfolder + File.separator + "*.fff > " + filedir + File.separator + "thermalvid.raw"; + print("Combining the fff files into a thermalvid.raw file with: "); print(rawcombinecmd); if(OS=="Mac OS X"){ @@ -997,12 +1262,30 @@ function ConvertFLIRVideo(vidtype, outtype, outcodec, converttotemperature, usev exec(perl, perlsplit, "-i", filedir + File.separator + "thermalvid.raw", "-o", filedir + File.separator + "temp", "-b", "frame", "-p", "jpegls", "-x", "jpegls"); } - // Execute the ffmpeg command to assimilate all the tiff files into one avi file +} + +////////////////// ^ Thermalvid.raw approach ^ ////////////////////// + + + +// Execute the ffmpeg command to assimilate all the image (TIFF or JPEGLS or PNG?) files into one avi file + + if(copycodec=="copy"){ + outcodec="copy"; + } + print("Combining the " + RawThermalType + " files into " + outcodec + " files ready for import with: "); - tiffcombinecmd = ffmpeg + " -f" + " image2" + " -vcodec" + " " + RawThermalType + " -r" + " 30" + " -i " + tempfolder + File.separator + "frame%05d." + RawThermalType + " -pix_fmt" + " gray16be" + " -vcodec " + outcodec + " " + filedir + File.separator + fileout + " -y"; - print(tiffcombinecmd); - - exec(ffmpeg, "-f", "image2", "-vcodec", RawThermalType, "-r", "30", "-i", tempfolder + File.separator + "frame%05d." + RawThermalType, "-pix_fmt", pixfmt, "-vcodec", outcodec, filedir + File.separator + fileout, "-y"); + tiffcombinecmd = ffmpeg + " -f" + " image2" + " -vcodec" + " " + RawThermalType + " -r" + " 30" + " -i " + tempfolder + File.separator + "frame%05d." + RawThermalType + " -pix_fmt " + pixfmt + " -vcodec " + outcodec + " " + filedir + File.separator + fileout + " -y"; + print(tiffcombinecmd); + + if(outcodec=="jpegls"){ + // for recoding to jpegls, setting pred to 2 usually yields the smallest file size + // but no saving is generated from a CSQ file where the jpegls compression is already optimal, so it is better simply to set -vodec to copy + exec(ffmpeg, "-f", "image2", "-vcodec", RawThermalType, "-r", "30", "-i", tempfolder + File.separator + "frame%05d." + RawThermalType, "-pix_fmt", pixfmt, "-vcodec", outcodec, "-pred", "2", filedir + File.separator + fileout, "-y"); + } + else{ + exec(ffmpeg, "-f", "image2", "-vcodec", RawThermalType, "-r", "30", "-i", tempfolder + File.separator + "frame%05d." + RawThermalType, "-pix_fmt", pixfmt, "-vcodec", outcodec, filedir + File.separator + fileout, "-y"); + } templist = getFileList(tempfolder); for (i = 0; i < templist.length; i++){ @@ -1012,9 +1295,9 @@ function ConvertFLIRVideo(vidtype, outtype, outcodec, converttotemperature, usev thermalviddelete_success=File.delete(filedir + File.separator + "thermalvid.raw"); tempfolderdelete_success=File.delete(filedir + File.separator + "temp" + File.separator ); - if(tempfilesdelete_success + tempfolderdelete_success + thermalviddelete_success==3){ - print("Temporary files and folder deleted"); - } + //if(tempfilesdelete_success + tempfolderdelete_success ==2){ + // print("Temporary files and folder deleted"); + //} if(outtype=="png"){ @@ -1030,7 +1313,7 @@ function ConvertFLIRVideo(vidtype, outtype, outcodec, converttotemperature, usev } if(outtype=="avi"){ - print("Importing AVI file"); + print("Importing 16-bit grayscale AVI file"); ffmpegimportarguments = "choose=" + filedir + File.separator + fileout + " first_frame=0 last_frame=-1"; if(usevirtual==1){ @@ -1040,20 +1323,21 @@ function ConvertFLIRVideo(vidtype, outtype, outcodec, converttotemperature, usev run("Movie (FFMPEG)...", ffmpegimportarguments); } - print("Adding file time origin as slice label"); - for (i=1; i<=nSlices; i++) { - setSlice(i); - slicelabel= dateoriginal + "_" + timeoriginal[i] + tz; - run("Set Label...", "label=" + slicelabel); - } +// print("Adding file time origin as slice label"); +// for (i=1; i<=nSlices; i++) { +// setSlice(i); +// slicelabel= dateoriginal + "_" + timeoriginal[i] + tz; +// run("Set Label...", "label=" + slicelabel); +// } // Set frame interval to stack - Stack.setFrameInterval(meanframediff); // sets the frame rate +// Stack.setFrameInterval(meanframediff); // sets the frame rate //run("Raw2Temp Tool"); + if(converttotemperature==1){ print("Converting file to temperature"); - Raw2Temp(PR1, PR2, PB, PF, PO, E, OD, RTemp, ATemp, IRWTemp, IRT, RH, defaultpalette, "Yes"); + Raw2Temp(PR1, PR2, PB, PF, PO, E, OD, RTemp, ATemp, IRWTemp, IRT, RH, defaultpalette, "Yes", "Fast", imagetemperaturemin, imagetemperaturemax); } print("Done"); @@ -1061,55 +1345,91 @@ function ConvertFLIRVideo(vidtype, outtype, outcodec, converttotemperature, usev } +function ImportFFmpegAVI(){ + AVIfile=File.openDialog("FFmpeg AVI File"); + ffmpegchoose="choose=" + "'" + AVIfile + "'" + " use_virtual_stack first_frame=0 last_frame=-1"; + run("Movie (FFMPEG)...", ffmpegchoose); + run("Animation Options...", "speed=30"); + doCommand("Start Animation [\\]"); +} -function Raw2Temp(PR1, PR2, PB, PF, PO, E, OD, RTemp, ATemp, IRWTemp, IRT, RH, palettetype, dialogprompt) { - print("Running Raw2Temp function"); +function Raw2Temp(PR1, PR2, PB, PF, PO, E, OD, RTemp, ATemp, IRWTemp, IRT, RH, palettetype, dialogprompt, FastSlow, imagetemperaturemin, imagetemperaturemax) { + + + //start=getTime(); - if(is("Virtual Stack")==true){ + print("\n------ Running Raw2Temp function ------"); + + // Apparently, no need to convert virtual stack if you use the Calibrate function and fast calculation + if(is("Virtual Stack")==1 && FastSlow=="Slow"){ + print("Cannot perform conversions on a virtual stack. Duplicating stack first.\nThis is slow and it is recommended that you crop and edit first before using the slow calculation"); run("Duplicate...", "duplicate"); } if(dialogprompt=="Yes"){ - byteorder=newArray("Default", "Swap"); - defaultbyteorder="Default"; - // Create a prompt dialog to ask user to verify the values to be used in the calculations below - Dialog.create("Verify Camera and Object Parameters"); - Dialog.addMessage("Camera parameters can be stored to memory using the FLIR Calibration Values Tool"); - Dialog.addMessage("Note: TIFF file pixel byte are usually little endian\nPNG file pixel bytes are usually big endian"); - Dialog.addChoice("Swap Byte Order?", byteorder, defaultbyteorder); - 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.addChoice("Palette", palettetypes, defaultpalette); - Dialog.show(); - - var ByteOrder=Dialog.getChoice(); - 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(); - var palettetype = Dialog.getChoice(); + byteorder=newArray("Default", "Swap"); + defaultbyteorder="Default"; + fastslowchoice=newArray("Fast", "Slow"); + fastslowchoicedefault=FastSlow; + + // Create a prompt dialog to ask user to verify the values to be used in the calculations below + + Dialog.create("Verify Camera and Object Parameters"); + Dialog.addMessage("If Calibration constants are unknown, run the FLIR Calibration Values Tool first!"); + Dialog.addMessage("TIFF file pixel byte are usually little endian\nPNG file pixel bytes are usually big endian"); + Dialog.addChoice("Swap Byte Order?", byteorder, defaultbyteorder); + Dialog.addChoice("Fast (approximate) or\nSlow (accurate) Calculation?", fastslowchoice, fastslowchoicedefault); + Dialog.addNumber("Estimated Image Temperature Minimum:", imagetemperaturemin, 0, 5, "C"); + Dialog.addNumber("Estimated Image Temperature Maximum:", imagetemperaturemax, 0, 5, "C"); + 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.addChoice("Palette", palettetypes, defaultpalette); + Dialog.show(); + + var ByteOrder=Dialog.getChoice(); + var FastSlow=Dialog.getChoice(); + var imagetemperaturemin = Dialog.getNumber(); + var imagetemperaturemax = Dialog.getNumber(); + 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(); + var palettetype = Dialog.getChoice(); + + call("ij.Prefs.set", "imagetemperaturemin.persistent",toString(imagetemperaturemin)); + call("ij.Prefs.set", "imagetemperaturemax.persistent",toString(imagetemperaturemax)); + + if(ByteOrder == "Swap"){ + run("Byte Swapper"); + } + + } + + if(is("Virtual Stack")==1 && FastSlow=="Slow"){ + print("Cannot perform conversions on a virtual stack. Duplicating stack first.\nThis is slow and it is recommended that you crop and edit first before using the slow calculation"); + run("Duplicate...", "duplicate"); } //setBatchMode(true); @@ -1143,55 +1463,149 @@ function Raw2Temp(PR1, PR2, PB, PF, PO, E, OD, RTemp, ATemp, IRWTemp, IRT, RH, p rawsubtract = (rawatm1attn + rawatm2attn + rawwindattn + rawrefl1attn + rawrefl2attn); rawdivisor = E*tau1*IRT*tau2; + + // Fast method is good if you restrict the imagetemperaturemin and imagetemperaturemax ranges to limits that realistically span + // the scene you are analysing. - //a = newArray(65536); - //templookup=newArray(65536); - //for (i=0; i<65536; i++) { - // a[i]=i; - // templookup[i] = 1500/log(21000/(0.012*(a[i]/0.9-100-7300))+1)-273.15; - // templookup[i] = PB/log(PR1/(PR2*(a[i]/rawdivisor-rawsubtract+PO))+PF)-273.15; - // } - //v=newArray(imagewidth*imageheight); - //t=newArray(imagewidth*imageheight); - //l=0; - //for(w=0; wimagetemperaturemax){ + templookup[i]=NaN; + } + + } + + //Fit.plot; + //initialGuesses = newArray(1507.2, 21546.203, 0.016229488, 0.9, (-6331-100), 1); + //print(Fit.nParams); + + s=""; // s = string to print to file for calibration + + text1="text1=["; + text2="text2=["; + x=newArray(655); + y=newArray(655); + + for(i=nai; i<655; i++){ + showProgress(i/655); + + if(isNaN(templookup[i])){ + i=100000000; // break out of the loop + } + + else{ + + x[i]=r[i]; + y[i]=templookup[i]; + + text1=text1 + d2s(r[i], 0) + " "; + text2=text2 + d2s(templookup[i], 12) + " "; + s = s + d2s(r[i], 0) + " \t" + d2s(templookup[i], 12) + "\n"; + } + } + + Fit.doFit("4th Degree Polynomial", x, y); + predicted=newArray(655); + resid=newArray(655); + for(i=0; i<655; i++){ + predicted[i]=Fit.f(x[i]); + resid[i]=predicted[i]-y[i]; + } + + rms_resid=RMS(resid); + + print("Temperature was estimated using a 4th order polynomial on a restricted range of the data."); + print("This approximates the Sakuma-Hattori equation (used for estimating Planck's law for instruments with non-finite bandwidth) across a limited temperature range"); + print("The root mean square of the error from polynomial predicted temperature using the fast calculation is:", rms_resid, "degrees C"); + print("Extreme temperatures are likely to have higher errors"); + print("If this error is too high, re-run the Raw2Temp with fast calculation setting more stringent image minimum and maximum, or select the Slow calculation"); + + //File.saveString(s, "/Users/GlennTattersall/Desktop/calibration.txt"); + + text1=text1 + "] "; + text2=text2 + "] "; + + calibrateargument = "function=[4th Degree Polynomial] unit=°C " + text1 + text2 + "global"; + + run("Calibrate...", calibrateargument); + + getStatistics(count, mean, min, max); + + toohigh=0; toolow=0; + if(max + 5 > imagetemperaturemax){ + toohigh=1; + } + + if(min - 5 < imagetemperaturemin){ + toolow=1; + } + outofrange=toohigh+toolow; + + if(outofrange>0){ + print(" ------- WARNING -------"); + print("Temperatures calculated fall outside the expected image min and max values and are likely subject to extrapolation errors."); + print("Please recalculate using the fast option with wider min or max ranges."); + } + } - if(nSlices()>1){ + if(FastSlow=="Slow"){ + // remove any latent calibration on the raw 16-bit data + run("Calibrate...", "function=[Straight Line] unit=C text1=[0 1] text2=[0 1] global"); + + if(nSlices()>1){ run("32-bit", "stack"); run("Macro...", "code=v=" +PB+ "/log(" +PR1+ "/(" +PR2+ "*(v/" +rawdivisor+ "-" +rawsubtract+ "+" +PO+ "))+" +PF+ ")-273.15 stack"); - Stack.getStatistics(count, mean, min, max, std); + Stack.getStatistics(count, mean, min, max); mintemp=min; maxtemp=max; - } + setMinAndMax(mintemp, maxtemp); + } if(nSlices()==1){ run("32-bit"); run("Macro...", "code=v=" +PB+ "/log(" +PR1+ "/(" +PR2+ "*(v/" +rawdivisor+ "-" +rawsubtract+ "+" +PO+ "))+" +PF+ ")-273.15"); - getStatistics(count, mean, min, max, std); + getStatistics(count, mean, min, max); mintemp=min; - maxtemp=max; + maxtemp=max; + setMinAndMax(mintemp, maxtemp); + } + } - //setBatchMode(false); //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); + run(palettetype); + //end=getTime(); + //timediff=end-start; + //print(timediff); + } function flirvalues(filepath, printvalues){ - - print("Running flirvalues function"); + + print("\n------ Running flirvalues function ------"); if(OS=="Mac OS X"){ var perlpath=perlpathOSX; @@ -1301,7 +1715,7 @@ function flirvalues(filepath, printvalues){ function flirdate(filepath, printvalues){ - print("Running flirdate function"); + print("\n------ Running flirdate function ------"); if(OS=="Mac OS X"){ var perlpath=perlpathOSX; @@ -1356,7 +1770,7 @@ function flirdate(filepath, printvalues){ function addMeasurementLabel(type, units, decimals, colour, addROI, drawx, drawy) { - print("Running addMeasurementLabel function"); + print("\n------ Running addMeasurementLabel function ------"); // type should be one of: Mean StdDev Min Max Mode Median Skew or Kurt // but will be converted to: mean min standard modal median skewness kurtosis @@ -1415,58 +1829,115 @@ function addMeasurementLabel(type, units, decimals, colour, addROI, drawx, drawy } +function StackDifference(){ + + activewindow=getInfo("window.title"); + //print(activewindow); + activeID=getImageID(); + //print(activeID); + close("\\Others"); + + //selectWindow(activewindow); + selectImage(activeID); + + ns=nSlices(); + + n2stacktext=" slices=2-" + ns; + n1stacktext=" slices=1-" + ns-1; + + run("Make Substack...", n2stacktext); + selectImage(activeID); + run("Make Substack...", n1stacktext); + + totalOpenImages=nImages; //get total number of open images + imageIDs=newArray(nImages); //create array to hold all image IDs + imagetitles=newArray(nImages); + for(i=0;i