From 124e27e99520eca4844947837a607ac6e7f309bb Mon Sep 17 00:00:00 2001 From: Glenn Tattersall Date: Sun, 12 May 2019 18:34:55 -0400 Subject: [PATCH] readme update, toolset update, added fourier analysis, ROI stack function, and cumulative difference sum function --- Readme.md | 22 +- toolsets/ThermImageJ.ijm | 525 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 537 insertions(+), 10 deletions(-) diff --git a/Readme.md b/Readme.md index 4b18a87..9f67858 100644 --- a/Readme.md +++ b/Readme.md @@ -216,12 +216,18 @@ Main Functions and Features - see SampleFiles.zip for sample data - Raw Import SEQ - custom macro to import FLIR SEQ using the Import-Raw command - - use only if you know the precise offset byte start and the number of bytes between frames. - - only works for certain SEQ files, and only formats where tiff format underlies the video + - use only if you know the precise offset byte start and the number of bytes between frames (see Frame Start Byte Macro below).s + - this only works for certain SEQ files (usually those captured to computer), and only formats where tiff format underlies the video. - see SampleFiles.zip for sample data ### Bits and Bytes +- Frame Start Byte + - This macro will scan a FLIR video file (SEQ) for the offset byte position '0200wwwwhhhh' where wwww and hhhh are the image width and height in 16-bit little endian hexadecimal. + - For example, the magicbyte for a 640x480 camera: 02008002e001", "8002" corresponds to 640 and "e001" corresponds to 480. + - The user can provide a custom magicbyte, but should leave this blank otherwise. + - The function returns best estimates for the offset and gap bytes necessary for use with the Raw Import FLIR SEQ macro, although is not guaranteed to be correct due to variances in SEQ file saving convention. + - Note: on unix based OS, this macro calls the **xxd** executable and runs quickly. For Windows OS, Powershell Core 6 needs to be installed with the updated **Format-Hex** function, and runs slowly. - Image Byte swap - simple call to the Byte Swapper plugin. - since FLIR files are sometimes saved using little endian order (tiff) and big endian order (png), a short-cut to a pixel byte swap is a fast way to repair files that have byte order mixed up @@ -278,6 +284,18 @@ Main Functions and Features - adds the result of the ROI parameter to the image as an overlay. - will work on stacks or single images. +### Math on Stacks + +- ROI on Entire Stack + - performs an ROI analysis across the entire stack. + - min, max, mean, median, mode, skewness, kurtosis for every slice are exported to the results window and to file to desktop + - select what summary statistic to perform discrete fourier analysis to extract dominant frequency components. +- Cumulative Difference Sum on Stack (in progress) + - This function works on stacks, first by subtracting the difference in pixel values between frames, creating an absolute value difference stack n-1 frames in length. + - Then all pixels from each frame are examined for the mean and standard deviation per frame, stored to the results window, after which a cumulative value is calculated. + - This cumulative absolute difference value is then detrended and zeroed to remove mean value offset prior to a discrete fourier analysis to return freuquency components. + - The user should provide time interval in seconds for the image stack. + Workflow -------- diff --git a/toolsets/ThermImageJ.ijm b/toolsets/ThermImageJ.ijm index 60ed4cb..a15439d 100644 --- a/toolsets/ThermImageJ.ijm +++ b/toolsets/ThermImageJ.ijm @@ -34,6 +34,8 @@ var IRT = parseFloat(call("ij.Prefs.get", "IRT.persistent","1")); var RH = parseFloat(call("ij.Prefs.get", "RH.persistent","50.0")); 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")); /////////////////////////////////////////////////////////////////////////////////////////////////////// //// //// @@ -55,7 +57,7 @@ var perlsplit=perlscriptpath + "split.pl"; var OS=getInfo("os.name"); // OSX Users verify these settings: // <- VERIFY THIS -var perlpathOSX="/usr/local/bin/"; +var perlpathOSX="/usr/bin/"; // or var perlpathOSX="/usr/local/bin" var exiftoolpathOSX="/usr/local/bin/"; var exiftoolOSX="exiftool"; var ffmpegpathOSX="/usr/local/bin/"; @@ -232,6 +234,49 @@ function Pearson(ArrayX, ArrayY){ return r; } +// Returns the regression slope of y on x +function Slope(ArrayX, ArrayY){ + Array.getStatistics(ArrayX, mean, min, max, std); + meanx=mean; + sx=std; + Array.getStatistics(ArrayY, mean, min, max, std); + meany=mean; + sy=std; + slp=Pearson(ArrayX, ArrayY)*sy/sx; + return slp; +} + +// Returns the regression intercept of y on x +function Intercept(ArrayX, ArrayY){ + Array.getStatistics(ArrayX, mean, min, max, std); + meanx=mean; + Array.getStatistics(ArrayY, mean, min, max, std); + meany=mean; + slp=Slope(ArrayX, ArrayY); + b=meany - slp * meanx; + return b; +} + +// create a random sequence of numbers. assume gaussian. provide mean and sd +function randomseq(n, mean, sd){ + rn=newArray(n); + for (i=0; i1){ run("32-bit", "stack"); run("Macro...", "code=v=" +PB+ "/log(" +PR1+ "/(" +PR2+ "*(v/" +rawdivisor+ "-" +rawsubtract+ "+" +PO+ "))+" +PF+ ")-273.15 stack"); @@ -1118,6 +1176,9 @@ function Raw2Temp(PR1, PR2, PB, PF, PO, E, OD, RTemp, ATemp, IRWTemp, IRT, RH, p mintemp=min; maxtemp=max; } + t2=getTime(); + deltat=t2-t1; + print(deltat); //setBatchMode(false); //mintemp=PB/log(PR1/(PR2*(minpix/rawdivisor-rawsubtract+PO))+PF)-273.15; @@ -1355,6 +1416,142 @@ function addMeasurementLabel(type, units, decimals, colour, addROI, drawx, drawy } + +function StackCumulativeDiffSummation(interval, dataType, windowType, detrend, removemean){ + + run("Duplicate...", "duplicate"); + run("8-bit"); + run("Stack Difference", "gap=1"); // default provides absolute difference, no negative numbers + run("Enhance Contrast", "saturated=0.35"); + + ns=nSlices(); + + // create arrays for mean and standard deviations + mn=newArray(ns); + sd=newArray(ns); + + // obtain the mean and sd of the difference image. each frame's values are summarised + for(i=0; i0){ + magicbyte=custommagicbyte; + } + + if(lengthOf(custommagicbyte)==0){ + magicbyte = "0200" + swap(width) + swap(height); + } + + call("ij.Prefs.set", "magicbyte.persistent", toString(magicbyte)); + + filepath=File.openDialog("Select a File"); + print("Magicbyte search"); + print("Scanning: ", filepath, "for ", magicbyte); + + // create a search byte length that is at least 2 frames in length + searchlength=imagewidth*imageheight*2*2; + + // command="xxd -p -l " + searchlength + " " + filepath + " | grep -aob " + magicbyte + " | head -n10"; + command="xxd -g 1 -ps -l " + searchlength + " -aob " + filepath; + // use the bash xxd to hexdump "searchlength" amount of the begining of file and store this as a string variable + + if(OS=="Mac OS X"){ + print("Detected Operating System: " + OS); + print("Using the following bash command: "); + print(command); + res=exec("/bin/sh", "-c", command); + print("Cleaning up hex output"); + res=replace(res, "\n", ""); + } + + if(OS=="Linux"){ + print("Detected Operating System: " + OS); + print("Using the following bash command: "); + print(command); + res=exec("/bin/sh", "-c", command); + print("Cleaning up hex output"); + res=replace(res, "\n", ""); + } + + // Windows does not have xxd installed, so need an alternative - + // Need to install powershell open source frmo github: + // https://github.com/powershell/powershell + + if(substring(OS, 0, 5)=="Windo"){ + + checkpwsh=exec("where pwsh.exe"); + if(lengthOf(checkpwsh)==0){ + exit("Powershell Core 6 (pwsh.exe) not found.\nPlease install from github.com/powershell and try again."); + } + + command="Format-Hex -Path " + filepath + " " + "-Count " + searchlength; + + print("Detected Operating System: " + OS); + print("Using the following powershell command: "); + print(command); + + res=exec("pwsh", "-c", command); + //print(res); + resarray=split(res, "\n"); + resarray=Array.deleteIndex(resarray, 0); resarray=Array.deleteIndex(resarray, 0); + resarray=Array.deleteIndex(resarray, 0); resarray=Array.deleteIndex(resarray, 0); + resarray=Array.deleteIndex(resarray, 0); + + print("Cleaning up hex output"); + res=""; + start=20; // removes the first 20 char of the byte line + end=70; // removes the unicode conversion at the end of each line + String.resetBuffer; + for(i=0; i0){ + ind[j]=ind[j] + ind[j-1] + lengthOf(magicbyte)/2; + // add the previous index value so that ind reflects position in string + } + + j++; + newres=substring(newres, position + lengthOf(magicbyte), l); + } + } + + //Array.print(ind); + ind=Array.deleteValue(ind, 0); // remove 0s from the array + + if(ind.length<2){ + Array.print(ind); + print("Number of magicbyte positions detected: " + ind.length); + print("\n"); + exit("Too few magicbyte positions detected. Please try a different magicbyte or use a Hex editor to search manually."); + } + + for(j=0; j