diff --git a/.classpath b/.classpath new file mode 100644 index 0000000000000000000000000000000000000000..7219206354fac7caf56d961f01cef916d5d98752 --- /dev/null +++ b/.classpath @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"> + <attributes> + <attribute name="module" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="var" path="ICY_JAR"/> + <classpathentry kind="var" path="ICY_PLUGINS/adufour/ezplug/EzPlug.jar"/> + <classpathentry kind="var" path="ICY_PLUGINS/adufour/blocks/Blocks.jar"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3a1f4bfdeca01d47c23831d9632af6b40a27800a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/*.jar +/bin/ diff --git a/.project b/.project new file mode 100644 index 0000000000000000000000000000000000000000..f69e79c7e58de5ada51385dab7df0ed5c21c7903 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>fft</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/FFT.png b/FFT.png new file mode 100644 index 0000000000000000000000000000000000000000..8f59f36b596c2521b5e3929b4ebab0aa69b49eeb Binary files /dev/null and b/FFT.png differ diff --git a/FFT_icon.png b/FFT_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5c94a39495b91786100b93192a3dacc4e41894c3 Binary files /dev/null and b/FFT_icon.png differ diff --git a/export.jardesc b/export.jardesc new file mode 100644 index 0000000000000000000000000000000000000000..0430195f09498518aca429b4fdfe4a6023fe3bef --- /dev/null +++ b/export.jardesc @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="WINDOWS-1252" standalone="no"?> +<jardesc> + <jar path="fft/fftPlugin.jar"/> + <options buildIfNeeded="true" compress="true" descriptionLocation="/fft/export.jardesc" exportErrors="false" exportWarnings="true" includeDirectoryEntries="true" overwrite="true" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/> + <storedRefactorings deprecationInfo="true" structuralOnly="false"/> + <selectedProjects/> + <manifest generateManifest="true" manifestLocation="/Jython/META-INF/MANIFEST.MF" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true"> + <sealing sealJar="false"> + <packagesToSeal/> + <packagesToUnSeal/> + </sealing> + </manifest> + <selectedElements exportClassFiles="true" exportJavaFiles="true" exportOutputFolder="false"> + <javaElement handleIdentifier="=fft/src"/> + <file path="/fft/export.jardesc"/> + <file path="/fft/.classpath"/> + <file path="/fft/.project"/> + </selectedElements> +</jardesc> diff --git a/src/plugins/praveen/fft/AssignFunction2D.java b/src/plugins/praveen/fft/AssignFunction2D.java new file mode 100644 index 0000000000000000000000000000000000000000..2a49873857a04f04df0c438c2b11867d1eae7e36 --- /dev/null +++ b/src/plugins/praveen/fft/AssignFunction2D.java @@ -0,0 +1,8 @@ +package plugins.praveen.fft; + +import cern.colt.function.tdouble.DoubleDoubleFunction; + +public interface AssignFunction2D { + void assign(double[] in, double[] out, int _w, int _h, + DoubleDoubleFunction Function); +} diff --git a/src/plugins/praveen/fft/AssignFunction3D.java b/src/plugins/praveen/fft/AssignFunction3D.java new file mode 100644 index 0000000000000000000000000000000000000000..b5ab05d70e2f977cbdf3bd130711282d10581b8c --- /dev/null +++ b/src/plugins/praveen/fft/AssignFunction3D.java @@ -0,0 +1,8 @@ +package plugins.praveen.fft; + +import cern.colt.function.tdouble.DoubleDoubleFunction; + +public interface AssignFunction3D { + void assign(double[] in, double[][][] out, int _w, int _h, int _z, int c, + DoubleDoubleFunction function); +} diff --git a/src/plugins/praveen/fft/AssignFunctions.java b/src/plugins/praveen/fft/AssignFunctions.java new file mode 100644 index 0000000000000000000000000000000000000000..bd6a326eda7a7f2abd7cfcadc6a227e79f6f5fc3 --- /dev/null +++ b/src/plugins/praveen/fft/AssignFunctions.java @@ -0,0 +1,95 @@ +package plugins.praveen.fft; + +import cern.colt.function.tdouble.DoubleDoubleFunction; + +public class AssignFunctions { + + //function that walks the 2D FFT from JTransforms and fills the sequence data array + //this is the version that does not swap the quadrants + public static class DirectAssign2D implements AssignFunction2D { + public void assign(double[] in, double[] out, int _w, int _h, + DoubleDoubleFunction function) { + for (int i = 0; i < in.length/2; i++) + { + double real = in[2*i]; + double imag = in[2*i + 1]; + + out[i] = function.apply(real, imag); + } + } + } + + //function that walks the 3D FFT from JTransforms and fills the sequence data array + //this is the version that does not swap the quadrants + public static class DirectAssign3D implements AssignFunction3D { + public void assign(double[] in, double[][][] out, int _w, int _h, int _z, int c, + DoubleDoubleFunction function) { + for(int z = 0; z < _z; z++) + { + for(int y = 0; y < _h; y++) + { + for(int x = 0; x < _w; x++) + { + double real = in[(x + (y * _w) + (z * _w * _h))*2 + 0]; + double imag = in[(x + (y * _w) + (z * _w * _h))*2 + 1]; + out[z][c][x + _w*y] = function.apply(real, imag); + } + } + } + } + } + + // function that walks the 2D FFT from JTransforms and fills the sequence data array + // this is the version that swaps the quadrants + public static class SwapAssign2D implements AssignFunction2D{ + public void assign(double[] in, double[] out, int _w, int _h, + DoubleDoubleFunction function) + { + int wc = (int) Math.ceil(_w/2); + int hc = (int) Math.ceil(_h/2); + + for(int y = 0; y < _h; y++) + { + for(int x = 0; x < _w; x++) + { + double real = in[(x + y*_w)*2 + 0]; + double imag = in[(x + y*_w)*2 + 1]; + + int sx = (x + wc)%_w; // swap quadrants ! + int sy = (y + hc)%_h; + + out[sx + _w*sy] = function.apply(real, imag); + } + } + } + } + + //function that walks the 3D FFT from JTransforms and fills the sequence data array + //this is the version that swaps the quadrants + public static class SwapAssign3D implements AssignFunction3D { + public void assign(double[] in, double[][][] out, int _w, int _h, int _z, int c, + DoubleDoubleFunction function) { + int wc = (int) Math.ceil(_w/2); + int hc = (int) Math.ceil(_h/2); + int zc = (int) Math.ceil(_z/2); + + for(int z = 0; z < _z; z++) + { + for(int y = 0; y < _h; y++) + { + for(int x = 0; x < _w; x++) + { + double real = in[(x + y * _w + z * _w * _h)*2 + 0]; + double imag = in[(x + y * _w + z * _w * _h)*2 + 1]; + + int sx = (x + wc)%_w; // swap quadrants ! + int sy = (y + hc)%_h; + int sz = (z + zc)%_z; + + out[sz][c][sx + _w*sy] = function.apply(real, imag); + } + } + } + } + } +} diff --git a/src/plugins/praveen/fft/ComplexFunctions.java b/src/plugins/praveen/fft/ComplexFunctions.java new file mode 100644 index 0000000000000000000000000000000000000000..2e385e8d3c996a070f344e4fae3065182d635724 --- /dev/null +++ b/src/plugins/praveen/fft/ComplexFunctions.java @@ -0,0 +1,37 @@ +package plugins.praveen.fft; + +import cern.colt.function.tdouble.DoubleDoubleFunction; + +public class ComplexFunctions { + public static class Real implements DoubleDoubleFunction + { + public double apply(double real, double imag) + { + return real; + } + }; + + public static class Imag implements DoubleDoubleFunction + { + public double apply(double real, double imag) + { + return imag; + } + }; + + public static class Magnitude implements DoubleDoubleFunction + { + public double apply(double real, double imag) + { + return Math.sqrt(Math.pow(real, 2) + Math.pow(imag, 2)); + } + }; + + public static class Angle implements DoubleDoubleFunction + { + public double apply(double real, double imag) + { + return Math.atan2(imag, real); + } + }; +} diff --git a/src/plugins/praveen/fft/FFT.java b/src/plugins/praveen/fft/FFT.java new file mode 100644 index 0000000000000000000000000000000000000000..68f5b1d61aeb455805dd27fc3ca5c9c41466471c --- /dev/null +++ b/src/plugins/praveen/fft/FFT.java @@ -0,0 +1,220 @@ +package plugins.praveen.fft; + +import cern.colt.function.tdouble.DoubleDoubleFunction; +import edu.emory.mathcs.jtransforms.fft.DoubleFFT_2D; +import edu.emory.mathcs.jtransforms.fft.DoubleFFT_3D; +import icy.image.IcyBufferedImage; +import icy.sequence.Sequence; +import icy.type.DataType; +import icy.type.collection.array.Array1DUtil; +import plugins.adufour.blocks.lang.Block; +import plugins.adufour.blocks.util.VarList; +import plugins.adufour.ezplug.EzPlug; +import plugins.adufour.ezplug.EzVarBoolean; +import plugins.adufour.ezplug.EzVarEnum; +import plugins.adufour.ezplug.EzVarSequence; +import plugins.adufour.vars.lang.VarSequence; + +public class FFT extends EzPlug implements Block { + + public static enum FFTDims { + FFT_2D("2D (xy)"), FFT_3D("3D (xyz)"); + private String stringValue; + FFTDims(String s) { stringValue = s; } + public String toString() { return stringValue; } + } + + public static enum FFTOutputType { + MAGNITUDE_PHASE("Magnitude/Phase Pair"), REAL_IMAG("Real/Imaginary Pair"); + private String stringValue; + FFTOutputType(String s) { stringValue = s; } + public String toString() { return stringValue; } + } + + EzVarSequence input = new EzVarSequence("Input"); + EzVarEnum<FFTDims> ndims = new EzVarEnum<FFTDims>("Type", FFTDims.values(), 0); + EzVarEnum<FFTOutputType> outputType = new EzVarEnum<FFTOutputType>("Output as", FFTOutputType.values(), 0); + EzVarBoolean swap = new EzVarBoolean("Swap Quadrants?", false); + + VarSequence fSequenceVar = new VarSequence("FFT sequence", null); + + @Override + protected void initialize() { + super.addEzComponent(input); + super.addEzComponent(ndims); + super.addEzComponent(outputType); + super.addEzComponent(swap); + super.setTimeDisplay(true); + } + + // declare ourself to Blocks + @Override + public void declareInput(VarList inputMap) { + inputMap.add(input.name, input.getVariable()); + inputMap.add(ndims.name, ndims.getVariable()); + inputMap.add(outputType.name, outputType.getVariable()); + inputMap.add(swap.name, swap.getVariable()); + } + + // declare ourself to Blocks + @Override + public void declareOutput(VarList outputMap) { + outputMap.add(fSequenceVar.getName(), fSequenceVar); + } + + @Override + protected void execute() { + Sequence sequence = input.getValue(); + Sequence fSequence = null; + + if(ndims.getValue()==FFTDims.FFT_2D) + { + fSequence = FFT_2D(sequence, swap.getValue(), outputType.getValue()); + } + else + { + if (sequence.getSizeZ() >= 2) + { + fSequence = FFT_3D(sequence, swap.getValue(), outputType.getValue()); + } + else + { + System.err.println("Sequence depth is 1, so computing 2D FFT instead of 3D."); + fSequence = FFT_2D(sequence, swap.getValue(), outputType.getValue()); + } + } + + if (!isHeadLess()) { + addSequence(fSequence); + } + + fSequenceVar.setValue(fSequence); + } + + private Sequence FFT_3D(Sequence sequence, boolean swap, FFTOutputType outputType) { + int _w = sequence.getSizeX(); + int _h = sequence.getSizeY(); + int _z = sequence.getSizeZ(); + + final DoubleFFT_3D fft = new DoubleFFT_3D(_z, _h, _w); + Sequence fSequence = new Sequence(); + fSequence.setName("Fourier Transform 3D"); + + // allocate the output sequence + for(int k = 0; k < _z; k++) + { + IcyBufferedImage fImage = new IcyBufferedImage(_w, _h, 2, DataType.DOUBLE); + fSequence.setImage(0, k, fImage); + } + + double[] fArray = new double[_w*_h*_z*2]; + // copy the data in fArray, with proper structure + for(int k = 0; k < _z; k++) + { + Array1DUtil.arrayToDoubleArray(sequence.getDataXY(0, k, 0), 0, fArray, k*_w*_h, _w*_h, sequence.isSignedDataType()); + } + + fft.realForwardFull(fArray); + + // direct reference to 3D byte array data [Z][C][XY] for specified t + double[][][] resultData = fSequence.getDataXYCZAsDouble(0); + + DoubleDoubleFunction channel0ApplyFunction = null; + DoubleDoubleFunction channel1ApplyFunction = null; + if(outputType == FFTOutputType.MAGNITUDE_PHASE) + { + channel0ApplyFunction = new ComplexFunctions.Magnitude(); + channel1ApplyFunction = new ComplexFunctions.Angle(); + fSequence.setChannelName(0, "Magnitude"); + fSequence.setChannelName(1, "Phase"); + } + else + { + channel0ApplyFunction = new ComplexFunctions.Real(); + channel1ApplyFunction = new ComplexFunctions.Imag(); + fSequence.setChannelName(0, "Real"); + fSequence.setChannelName(1, "Imaginary"); + } + + AssignFunction3D assignFunction = null; + if(!swap) // No Quadrant swapping. Leave as it is. + { + assignFunction = new AssignFunctions.DirectAssign3D(); + } + else + { + assignFunction = new AssignFunctions.SwapAssign3D(); // Swap Quadrants + } + + assignFunction.assign(fArray, resultData, _w, _h, _z, 0, channel0ApplyFunction); + assignFunction.assign(fArray, resultData, _w, _h, _z, 1, channel1ApplyFunction); + + fSequence.dataChanged(); + + return fSequence; + } + + private Sequence FFT_2D(Sequence sequence, boolean swap, FFTOutputType outputType) + { + Sequence fSequence = new Sequence(); + fSequence.setName("Fourier Transform 2D"); + int _w = sequence.getSizeX(); + int _h = sequence.getSizeY(); + int _z = sequence.getSizeZ(); + + final DoubleFFT_2D fft = new DoubleFFT_2D(_h, _w); + + DoubleDoubleFunction channel0Function = null; + DoubleDoubleFunction channel1Function = null; + if(outputType == FFTOutputType.MAGNITUDE_PHASE) + { + channel0Function = new ComplexFunctions.Magnitude(); + channel1Function = new ComplexFunctions.Angle(); + fSequence.setChannelName(0, "Magnitude"); + fSequence.setChannelName(1, "Phase"); + } + else // Real/Imaginary Pair + { + channel0Function = new ComplexFunctions.Real(); + channel1Function = new ComplexFunctions.Imag(); + fSequence.setChannelName(0, "Real"); + fSequence.setChannelName(1, "Imaginary"); + } + + AssignFunction2D assignFunction = null; + if(!swap) //No Quadrant swapping + { + assignFunction = new AssignFunctions.DirectAssign2D(); + } + else //Swap quadrants + { + assignFunction = new AssignFunctions.SwapAssign2D(); + } + + for(int k = 0; k < _z; k++) + { + double[] fArray = new double[_w*_h*2]; + Array1DUtil.arrayToDoubleArray(sequence.getDataXY(0, k, 0), 0, fArray, 0, _w*_h, sequence.isSignedDataType()); + + // Computes 2D forward DFT of real data leaving the result in fArray + // Because the result is stored in fArray, fArray must be of size rows*2*columns, + // with only the first rows*columns elements filled with real data. + fft.realForwardFull(fArray); + + IcyBufferedImage resultArray = new IcyBufferedImage(_w, _h, 2, DataType.DOUBLE); + double[][] resultData = resultArray.getDataXYCAsDouble(); + + assignFunction.assign(fArray, resultData[0], _w, _h, channel0Function); + assignFunction.assign(fArray, resultData[1], _w, _h, channel1Function); + + resultArray.dataChanged(); + fSequence.setImage(0, k, resultArray); + } + + return fSequence; + } + + @Override + public void clean() { + } +} \ No newline at end of file