From adf5c37424fb4823aac8b1511ad62287083fe7c8 Mon Sep 17 00:00:00 2001 From: "E. Soubies" <emmanuel.soubies@irit.fr> Date: Sun, 19 Nov 2023 23:48:19 +0100 Subject: [PATCH] modif settings pannels + make a proper saving of the table + the eigenPSF + all stacks raw / processed / background / proj --- jar/EigenPSF_Extractor-0.0.3.jar | Bin 23401410 -> 23755267 bytes javaworkspace/EigenPSF_Extractor/.classpath | 1 - javaworkspace/EigenPSF_Extractor/.project | 500 +- .../tt/BackgroundPatches.tif | Bin 0 -> 76415 bytes .../EigenPSF_Extractor/tt/EigenPSF.tif | Bin 0 -> 1365 bytes .../tt/ProcessedPatches.tif | Bin 0 -> 76415 bytes .../tt/ProjectedPatches.tif | Bin 0 -> 76415 bytes .../EigenPSF_Extractor/tt/RawPatches.tif | Bin 0 -> 76415 bytes .../EigenPSF_Extractor/tt/config.csv | 18 + .../EigenPSF_Extractor/tt/listfiles.csv | 2 + .../EigenPSF_Extractor/tt/patchesTable.csv | 60 + src/bilib/src/.DS_Store | Bin 0 -> 10244 bytes src/bilib/src/additionaluserinterface.zip | Bin 0 -> 15430 bytes .../src/additionaluserinterface/Chrono.java | 54 + .../additionaluserinterface/GridPanel.java | 128 + .../additionaluserinterface/GridToolbar.java | 191 + .../additionaluserinterface/NumericTable.java | 82 + .../src/additionaluserinterface/Settings.java | 479 + .../SpinnerDouble.java | 105 + .../additionaluserinterface/SpinnerFloat.java | 106 + .../SpinnerInteger.java | 104 + .../src/additionaluserinterface/WalkBar.java | 356 + src/bilib/src/bilib.zip | Bin 0 -> 137151 bytes src/bilib/src/bilib/.DS_Store | Bin 0 -> 6148 bytes src/bilib/src/bilib/commons.zip | Bin 0 -> 88216 bytes src/bilib/src/bilib/commons/.DS_Store | Bin 0 -> 6148 bytes .../bilib/commons/buttons/ButtonFactory.java | 89 + src/bilib/src/bilib/commons/buttons/about.png | Bin 0 -> 827 bytes src/bilib/src/bilib/commons/buttons/close.png | Bin 0 -> 3381 bytes src/bilib/src/bilib/commons/buttons/help.png | Bin 0 -> 3075 bytes src/bilib/src/bilib/commons/buttons/prefs.png | Bin 0 -> 3696 bytes src/bilib/src/bilib/commons/buttons/run.png | Bin 0 -> 3144 bytes src/bilib/src/bilib/commons/buttons/save.png | Bin 0 -> 3269 bytes .../src/bilib/commons/buttons/snapshot.png | Bin 0 -> 3373 bytes src/bilib/src/bilib/commons/buttons/stop.png | Bin 0 -> 2923 bytes .../components/BorderToggledButton.java | 54 + .../components/DoubleScrollablePanel.java | 73 + .../bilib/commons/components/GridPanel.java | 200 + .../bilib/commons/components/GridToolbar.java | 246 + .../bilib/commons/components/HTMLPane.java | 141 + .../components/SpinnerRangeDouble.java | 197 + .../commons/components/SpinnerRangeFloat.java | 197 + .../components/SpinnerRangeInteger.java | 196 + src/bilib/src/bilib/commons/fft/BasicFFT.java | 234 + src/bilib/src/bilib/commons/job/.DS_Store | Bin 0 -> 6148 bytes .../src/bilib/commons/job/ExecutionMode.java | 17 + .../src/bilib/commons/job/JobAbstract.java | 69 + src/bilib/src/bilib/commons/job/JobEvent.java | 94 + .../bilib/commons/job/MonitorAbstract.java | 32 + .../src/bilib/commons/job/MonitorConsole.java | 55 + .../bilib/commons/job/MonitorProgressBar.java | 76 + .../bilib/commons/job/MonitorTimedLog.java | 100 + .../commons/job/MonitorTimedProgressBar.java | 106 + .../src/bilib/commons/job/PoolAbstract.java | 52 + .../commons/job/callable/CallableDemo.java | 303 + .../src/bilib/commons/job/callable/Job.java | 253 + .../src/bilib/commons/job/callable/Pool.java | 288 + .../commons/job/callable/PoolResponder.java | 24 + .../src/bilib/commons/job/runnable/Job.java | 251 + .../src/bilib/commons/job/runnable/Pool.java | 286 + .../commons/job/runnable/PoolResponder.java | 24 + .../commons/job/runnable/RunnableDemo.java | 301 + .../src/bilib/commons/job/worker/Job.java | 255 + .../src/bilib/commons/job/worker/Pool.java | 292 + .../commons/job/worker/PoolResponder.java | 24 + .../bilib/commons/job/worker/WorkerDemo.java | 303 + src/bilib/src/bilib/commons/math/.DS_Store | Bin 0 -> 6148 bytes .../src/bilib/commons/math/bessel/Bessel.java | 82 + .../commons/math/windowing/Windowing.java | 177 + src/bilib/src/bilib/commons/random/Noise.java | 29 + .../commons/random/NoiseExponential.java | 50 + .../bilib/commons/random/NoiseGaussian.java | 50 + .../bilib/commons/random/NoisePoisson.java | 98 + .../bilib/commons/random/NoiseRayleigh.java | 43 + .../bilib/commons/random/NoiseUniform.java | 51 + .../src/bilib/commons/settings/Settings.java | 430 + .../commons/settings/SettingsFileDialog.java | 107 + .../bilib/commons/table/CustomizedColumn.java | 102 + .../bilib/commons/table/CustomizedTable.java | 336 + src/bilib/src/bilib/commons/utils/Chrono.java | 91 + src/bilib/src/bilib/commons/utils/Files.java | 84 + src/bilib/src/bilib/commons/utils/Log.java | 59 + .../src/bilib/commons/utils/NumFormat.java | 157 + .../src/bilib/commons/utils/WebBrowser.java | 74 + .../bilib/fft/AcademicFourierTransform.java | 21796 ++++++++++++++++ src/bilib/src/bilib/optimization/.DS_Store | Bin 0 -> 6148 bytes .../levenbergmarquardt/Cholesky.java | 111 + .../levenbergmarquardt/Function.java | 32 + .../LevenbergMarquardt.java | 297 + src/bilib/src/ijtools/Convolver1D.java | 118 + src/bilib/src/ijtools/Convolver2D.java | 1133 + src/bilib/src/ijtools/IJmath.java | 549 + src/bilib/src/ijtools/IJtools.java | 847 + src/bilib/src/ijtools/Interpolator.java | 324 + src/bilib/src/ijtools/SplineConvolver.java | 437 + src/bilib/src/imageware.zip | Bin 0 -> 88737 bytes src/bilib/src/imageware/Access.java | 256 + src/bilib/src/imageware/Buffer.java | 188 + src/bilib/src/imageware/Builder.java | 732 + src/bilib/src/imageware/ByteAccess.java | 1 + src/bilib/src/imageware/ByteBuffer.java | 1 + src/bilib/src/imageware/BytePointwise.java | 1 + src/bilib/src/imageware/ByteProcess.java | 1 + src/bilib/src/imageware/ByteSet.java | 1 + src/bilib/src/imageware/Convolver.java | 1 + src/bilib/src/imageware/Display.java | 1 + src/bilib/src/imageware/DoubleAccess.java | 1 + src/bilib/src/imageware/DoubleBuffer.java | 2769 ++ src/bilib/src/imageware/DoublePointwise.java | 1 + src/bilib/src/imageware/DoubleProcess.java | 1 + src/bilib/src/imageware/DoubleSet.java | 1 + src/bilib/src/imageware/FMath.java | 1 + src/bilib/src/imageware/FloatAccess.java | 1 + src/bilib/src/imageware/FloatBuffer.java | 1 + src/bilib/src/imageware/FloatPointwise.java | 1 + src/bilib/src/imageware/FloatProcess.java | 1 + src/bilib/src/imageware/FloatSet.java | 1 + src/bilib/src/imageware/ImageAccess.java | 1 + src/bilib/src/imageware/ImageWare.java | 62 + src/bilib/src/imageware/Pointwise.java | 72 + src/bilib/src/imageware/Process.java | 27 + src/bilib/src/imageware/ShortAccess.java | 1 + src/bilib/src/imageware/ShortBuffer.java | 2768 ++ src/bilib/src/imageware/ShortPointwise.java | 1 + src/bilib/src/imageware/ShortProcess.java | 1 + src/bilib/src/imageware/ShortSet.java | 1 + src/bilib/src/jama/CholeskyDecomposition.java | 195 + .../src/jama/EigenvalueDecomposition.java | 990 + src/bilib/src/jama/LUDecomposition.java | 318 + src/bilib/src/jama/Maths.java | 1 + src/bilib/src/jama/Matrix.java | 1276 + src/bilib/src/jama/QRDecomposition.java | 240 + .../src/jama/SingularValueDecomposition.java | 573 + .../src/levenbergmarquardt/Cholesky.java | 111 + .../src/levenbergmarquardt/Function.java | 32 + .../LevenbergMarquardt.java | 297 + .../polyharmonicwavelets/Autocorrelation.java | 291 + .../polyharmonicwavelets/CoeffProcessing.java | 371 + .../polyharmonicwavelets/ComplexImage.java | 2706 ++ .../polyharmonicwavelets/DyadicFilters.java | 497 + .../polyharmonicwavelets/DyadicTransform.java | 556 + src/bilib/src/polyharmonicwavelets/FFT1D.java | 1 + .../polyharmonicwavelets/GammaFunction.java | 208 + .../src/polyharmonicwavelets/Parameters.java | 139 + .../polyharmonicwavelets/QuincunxFilters.java | 505 + .../QuincunxTransform.java | 561 + src/bilib/src/polyharmonicwavelets/Riesz.java | 447 + src/bilib/src/wavelets/ComplexWaveFilter.java | 283 + src/bilib/src/wavelets/ComplexWavelet.java | 509 + src/bilib/src/wavelets/ImageAccess.java | 1354 + src/bilib/src/wavelets/WaveSpline.java | 423 + src/bilib/src/wavelets/WaveSplineFilter.java | 181 + src/src/eigenpsf/AdvancedSettingsPanel.java | 14 +- src/src/eigenpsf/Constants.java | 2 +- src/src/eigenpsf/SettingsDialog.java | 40 +- src/src/eigenpsf/processing/Processing.java | 15 +- src/src/eigenpsf/project/MainDialog.java | 120 +- src/src/eigenpsf/project/Toolbar.java | 16 +- src/src/eigenpsf/stack/Patch.java | 4 + todo.txt | 1 - 160 files changed, 54802 insertions(+), 53 deletions(-) create mode 100644 javaworkspace/EigenPSF_Extractor/tt/BackgroundPatches.tif create mode 100644 javaworkspace/EigenPSF_Extractor/tt/EigenPSF.tif create mode 100644 javaworkspace/EigenPSF_Extractor/tt/ProcessedPatches.tif create mode 100644 javaworkspace/EigenPSF_Extractor/tt/ProjectedPatches.tif create mode 100644 javaworkspace/EigenPSF_Extractor/tt/RawPatches.tif create mode 100644 javaworkspace/EigenPSF_Extractor/tt/config.csv create mode 100644 javaworkspace/EigenPSF_Extractor/tt/listfiles.csv create mode 100644 javaworkspace/EigenPSF_Extractor/tt/patchesTable.csv create mode 100644 src/bilib/src/.DS_Store create mode 100644 src/bilib/src/additionaluserinterface.zip create mode 100644 src/bilib/src/additionaluserinterface/Chrono.java create mode 100644 src/bilib/src/additionaluserinterface/GridPanel.java create mode 100644 src/bilib/src/additionaluserinterface/GridToolbar.java create mode 100644 src/bilib/src/additionaluserinterface/NumericTable.java create mode 100644 src/bilib/src/additionaluserinterface/Settings.java create mode 100644 src/bilib/src/additionaluserinterface/SpinnerDouble.java create mode 100644 src/bilib/src/additionaluserinterface/SpinnerFloat.java create mode 100644 src/bilib/src/additionaluserinterface/SpinnerInteger.java create mode 100644 src/bilib/src/additionaluserinterface/WalkBar.java create mode 100644 src/bilib/src/bilib.zip create mode 100644 src/bilib/src/bilib/.DS_Store create mode 100644 src/bilib/src/bilib/commons.zip create mode 100644 src/bilib/src/bilib/commons/.DS_Store create mode 100644 src/bilib/src/bilib/commons/buttons/ButtonFactory.java create mode 100644 src/bilib/src/bilib/commons/buttons/about.png create mode 100644 src/bilib/src/bilib/commons/buttons/close.png create mode 100644 src/bilib/src/bilib/commons/buttons/help.png create mode 100644 src/bilib/src/bilib/commons/buttons/prefs.png create mode 100644 src/bilib/src/bilib/commons/buttons/run.png create mode 100644 src/bilib/src/bilib/commons/buttons/save.png create mode 100644 src/bilib/src/bilib/commons/buttons/snapshot.png create mode 100644 src/bilib/src/bilib/commons/buttons/stop.png create mode 100644 src/bilib/src/bilib/commons/components/BorderToggledButton.java create mode 100644 src/bilib/src/bilib/commons/components/DoubleScrollablePanel.java create mode 100644 src/bilib/src/bilib/commons/components/GridPanel.java create mode 100644 src/bilib/src/bilib/commons/components/GridToolbar.java create mode 100644 src/bilib/src/bilib/commons/components/HTMLPane.java create mode 100644 src/bilib/src/bilib/commons/components/SpinnerRangeDouble.java create mode 100644 src/bilib/src/bilib/commons/components/SpinnerRangeFloat.java create mode 100644 src/bilib/src/bilib/commons/components/SpinnerRangeInteger.java create mode 100644 src/bilib/src/bilib/commons/fft/BasicFFT.java create mode 100644 src/bilib/src/bilib/commons/job/.DS_Store create mode 100644 src/bilib/src/bilib/commons/job/ExecutionMode.java create mode 100644 src/bilib/src/bilib/commons/job/JobAbstract.java create mode 100644 src/bilib/src/bilib/commons/job/JobEvent.java create mode 100644 src/bilib/src/bilib/commons/job/MonitorAbstract.java create mode 100644 src/bilib/src/bilib/commons/job/MonitorConsole.java create mode 100644 src/bilib/src/bilib/commons/job/MonitorProgressBar.java create mode 100644 src/bilib/src/bilib/commons/job/MonitorTimedLog.java create mode 100644 src/bilib/src/bilib/commons/job/MonitorTimedProgressBar.java create mode 100644 src/bilib/src/bilib/commons/job/PoolAbstract.java create mode 100644 src/bilib/src/bilib/commons/job/callable/CallableDemo.java create mode 100644 src/bilib/src/bilib/commons/job/callable/Job.java create mode 100644 src/bilib/src/bilib/commons/job/callable/Pool.java create mode 100644 src/bilib/src/bilib/commons/job/callable/PoolResponder.java create mode 100644 src/bilib/src/bilib/commons/job/runnable/Job.java create mode 100644 src/bilib/src/bilib/commons/job/runnable/Pool.java create mode 100644 src/bilib/src/bilib/commons/job/runnable/PoolResponder.java create mode 100644 src/bilib/src/bilib/commons/job/runnable/RunnableDemo.java create mode 100644 src/bilib/src/bilib/commons/job/worker/Job.java create mode 100644 src/bilib/src/bilib/commons/job/worker/Pool.java create mode 100644 src/bilib/src/bilib/commons/job/worker/PoolResponder.java create mode 100644 src/bilib/src/bilib/commons/job/worker/WorkerDemo.java create mode 100644 src/bilib/src/bilib/commons/math/.DS_Store create mode 100644 src/bilib/src/bilib/commons/math/bessel/Bessel.java create mode 100644 src/bilib/src/bilib/commons/math/windowing/Windowing.java create mode 100644 src/bilib/src/bilib/commons/random/Noise.java create mode 100644 src/bilib/src/bilib/commons/random/NoiseExponential.java create mode 100644 src/bilib/src/bilib/commons/random/NoiseGaussian.java create mode 100644 src/bilib/src/bilib/commons/random/NoisePoisson.java create mode 100644 src/bilib/src/bilib/commons/random/NoiseRayleigh.java create mode 100644 src/bilib/src/bilib/commons/random/NoiseUniform.java create mode 100644 src/bilib/src/bilib/commons/settings/Settings.java create mode 100644 src/bilib/src/bilib/commons/settings/SettingsFileDialog.java create mode 100644 src/bilib/src/bilib/commons/table/CustomizedColumn.java create mode 100644 src/bilib/src/bilib/commons/table/CustomizedTable.java create mode 100644 src/bilib/src/bilib/commons/utils/Chrono.java create mode 100644 src/bilib/src/bilib/commons/utils/Files.java create mode 100644 src/bilib/src/bilib/commons/utils/Log.java create mode 100644 src/bilib/src/bilib/commons/utils/NumFormat.java create mode 100644 src/bilib/src/bilib/commons/utils/WebBrowser.java create mode 100644 src/bilib/src/bilib/fft/AcademicFourierTransform.java create mode 100644 src/bilib/src/bilib/optimization/.DS_Store create mode 100644 src/bilib/src/bilib/optimization/levenbergmarquardt/Cholesky.java create mode 100644 src/bilib/src/bilib/optimization/levenbergmarquardt/Function.java create mode 100644 src/bilib/src/bilib/optimization/levenbergmarquardt/LevenbergMarquardt.java create mode 100644 src/bilib/src/ijtools/Convolver1D.java create mode 100644 src/bilib/src/ijtools/Convolver2D.java create mode 100644 src/bilib/src/ijtools/IJmath.java create mode 100644 src/bilib/src/ijtools/IJtools.java create mode 100644 src/bilib/src/ijtools/Interpolator.java create mode 100644 src/bilib/src/ijtools/SplineConvolver.java create mode 100644 src/bilib/src/imageware.zip create mode 100644 src/bilib/src/imageware/Access.java create mode 100644 src/bilib/src/imageware/Buffer.java create mode 100644 src/bilib/src/imageware/Builder.java create mode 100644 src/bilib/src/imageware/ByteAccess.java create mode 100644 src/bilib/src/imageware/ByteBuffer.java create mode 100644 src/bilib/src/imageware/BytePointwise.java create mode 100644 src/bilib/src/imageware/ByteProcess.java create mode 100644 src/bilib/src/imageware/ByteSet.java create mode 100644 src/bilib/src/imageware/Convolver.java create mode 100644 src/bilib/src/imageware/Display.java create mode 100644 src/bilib/src/imageware/DoubleAccess.java create mode 100644 src/bilib/src/imageware/DoubleBuffer.java create mode 100644 src/bilib/src/imageware/DoublePointwise.java create mode 100644 src/bilib/src/imageware/DoubleProcess.java create mode 100644 src/bilib/src/imageware/DoubleSet.java create mode 100644 src/bilib/src/imageware/FMath.java create mode 100644 src/bilib/src/imageware/FloatAccess.java create mode 100644 src/bilib/src/imageware/FloatBuffer.java create mode 100644 src/bilib/src/imageware/FloatPointwise.java create mode 100644 src/bilib/src/imageware/FloatProcess.java create mode 100644 src/bilib/src/imageware/FloatSet.java create mode 100644 src/bilib/src/imageware/ImageAccess.java create mode 100644 src/bilib/src/imageware/ImageWare.java create mode 100644 src/bilib/src/imageware/Pointwise.java create mode 100644 src/bilib/src/imageware/Process.java create mode 100644 src/bilib/src/imageware/ShortAccess.java create mode 100644 src/bilib/src/imageware/ShortBuffer.java create mode 100644 src/bilib/src/imageware/ShortPointwise.java create mode 100644 src/bilib/src/imageware/ShortProcess.java create mode 100644 src/bilib/src/imageware/ShortSet.java create mode 100644 src/bilib/src/jama/CholeskyDecomposition.java create mode 100644 src/bilib/src/jama/EigenvalueDecomposition.java create mode 100644 src/bilib/src/jama/LUDecomposition.java create mode 100644 src/bilib/src/jama/Maths.java create mode 100644 src/bilib/src/jama/Matrix.java create mode 100644 src/bilib/src/jama/QRDecomposition.java create mode 100644 src/bilib/src/jama/SingularValueDecomposition.java create mode 100644 src/bilib/src/levenbergmarquardt/Cholesky.java create mode 100644 src/bilib/src/levenbergmarquardt/Function.java create mode 100644 src/bilib/src/levenbergmarquardt/LevenbergMarquardt.java create mode 100644 src/bilib/src/polyharmonicwavelets/Autocorrelation.java create mode 100644 src/bilib/src/polyharmonicwavelets/CoeffProcessing.java create mode 100644 src/bilib/src/polyharmonicwavelets/ComplexImage.java create mode 100644 src/bilib/src/polyharmonicwavelets/DyadicFilters.java create mode 100644 src/bilib/src/polyharmonicwavelets/DyadicTransform.java create mode 100644 src/bilib/src/polyharmonicwavelets/FFT1D.java create mode 100644 src/bilib/src/polyharmonicwavelets/GammaFunction.java create mode 100644 src/bilib/src/polyharmonicwavelets/Parameters.java create mode 100644 src/bilib/src/polyharmonicwavelets/QuincunxFilters.java create mode 100644 src/bilib/src/polyharmonicwavelets/QuincunxTransform.java create mode 100644 src/bilib/src/polyharmonicwavelets/Riesz.java create mode 100644 src/bilib/src/wavelets/ComplexWaveFilter.java create mode 100644 src/bilib/src/wavelets/ComplexWavelet.java create mode 100644 src/bilib/src/wavelets/ImageAccess.java create mode 100644 src/bilib/src/wavelets/WaveSpline.java create mode 100644 src/bilib/src/wavelets/WaveSplineFilter.java diff --git a/jar/EigenPSF_Extractor-0.0.3.jar b/jar/EigenPSF_Extractor-0.0.3.jar index 4083cd65759b6ef0bd70c5e33d84096068dbaf6f..4dcdecfec910ea7a47f179f1007353aa081bbab1 100644 GIT binary patch delta 500317 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�@i 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 delta 164672 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Ėt_Vp|lp1!1rzvpO)-9uK{wxd%_ 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ðxcYgxn2 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%0D`LZ4?@wP^D{@iF- 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 diff --git a/javaworkspace/EigenPSF_Extractor/.classpath b/javaworkspace/EigenPSF_Extractor/.classpath index f96c12c..e6a9ad7 100644 --- a/javaworkspace/EigenPSF_Extractor/.classpath +++ b/javaworkspace/EigenPSF_Extractor/.classpath @@ -2,7 +2,6 @@ <classpath> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="src" path="src"/> - <classpathentry kind="lib" path="lib/bilib-commons.jar"/> <classpathentry kind="lib" path="lib/ij.jar"/> <classpathentry kind="lib" path="lib/jblas-1.2.5.jar"/> <classpathentry kind="lib" path="lib/JTransforms-3.1-with-dependencies.jar"/> diff --git a/javaworkspace/EigenPSF_Extractor/.project b/javaworkspace/EigenPSF_Extractor/.project index 4f63843..45fba7c 100644 --- a/javaworkspace/EigenPSF_Extractor/.project +++ b/javaworkspace/EigenPSF_Extractor/.project @@ -40,11 +40,6 @@ <type>1</type> <locationURI>PARENT-2-PROJECT_LOC/src/lib/JTransforms-3.1-with-dependencies.jar</locationURI> </link> - <link> - <name>lib/bilib-commons.jar</name> - <type>1</type> - <locationURI>PARENT-2-PROJECT_LOC/src/lib/bilib-commons.jar</locationURI> - </link> <link> <name>lib/ij.jar</name> <type>1</type> @@ -85,6 +80,11 @@ <type>1</type> <locationURI>PARENT-2-PROJECT_LOC/src/src/Unit_Tests.java</locationURI> </link> + <link> + <name>src/bilib</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> <link> <name>src/eigenpsf</name> <type>2</type> @@ -120,6 +120,16 @@ <type>1</type> <locationURI>PARENT-2-PROJECT_LOC/src/src/RefineCodeCpp/libEigenPSF_Refine.so</locationURI> </link> + <link> + <name>bin/bilib/.DS_Store</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/.DS_Store</locationURI> + </link> + <link> + <name>bin/bilib/commons.zip</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons.zip</locationURI> + </link> <link> <name>bin/resources/about.png</name> <type>1</type> @@ -255,6 +265,31 @@ <type>1</type> <locationURI>PARENT-2-PROJECT_LOC/src/src/RefineCodeCpp/libEigenPSF_Refine.so</locationURI> </link> + <link> + <name>src/bilib/.DS_Store</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/.DS_Store</locationURI> + </link> + <link> + <name>src/bilib/commons</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons.zip</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons.zip</locationURI> + </link> + <link> + <name>src/bilib/fft</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/optimization</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> <link> <name>src/eigenpsf/AdvancedSettingsPanel.java</name> <type>1</type> @@ -460,6 +495,81 @@ <type>1</type> <locationURI>PARENT-2-PROJECT_LOC/src/src/resources/update.png</locationURI> </link> + <link> + <name>bin/bilib/commons/.DS_Store</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/.DS_Store</locationURI> + </link> + <link> + <name>bin/bilib/optimization/.DS_Store</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/optimization/.DS_Store</locationURI> + </link> + <link> + <name>src/bilib/commons/.DS_Store</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/.DS_Store</locationURI> + </link> + <link> + <name>src/bilib/commons/buttons</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/components</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/fft</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/job</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/math</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/random</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/settings</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/table</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/utils</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/fft/AcademicFourierTransform.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/fft/AcademicFourierTransform.java</locationURI> + </link> + <link> + <name>src/bilib/optimization/.DS_Store</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/optimization/.DS_Store</locationURI> + </link> + <link> + <name>src/bilib/optimization/levenbergmarquardt</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> <link> <name>src/eigenpsf/data/Convolution.java</name> <type>1</type> @@ -660,5 +770,385 @@ <type>1</type> <locationURI>PARENT-2-PROJECT_LOC/src/src/eigenpsf/stack/ZStack.java</locationURI> </link> + <link> + <name>bin/bilib/commons/buttons/about.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/about.png</locationURI> + </link> + <link> + <name>bin/bilib/commons/buttons/close.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/close.png</locationURI> + </link> + <link> + <name>bin/bilib/commons/buttons/help.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/help.png</locationURI> + </link> + <link> + <name>bin/bilib/commons/buttons/prefs.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/prefs.png</locationURI> + </link> + <link> + <name>bin/bilib/commons/buttons/run.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/run.png</locationURI> + </link> + <link> + <name>bin/bilib/commons/buttons/save.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/save.png</locationURI> + </link> + <link> + <name>bin/bilib/commons/buttons/snapshot.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/snapshot.png</locationURI> + </link> + <link> + <name>bin/bilib/commons/buttons/stop.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/stop.png</locationURI> + </link> + <link> + <name>bin/bilib/commons/job/.DS_Store</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/.DS_Store</locationURI> + </link> + <link> + <name>bin/bilib/commons/math/.DS_Store</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/math/.DS_Store</locationURI> + </link> + <link> + <name>src/bilib/commons/buttons/ButtonFactory.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/ButtonFactory.java</locationURI> + </link> + <link> + <name>src/bilib/commons/buttons/about.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/about.png</locationURI> + </link> + <link> + <name>src/bilib/commons/buttons/close.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/close.png</locationURI> + </link> + <link> + <name>src/bilib/commons/buttons/help.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/help.png</locationURI> + </link> + <link> + <name>src/bilib/commons/buttons/prefs.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/prefs.png</locationURI> + </link> + <link> + <name>src/bilib/commons/buttons/run.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/run.png</locationURI> + </link> + <link> + <name>src/bilib/commons/buttons/save.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/save.png</locationURI> + </link> + <link> + <name>src/bilib/commons/buttons/snapshot.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/snapshot.png</locationURI> + </link> + <link> + <name>src/bilib/commons/buttons/stop.png</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/buttons/stop.png</locationURI> + </link> + <link> + <name>src/bilib/commons/components/BorderToggledButton.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/BorderToggledButton.java</locationURI> + </link> + <link> + <name>src/bilib/commons/components/DoubleScrollablePanel.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/DoubleScrollablePanel.java</locationURI> + </link> + <link> + <name>src/bilib/commons/components/GridPanel.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/GridPanel.java</locationURI> + </link> + <link> + <name>src/bilib/commons/components/GridToolbar.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/GridToolbar.java</locationURI> + </link> + <link> + <name>src/bilib/commons/components/HTMLPane.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/HTMLPane.java</locationURI> + </link> + <link> + <name>src/bilib/commons/components/SpinnerRangeDouble.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/SpinnerRangeDouble.java</locationURI> + </link> + <link> + <name>src/bilib/commons/components/SpinnerRangeFloat.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/SpinnerRangeFloat.java</locationURI> + </link> + <link> + <name>src/bilib/commons/components/SpinnerRangeInteger.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/components/SpinnerRangeInteger.java</locationURI> + </link> + <link> + <name>src/bilib/commons/fft/BasicFFT.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/fft/BasicFFT.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/.DS_Store</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/.DS_Store</locationURI> + </link> + <link> + <name>src/bilib/commons/job/ExecutionMode.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/ExecutionMode.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/JobAbstract.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/JobAbstract.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/JobEvent.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/JobEvent.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/MonitorAbstract.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/MonitorAbstract.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/MonitorConsole.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/MonitorConsole.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/MonitorProgressBar.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/MonitorProgressBar.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/MonitorTimedLog.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/MonitorTimedLog.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/MonitorTimedProgressBar.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/MonitorTimedProgressBar.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/PoolAbstract.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/PoolAbstract.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/callable</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/job/runnable</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/job/worker</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/math/.DS_Store</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/math/.DS_Store</locationURI> + </link> + <link> + <name>src/bilib/commons/math/bessel</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/math/windowing</name> + <type>2</type> + <locationURI>virtual:/virtual</locationURI> + </link> + <link> + <name>src/bilib/commons/random/Noise.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/random/Noise.java</locationURI> + </link> + <link> + <name>src/bilib/commons/random/NoiseExponential.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/random/NoiseExponential.java</locationURI> + </link> + <link> + <name>src/bilib/commons/random/NoiseGaussian.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/random/NoiseGaussian.java</locationURI> + </link> + <link> + <name>src/bilib/commons/random/NoisePoisson.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/random/NoisePoisson.java</locationURI> + </link> + <link> + <name>src/bilib/commons/random/NoiseRayleigh.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/random/NoiseRayleigh.java</locationURI> + </link> + <link> + <name>src/bilib/commons/random/NoiseUniform.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/random/NoiseUniform.java</locationURI> + </link> + <link> + <name>src/bilib/commons/settings/Settings.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/settings/Settings.java</locationURI> + </link> + <link> + <name>src/bilib/commons/settings/SettingsFileDialog.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/settings/SettingsFileDialog.java</locationURI> + </link> + <link> + <name>src/bilib/commons/table/CustomizedColumn.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/table/CustomizedColumn.java</locationURI> + </link> + <link> + <name>src/bilib/commons/table/CustomizedTable.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/table/CustomizedTable.java</locationURI> + </link> + <link> + <name>src/bilib/commons/utils/Chrono.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/utils/Chrono.java</locationURI> + </link> + <link> + <name>src/bilib/commons/utils/Files.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/utils/Files.java</locationURI> + </link> + <link> + <name>src/bilib/commons/utils/Log.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/utils/Log.java</locationURI> + </link> + <link> + <name>src/bilib/commons/utils/NumFormat.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/utils/NumFormat.java</locationURI> + </link> + <link> + <name>src/bilib/commons/utils/WebBrowser.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/utils/WebBrowser.java</locationURI> + </link> + <link> + <name>src/bilib/optimization/levenbergmarquardt/Cholesky.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/optimization/levenbergmarquardt/Cholesky.java</locationURI> + </link> + <link> + <name>src/bilib/optimization/levenbergmarquardt/Function.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/optimization/levenbergmarquardt/Function.java</locationURI> + </link> + <link> + <name>src/bilib/optimization/levenbergmarquardt/LevenbergMarquardt.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/optimization/levenbergmarquardt/LevenbergMarquardt.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/callable/CallableDemo.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/callable/CallableDemo.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/callable/Job.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/callable/Job.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/callable/Pool.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/callable/Pool.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/callable/PoolResponder.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/callable/PoolResponder.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/runnable/Job.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/runnable/Job.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/runnable/Pool.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/runnable/Pool.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/runnable/PoolResponder.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/runnable/PoolResponder.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/runnable/RunnableDemo.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/runnable/RunnableDemo.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/worker/Job.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/worker/Job.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/worker/Pool.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/worker/Pool.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/worker/PoolResponder.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/worker/PoolResponder.java</locationURI> + </link> + <link> + <name>src/bilib/commons/job/worker/WorkerDemo.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/job/worker/WorkerDemo.java</locationURI> + </link> + <link> + <name>src/bilib/commons/math/bessel/Bessel.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/math/bessel/Bessel.java</locationURI> + </link> + <link> + <name>src/bilib/commons/math/windowing/Windowing.java</name> + <type>1</type> + <locationURI>PARENT-2-PROJECT_LOC/src/bilib/src/bilib/commons/math/windowing/Windowing.java</locationURI> + </link> </linkedResources> </projectDescription> diff --git a/javaworkspace/EigenPSF_Extractor/tt/BackgroundPatches.tif b/javaworkspace/EigenPSF_Extractor/tt/BackgroundPatches.tif new file mode 100644 index 0000000000000000000000000000000000000000..e830a35cefe845cc6a460fc4aa1bf687d1d16e84 GIT binary patch literal 76415 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 literal 0 HcmV?d00001 diff --git a/javaworkspace/EigenPSF_Extractor/tt/EigenPSF.tif b/javaworkspace/EigenPSF_Extractor/tt/EigenPSF.tif new file mode 100644 index 0000000000000000000000000000000000000000..29282da5bc828418a5917dea51a7c2c905584085 GIT binary patch literal 1365 zcmebEWzb?^VBlcjX86az!oa}5$N(W=Y(YjwxVRuA69Y3?T7iLqkqydbWME+AV_;%n zV6bOkV3@=x2v>iRQ4~py7?dr@z`!UDQ^V527z`B$nasez;F+74p6X?5sAp=N$CaCz zXRB+VXJ%nwVQOh=U}k7+U}R`v%9Wc~VQZjgU}#`yZfb05U}$7yXliV3YRSc5HSu(} z^|Y-0yXs#ou{L{SY%OH?&f5N<wH0I3@?E+-)w@<$iCP|r53_7(d1ZZK?cUuYflGJS zC_S^@95dBQReRyCa~q9!N_4SUylq~%yW{}-?%#L*?Cdb)-Zf|UI&1HU?biJhuUmbZ zdEI*QFKug|zaiF_Yt5~`Zq2k>JyXM4fZ5sdpHJ!zm7j(-YTKG^6n1~JnyFR4d(sSN z%cl1nyTnZQ*a%&^XFc7?YnS$<r#rt(a$0vbPPgu_tgs50^xX2YMX`C5S(1$k=R<4e z(6iS6x)xi@zL41+X5qHm*W`y4|NhInCwOUDPyVW7Q83ke_nwR`yPtnawGIk2vC{Di z+|_BmcX!kE(p@_=CtB&U&a~>8$g_u|W%8~fO{-nTJ$HAvABft$?i1r4R;Q(wjRu8Q z{k9pq-);H1NBmaGu3s%5t&WyO?K<-2s<mIXvyG@~+OA0+ZMze%F5j)b>ZH}%e@VNW zix=$fyPjyhwE6$8#eo`D6D~&XZa&zsXYunVdqUhN>^lB@k=5Z-GS(7q9($Botalfv zGg?OmzqdX-Z-=?MpOJM-$)#OW{IYh(Z%ViBlen{Mrn<xK(8ln+A(FH9zS~u`*J|H4 z%f9}bW*yH(tvAoPzk9~!g}Y}zO0>GVD9l=Z*LSO)?~8Y>>0WI;^O=%$<NGvA34@Kh zpBtO+WOARm&qrK(-|MEJeIo9OyOU=L>~6k)WOsM^=iTf|2duwN?y=6d`eOB6<nV5> zGgGXzxj$I#>I|@2Y4u_EvVy;R<l8LwnD4r^Z)T~_em$S_`$7+{-;>b(b9cCx!tRyc zxz?_FUv^DPNZd8+nYwk}!j0C=?he*b-%nX}y?(uW@iNZcm&=9ssIKeZQ=@Qf@3WsJ z`+DQ2?cKp~eNW}|p55h!GJ6D7w(Ycy^R%{K9A<4ag=stQnse6Tubp<jYS7(%)qL^p zS@%k<BP!4DGB~zik635nUT&%Cy`7uo_S#Kl+f(vJdv|=spWRazI#|v6XtsO8jOkWV zS((<|rthqJPBvKm-}`pA))(E~)ef(BuX$;^o5|39w@%uYJ!LB^_nc6_yyr&O`8}o5 zCcD;1=j`DYo@X^B__5__wX;_HF4kC2mAPYG(xGHEWzJgbc1_;h^Or5#)%d1mcW7SD z?!B{EchBBvzI(RLqdlDOnRc%}dUm(^v89$rri)k~uYSKf>C5ijjlwr~JGxd`dz`Jb zzUT9A=W4FdT@NSp>^i(p)G~MSLTkZ{&(@P4-`X8-cx`v<o@{HO{J++VA3WK;B2Up; z@Px+hwo7@t4~f3ot@ugE+U3AO>t}~Nb{=7QxqDHN!|qxMlU+T<tkzDwt9Bn_ys^uv z<$!flt^2Nd#wV?(+j3afJomS5fAhoo{_2Ih{EAI>U3TBUTQ6U4*AL<6yKXoX?fmTh z&Z=|LiCsIm=ImCNQrSK2&+gs5t=+pjGyYn4Jos$g-e#~%kYCqo&ynf7Hci>QyYJom z-9g!FtXsvqttTv8x7+pnww)aUGS<6OC-1zsF?8pVyi2=nbRO*ClQG?`#k<*B=U|s* cf|B#@Fx&RsdJn6t&s{6Cj&we2&1%gK01*LMP5=M^ literal 0 HcmV?d00001 diff --git a/javaworkspace/EigenPSF_Extractor/tt/ProcessedPatches.tif b/javaworkspace/EigenPSF_Extractor/tt/ProcessedPatches.tif new file mode 100644 index 0000000000000000000000000000000000000000..5619a00edb29f1d5e30c64013936ff41d0379ad3 GIT binary patch literal 76415 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 M+jf#&!oa`)0Ir5SzW@LL literal 0 HcmV?d00001 diff --git a/javaworkspace/EigenPSF_Extractor/tt/ProjectedPatches.tif b/javaworkspace/EigenPSF_Extractor/tt/ProjectedPatches.tif new file mode 100644 index 0000000000000000000000000000000000000000..2bde6e0b9407e602960761d7cac5c7cc00358d89 GIT binary patch literal 76415 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 literal 0 HcmV?d00001 diff --git a/javaworkspace/EigenPSF_Extractor/tt/RawPatches.tif b/javaworkspace/EigenPSF_Extractor/tt/RawPatches.tif new file mode 100644 index 0000000000000000000000000000000000000000..f863fb9f8ce0a70d6e1115484b83889ed794a530 GIT binary patch literal 76415 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Ðx{Xg>sKOc8We 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 literal 0 HcmV?d00001 diff --git a/javaworkspace/EigenPSF_Extractor/tt/config.csv b/javaworkspace/EigenPSF_Extractor/tt/config.csv new file mode 100644 index 0000000..fb61b8a --- /dev/null +++ b/javaworkspace/EigenPSF_Extractor/tt/config.csv @@ -0,0 +1,18 @@ +detectionMethod, SIFT +backgroundMethod, Polynomial Fit +registrationMethod, Scale Space +normalize, l2 +dim, 2 +Nscale, 3 +NbasisVectSIFT, 3 +NeigenElements, 1 +nthin, 7 +scaleBack, 4 +factorDiamBack, 2.0 +thresholdDetection, 0.5 +quantile, 2.0 +smin, 0.5 +smax, 1.5 +psf_visible_width_0, 17 +psf_visible_width_1, 17 +psf_visible_width_2, 1 diff --git a/javaworkspace/EigenPSF_Extractor/tt/listfiles.csv b/javaworkspace/EigenPSF_Extractor/tt/listfiles.csv new file mode 100644 index 0000000..2c705df --- /dev/null +++ b/javaworkspace/EigenPSF_Extractor/tt/listfiles.csv @@ -0,0 +1,2 @@ +Name,Beads,Show,Mask, +1,2D_widefield_astig-1.tif,/home/esoubies/Bureau/GitHub/eigenpsf-extractor/demo/2D_widefield_astig-1.tif, diff --git a/javaworkspace/EigenPSF_Extractor/tt/patchesTable.csv b/javaworkspace/EigenPSF_Extractor/tt/patchesTable.csv new file mode 100644 index 0000000..a7a581f --- /dev/null +++ b/javaworkspace/EigenPSF_Extractor/tt/patchesTable.csv @@ -0,0 +1,60 @@ +Image name ID X Y Z Quality Dist Max Valid +2D_widefield_astig-1.tif 52 231 334 0 2.6368922825871293 138.74076545846214 8,161E+03 +2D_widefield_astig-1.tif 40 289 183 0 1.9621766979946318 92.39588735436226 1,104E+04 Ok +2D_widefield_astig-1.tif 57 101 162 0 3.4731905008960595 104.06248123122954 9,983E+03 +2D_widefield_astig-1.tif 34 60 207 0 1.3833614005738457 138.2931668593933 5,789E+03 Ok +2D_widefield_astig-1.tif 36 376 248 0 1.639697072655175 184.6212338816963 6,704E+03 Ok +2D_widefield_astig-1.tif 13 315 222 0 0.9588205913708715 119.4361754243663 8,999E+03 Ok +2D_widefield_astig-1.tif 42 110 263 0 2.051581162503771 108.60018416190647 7,072E+03 +2D_widefield_astig-1.tif 15 169 99 0 1.039431340797815 104.12012293500234 8,953E+03 Ok +2D_widefield_astig-1.tif 39 98 333 0 1.9097765431261826 168.6001186239203 1,715E+04 Ok +2D_widefield_astig-1.tif 50 117 201 0 2.476141207912426 82.02438661763951 2,001E+04 +2D_widefield_astig-1.tif 49 133 218 0 2.43296303234266 68.00735254367721 1,296E+04 +2D_widefield_astig-1.tif 48 291 375 0 2.1925531446676088 200.83077453418338 1,181E+04 +2D_widefield_astig-1.tif 38 111 308 0 1.7730343406949798 140.24621207005913 7,977E+03 Ok +2D_widefield_astig-1.tif 37 141 327 0 1.7044494201054463 141.0319112825179 8,899E+03 Ok +2D_widefield_astig-1.tif 16 133 34 0 1.1020035515862272 176.41145087550296 6,234E+03 Ok +2D_widefield_astig-1.tif 35 373 159 0 1.6114569779425758 179.2930561957155 8,358E+03 Ok +2D_widefield_astig-1.tif 47 224 249 0 2.175836056596235 58.137767414994535 1,444E+04 +2D_widefield_astig-1.tif 55 193 344 0 2.9265074368094908 145.1240848377691 9,201E+03 +2D_widefield_astig-1.tif 43 193 173 0 2.1160033926768933 26.476404589747453 1,273E+04 +2D_widefield_astig-1.tif 22 152 305 0 1.1690761198439292 116.46887996370532 1,775E+04 Ok +2D_widefield_astig-1.tif 21 232 290 0 1.1684242470791302 98.08159868191383 1,104E+04 Ok +2D_widefield_astig-1.tif 53 322 242 0 2.7845849677774575 132.18925826253812 8,761E+03 +2D_widefield_astig-1.tif 46 220 307 0 2.1602337204229864 111.9866063420086 1,174E+04 +2D_widefield_astig-1.tif 7 61 257 0 0.898166793749632 149.164338901763 6,674E+03 Ok +2D_widefield_astig-1.tif 10 272 179 0 0.9308440447335254 76.6550715869472 3,827E+04 Ok +2D_widefield_astig-1.tif 2 372 202 0 0.8365680637800508 174.04597093871493 7,782E+03 Ok +2D_widefield_astig-1.tif 14 90 210 0 0.9607983531660049 108.664621657649 1,720E+04 Ok +2D_widefield_astig-1.tif 51 308 256 0 2.602329097485422 123.47064428438041 1,146E+04 +2D_widefield_astig-1.tif 11 315 190 0 0.9396605727284989 117.27318534089538 2,809E+04 Ok +2D_widefield_astig-1.tif 9 252 192 0 0.9093135207899893 53.46026561849464 2,802E+04 Ok +2D_widefield_astig-1.tif 27 108 229 0 1.2895707808526016 95.1892851112981 8,811E+03 Ok +2D_widefield_astig-1.tif 17 180 321 0 1.121353980827542 124.31009613060397 2,224E+04 Ok +2D_widefield_astig-1.tif 29 200 156 0 1.3195480677918965 42.04759208325728 1,071E+04 Ok +2D_widefield_astig-1.tif 56 184 292 0 3.2042571166320943 94.89467845985885 1,406E+04 +2D_widefield_astig-1.tif 41 367 115 0 2.0246417215407626 186.9438418349211 7,738E+03 +2D_widefield_astig-1.tif 24 89 308 0 1.2037193785876887 155.56349186104046 1,193E+04 Ok +2D_widefield_astig-1.tif 1 356 167 0 0.7821929662752916 160.0312469488381 1,100E+04 Ok +2D_widefield_astig-1.tif 59 263 296 0 4.72015806113066 116.50321883965266 1,144E+04 +2D_widefield_astig-1.tif 3 257 89 0 0.8522766100250591 123.94353553130554 1,033E+04 Ok +2D_widefield_astig-1.tif 44 163 326 0 2.1181977831426106 131.73458164050928 9,948E+03 +2D_widefield_astig-1.tif 12 263 8 0 0.9454884362991656 200.48940121612415 8,717E+03 Ok +2D_widefield_astig-1.tif 18 256 210 0 1.1327329132058517 59.22837157984339 1,164E+04 Ok +2D_widefield_astig-1.tif 28 283 272 0 1.3065621529883933 112.69871339105873 8,977E+03 Ok +2D_widefield_astig-1.tif 45 251 331 0 2.1194805664581393 144.10065926289164 8,908E+03 +2D_widefield_astig-1.tif 54 257 271 0 2.8405014851743613 93.08598175880189 2,937E+04 +2D_widefield_astig-1.tif 20 145 238 0 1.151344362258985 65.80273550544841 2,230E+04 Ok +2D_widefield_astig-1.tif 30 276 307 0 1.3200147695348847 133.2216198670471 1,042E+04 Ok +2D_widefield_astig-1.tif 58 165 197 0 4.009037772596921 31.016124838541646 2,136E+04 +2D_widefield_astig-1.tif 33 298 241 0 1.3808148976687682 108.85311203635843 1,323E+04 Ok +2D_widefield_astig-1.tif 31 276 210 0 1.3501578905120262 78.91767862779544 1,517E+04 Ok +2D_widefield_astig-1.tif 25 352 250 0 1.2414301893684534 162.22515218054195 2,044E+04 Ok +2D_widefield_astig-1.tif 32 162 356 0 1.3788240341940585 162.04937519163718 1,149E+04 Ok +2D_widefield_astig-1.tif 8 146 124 0 0.9082565892758712 90.44335243676011 8,439E+03 Ok +2D_widefield_astig-1.tif 4 170 121 0 0.8702071389742275 82.87339742040264 4,710E+04 Ok +2D_widefield_astig-1.tif 5 210 191 0 0.8757715793006416 13.892443989449804 4,780E+04 Ok +2D_widefield_astig-1.tif 26 252 240 0 1.2811400940397435 68.41052550594829 2,894E+04 Ok +2D_widefield_astig-1.tif 6 349 119 0 0.8867731113538374 170.88300090997933 1,735E+04 Ok +2D_widefield_astig-1.tif 23 263 160 0 1.1942832656560562 75.29276193632427 2,333E+04 Ok +2D_widefield_astig-1.tif 19 197 96 0 1.1511892666591277 102.00490184299969 1,702E+04 Ok diff --git a/src/bilib/src/.DS_Store b/src/bilib/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..6b9fe70d098578936fb60f7f68699ac925a87938 GIT binary patch literal 10244 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 UOj1gEwEsWa|0iw;dua9l0a@fT#sB~S literal 0 HcmV?d00001 diff --git a/src/bilib/src/additionaluserinterface.zip b/src/bilib/src/additionaluserinterface.zip new file mode 100644 index 0000000000000000000000000000000000000000..c0afaeb8e9bb683213def10a9810079f6f03a981 GIT binary patch literal 15430 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 literal 0 HcmV?d00001 diff --git a/src/bilib/src/additionaluserinterface/Chrono.java b/src/bilib/src/additionaluserinterface/Chrono.java new file mode 100644 index 0000000..178ea58 --- /dev/null +++ b/src/bilib/src/additionaluserinterface/Chrono.java @@ -0,0 +1,54 @@ +package additionaluserinterface; + +import java.text.DecimalFormat; + +/** + * This class provides static methods to measures the elapsed time. It is a + * equivalent to the function tic and toc of Matlab. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class Chrono { + + static private double chrono = 0; + + /** + * Register the current time. + */ + public static void tic() { + chrono = System.currentTimeMillis(); + } + + /** + * Returns a string that indicates the elapsed time since the last tic() + * call. + */ + public static String toc() { + return toc(""); + } + + /** + * Returns a string that indicates the elapsed time since the last tic() + * call. + * + * @param msg + * message to print + */ + public static String toc(String msg) { + double te = System.currentTimeMillis() - chrono; + String s = msg + " "; + DecimalFormat df = new DecimalFormat("####.##"); + if (te < 3000.0) + return s + df.format(te) + " ms"; + te /= 1000; + if (te < 600.1) + return s + df.format(te) + " s"; + te /= 60; + if (te < 240.1) + return s + df.format(te) + " min."; + te /= 24; + return s + df.format(te) + " h."; + } + +} diff --git a/src/bilib/src/additionaluserinterface/GridPanel.java b/src/bilib/src/additionaluserinterface/GridPanel.java new file mode 100644 index 0000000..0fa3fe0 --- /dev/null +++ b/src/bilib/src/additionaluserinterface/GridPanel.java @@ -0,0 +1,128 @@ +package additionaluserinterface; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JPanel; + +/** + * This class extends the JPanel to create grid panel given the possibility to + * place Java compoments in an organized manner in the dialog box. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class GridPanel extends JPanel { + + private GridBagLayout layout = new GridBagLayout(); + private GridBagConstraints constraint = new GridBagConstraints(); + private int defaultSpace = 3; + + /** + * Constructor. + */ + public GridPanel() { + super(); + setLayout(layout); + setBorder(BorderFactory.createEtchedBorder()); + } + + /** + * Constructor. + */ + public GridPanel(int defaultSpace) { + super(); + setLayout(layout); + this.defaultSpace = defaultSpace; + setBorder(BorderFactory.createEtchedBorder()); + } + + /** + * Constructor. + */ + public GridPanel(boolean border) { + super(); + setLayout(layout); + if (border) { + setBorder(BorderFactory.createEtchedBorder()); + } + } + + /** + * Constructor. + */ + public GridPanel(String title) { + super(); + setLayout(layout); + setBorder(BorderFactory.createTitledBorder(title)); + } + + /** + * Constructor. + */ + public GridPanel(boolean border, int defaultSpace) { + super(); + setLayout(layout); + this.defaultSpace = defaultSpace; + if (border) { + setBorder(BorderFactory.createEtchedBorder()); + } + } + + /** + * Constructor. + */ + public GridPanel(String title, int defaultSpace) { + super(); + setLayout(layout); + this.defaultSpace = defaultSpace; + setBorder(BorderFactory.createTitledBorder(title)); + } + + /** + * Specify the defaultSpace. + */ + public void setSpace(int defaultSpace) { + this.defaultSpace = defaultSpace; + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, JComponent comp) { + place(row, col, 1, 1, defaultSpace, comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int space, JComponent comp) { + place(row, col, 1, 1, space, comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int width, int height, JComponent comp) { + place(row, col, width, height, defaultSpace, comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int width, int height, int space, JComponent comp) { + constraint.gridx = col; + constraint.gridy = row; + constraint.gridwidth = width; + constraint.gridheight = height; + constraint.anchor = GridBagConstraints.NORTHWEST; + constraint.insets = new Insets(space, space, space, space); + constraint.fill = GridBagConstraints.HORIZONTAL; + layout.setConstraints(comp, constraint); + add(comp); + } + +} diff --git a/src/bilib/src/additionaluserinterface/GridToolbar.java b/src/bilib/src/additionaluserinterface/GridToolbar.java new file mode 100644 index 0000000..c2066f2 --- /dev/null +++ b/src/bilib/src/additionaluserinterface/GridToolbar.java @@ -0,0 +1,191 @@ +package additionaluserinterface; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JToolBar; + +/** + * This class extends the JToolbar to create grid panel given the possibility to + * place Java compoments in an organized manner in the dialog box. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class GridToolbar extends JToolBar { + + private GridBagLayout layout = new GridBagLayout(); + private GridBagConstraints constraint = new GridBagConstraints(); + private int defaultSpace = 3; + + /** + * Constructor. + */ + public GridToolbar() { + super("Control"); + setLayout(layout); + setBorder(BorderFactory.createEtchedBorder()); + setFloatable(false); + } + + /** + * Constructor. + */ + public GridToolbar(boolean border) { + super("Control"); + setLayout(layout); + if (border) { + setBorder(BorderFactory.createEtchedBorder()); + } + setFloatable(false); + } + + /** + * Constructor. + */ + public GridToolbar(String title) { + super(title); + setLayout(layout); + setBorder(BorderFactory.createTitledBorder(title)); + setFloatable(false); + } + + /** + * Constructor. + */ + public GridToolbar(int defaultSpace) { + super("Control"); + setLayout(layout); + this.defaultSpace = defaultSpace; + setBorder(BorderFactory.createEtchedBorder()); + setFloatable(false); + } + + /** + * Constructor. + */ + public GridToolbar(boolean border, int defaultSpace) { + super("Control"); + setLayout(layout); + this.defaultSpace = defaultSpace; + if (border) { + setBorder(BorderFactory.createEtchedBorder()); + } + setFloatable(false); + } + + /** + * Constructor. + */ + public GridToolbar(boolean border, int defaultSpace, boolean floatable) { + super("Control"); + setLayout(layout); + this.defaultSpace = defaultSpace; + if (border) { + setBorder(BorderFactory.createEtchedBorder()); + } + setFloatable(floatable); + } + + /** + * Constructor. + */ + public GridToolbar(boolean border, boolean floatable) { + super("Control"); + setLayout(layout); + if (border) { + setBorder(BorderFactory.createEtchedBorder()); + } + setFloatable(floatable); + } + + /** + * Constructor. + */ + public GridToolbar(String title, boolean floatable) { + super(title); + setLayout(layout); + setBorder(BorderFactory.createTitledBorder(title)); + setFloatable(floatable); + } + + /** + * Constructor. + */ + public GridToolbar(int defaultSpace, boolean floatable) { + super("Control"); + setLayout(layout); + this.defaultSpace = defaultSpace; + setBorder(BorderFactory.createEtchedBorder()); + setFloatable(floatable); + } + + /** + * Constructor. + */ + public GridToolbar(String title, int defaultSpace) { + super(title); + setLayout(layout); + this.defaultSpace = defaultSpace; + setBorder(BorderFactory.createTitledBorder(title)); + setFloatable(false); + } + + /** + * Constructor. + */ + public GridToolbar(String title, int defaultSpace, boolean floatable) { + super(title); + setLayout(layout); + this.defaultSpace = defaultSpace; + setBorder(BorderFactory.createTitledBorder(title)); + setFloatable(floatable); + } + + /** + * Specify the defaultSpace. + */ + public void setSpace(int defaultSpace) { + this.defaultSpace = defaultSpace; + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, JComponent comp) { + place(row, col, 1, 1, defaultSpace, comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int space, JComponent comp) { + place(row, col, 1, 1, space, comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int width, int height, JComponent comp) { + place(row, col, width, height, defaultSpace, comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int width, int height, int space, JComponent comp) { + constraint.gridx = col; + constraint.gridy = row; + constraint.gridwidth = width; + constraint.gridheight = height; + constraint.anchor = GridBagConstraints.NORTHWEST; + constraint.insets = new Insets(space, space, space, space); + constraint.fill = GridBagConstraints.HORIZONTAL; + layout.setConstraints(comp, constraint); + add(comp); + } + +} diff --git a/src/bilib/src/additionaluserinterface/NumericTable.java b/src/bilib/src/additionaluserinterface/NumericTable.java new file mode 100644 index 0000000..1c8a3b9 --- /dev/null +++ b/src/bilib/src/additionaluserinterface/NumericTable.java @@ -0,0 +1,82 @@ +package additionaluserinterface; + +import java.awt.Dimension; +import java.awt.Point; +import java.text.DecimalFormat; + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ScrollPaneConstants; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableColumn; + +/** + * This class extends JFrame and draw a simple table. All values are in 2D + * double arrays + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ + +public class NumericTable extends JFrame { + + private JTable table; + private DefaultTableModel model; + + public NumericTable(String title, String[] headings, Dimension dim) { + super(title); + setMinimumSize(dim); + setSize(dim); + setPreferredSize(dim); + + JScrollPane pane = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + model = new DefaultTableModel(); + table = new JTable(model); + for (int i = 0; i < headings.length; i++) { + model.addColumn(headings[i]); + } + + table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + pane.getViewport().add(table, null); + add(pane); + } + + public void setData(double data[][]) { + int nrow = data.length; + int ncol = data[0].length; + String s[] = new String[ncol]; + for (int r = 0; r < nrow; r++) { + for (int c = 0; c < ncol; c++) + s[c] = "" + data[r][c]; + model.addRow(s); + } + + } + + public void setData(double data[][], String[] formats) { + int nrow = data.length; + int ncol = data[0].length; + String s[] = new String[ncol]; + for (int r = 0; r < nrow; r++) { + for (int c = 0; c < ncol; c++) + s[c] = (new DecimalFormat(formats[c])).format(data[r][c]); + model.addRow(s); + } + + } + + public void setColumnSize(int width[]) { + for (int i = 0; i < width.length; i++) { + TableColumn column = table.getColumnModel().getColumn(i); + column.setPreferredWidth(width[i]); + } + } + + public void show(int posx, int posy) { + pack(); + setLocation(new Point(posx, posy)); + setVisible(true); + } + +} diff --git a/src/bilib/src/additionaluserinterface/Settings.java b/src/bilib/src/additionaluserinterface/Settings.java new file mode 100644 index 0000000..7b694fe --- /dev/null +++ b/src/bilib/src/additionaluserinterface/Settings.java @@ -0,0 +1,479 @@ +package additionaluserinterface; + +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.Properties; +import java.util.Vector; + +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JSlider; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.Timer; + +/** + * This class allows to store and load key-associated values in a text file. The + * class has methods to load and store single value linked to a string key + * describing the value. Futhermore, this class has methods to record a GUI + * component to a specified key. By this way this class allows to load and store + * all recorded items. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class Settings { + + private String filename; + private String project; + private Vector<Item> items; + private Properties props; + + /** + * Constructors a Settings abject for a given project name and a given + * filename. + * + * @param project + * a string describing the project + * @param filename + * a string give the full name of the file, including the path + */ + public Settings(String project, String filename) { + this.filename = filename; + this.project = project; + items = new Vector<Item>(); + props = new Properties(); + } + + /** + * Records a JTextField component to store/load automatically. + * + * @param key + * a string describing the value + * @param component + * the component to record + * @param defaultValue + * the default value + */ + public void record(String key, JTextField component, String defaultValue) { + Item item = new Item(key, component, defaultValue); + items.add(item); + } + + /** + * Records a JComboBox component to store/load automatically. + * + * @param key + * a string describing the value + * @param component + * the component to record + * @param defaultValue + * the default value + */ + public void record(String key, JComboBox component, String defaultValue) { + Item item = new Item(key, component, defaultValue); + items.add(item); + } + + /** + * Records a JSpinner component to store/load automatically. + * + * @param key + * a string describing the value + * @param component + * the component to record + * @param defaultValue + * the default value + */ + public void record(String key, JSpinner component, String defaultValue) { + Item item = new Item(key, component, defaultValue); + items.add(item); + } + + /** + * Records a JToggleButton component to store/load automatically. + * + * @param key + * a string describing the value + * @param component + * the component to record + * @param defaultValue + * the default value + */ + public void record(String key, JToggleButton component, boolean defaultValue) { + Item item = new Item(key, component, (defaultValue ? "on" : "off")); + items.add(item); + } + + /** + * Records a JCheckBox component to store/load automatically. + * + * @param key + * a string describing the value + * @param component + * the component to record + * @param defaultValue + * the default value + */ + public void record(String key, JCheckBox component, boolean defaultValue) { + Item item = new Item(key, component, (defaultValue ? "on" : "off")); + items.add(item); + } + + /** + * Records a JSlider component to store/load automatically. + * + * @param key + * a int value + * @param component + * the component to record + * @param defaultValue + * the default value + */ + public void record(String key, JSlider component, String defaultValue) { + Item item = new Item(key, component, defaultValue); + items.add(item); + } + + /** + * Load an individual double value given a specified key + * + * @param key + * a string describing the value + * @param defaultValue + * the default value + * @return the value get from the file + */ + public String loadValue(String key, String defaultValue) { + String s = ""; + try { + FileInputStream in = new FileInputStream(filename); + props.load(in); + s = props.getProperty(key, "" + defaultValue); + } + catch (Exception e) { + s = defaultValue; + } + return s; + } + + /** + * Load an individual double value given a specified key + * + * @param key + * a string describing the value + * @param defaultValue + * the default value + * @return the value get from the file + */ + public double loadValue(String key, double defaultValue) { + double d = 0; + try { + FileInputStream in = new FileInputStream(filename); + props.load(in); + String value = props.getProperty(key, "" + defaultValue); + d = (new Double(value)).doubleValue(); + } + catch (Exception e) { + d = defaultValue; + } + return d; + } + + /** + * Load an individual integer value given a specified key + * + * @param key + * a string describing the value + * @param defaultValue + * the default value + * @return the value get from the file + */ + public int loadValue(String key, int defaultValue) { + int i = 0; + try { + FileInputStream in = new FileInputStream(filename); + props.load(in); + String value = props.getProperty(key, "" + defaultValue); + i = (new Integer(value)).intValue(); + } + catch (Exception e) { + i = defaultValue; + } + return i; + } + + /** + * Load an individual boolean value given a specified key + * + * @param key + * a string describing the value + * @param defaultValue + * the default value + * @return the value get from the file + */ + public boolean loadValue(String key, boolean defaultValue) { + boolean b = false; + try { + FileInputStream in = new FileInputStream(filename); + props.load(in); + String value = props.getProperty(key, "" + defaultValue); + b = (new Boolean(value)).booleanValue(); + } + catch (Exception e) { + b = defaultValue; + } + return b; + } + + /** + * Store an individual double value given a specified key + * + * @param key + * a string describing the value + * @param value + * the value to store + */ + public void storeValue(String key, String value) { + props.setProperty(key, value); + try { + FileOutputStream out = new FileOutputStream(filename); + props.store(out, project); + } + catch (Exception e) { + new Msg(project, "Impossible to store settings in (" + filename + ")"); + } + } + + /** + * Store an individual double value given a specified key + * + * @param key + * a string describing the value + * @param value + * the value to store + */ + public void storeValue(String key, double value) { + props.setProperty(key, "" + value); + try { + FileOutputStream out = new FileOutputStream(filename); + props.store(out, project); + } + catch (Exception e) { + new Msg(project, "Impossible to store settings in (" + filename + ")"); + } + } + + /** + * Store an individual integer value given a specified key + * + * @param key + * a string describing the value + * @param value + * the value to store + */ + public void storeValue(String key, int value) { + props.setProperty(key, "" + value); + try { + FileOutputStream out = new FileOutputStream(filename); + props.store(out, project); + } + catch (Exception e) { + new Msg(project, "Impossible to store settings in (" + filename + ")"); + } + } + + /** + * Store an individual boolean value given a specified key + * + * @param key + * a string describing the value + * @param value + * the value to store + */ + public void storeValue(String key, boolean value) { + props.setProperty(key, "" + value); + try { + FileOutputStream out = new FileOutputStream(filename); + props.store(out, project); + } + catch (Exception e) { + new Msg(project, "Impossible to store settings in (" + filename + ")"); + } + } + + /** + * Load all recorded values. + */ + public void loadRecordedItems() { + loadRecordedItems(filename); + } + + /** + * Load all recorded values from a specified filename. + */ + public void loadRecordedItems(String fname) { + try { + FileInputStream in = new FileInputStream(fname); + props.load(in); + } + catch (Exception e) { + new Msg(project, "Loading default value. No settings file (" + fname + ")"); + return; + } + + for (int i = 0; i < items.size(); i++) { + Item item = (Item) items.get(i); + String value = props.getProperty(item.key, item.defaultValue); + if (item.component instanceof JTextField) { + ((JTextField) item.component).setText(value); + } + else if (item.component instanceof JComboBox) { + ((JComboBox) item.component).setSelectedItem(value); + } + else if (item.component instanceof JCheckBox) { + ((JCheckBox) item.component).setSelected(value.equals("on") ? true : false); + } + else if (item.component instanceof JToggleButton) { + ((JToggleButton) item.component).setSelected(value.equals("on") ? true : false); + } + else if (item.component instanceof SpinnerInteger) { + ((SpinnerInteger) item.component).set(Math.round((new Double(value)).intValue())); + } + else if (item.component instanceof SpinnerDouble) { + ((SpinnerDouble) item.component).set((new Double(value)).doubleValue()); + } + else if (item.component instanceof SpinnerFloat) { + ((SpinnerFloat) item.component).set((new Float(value)).floatValue()); + } + else if (item.component instanceof JSlider) { + ((JSlider) item.component).setValue((new Integer(value)).intValue()); + } + } + } + + /** + * Store all recorded values. + */ + public void storeRecordedItems() { + storeRecordedItems(filename); + } + + /** + * Store all recorded values into a specified filename + */ + public void storeRecordedItems(String fname) { + + for (int i = 0; i < items.size(); i++) { + Item item = (Item) items.get(i); + if (item.component instanceof JTextField) { + String value = ((JTextField) item.component).getText(); + props.setProperty(item.key, value); + } + else if (item.component instanceof JComboBox) { + String value = (String) ((JComboBox) item.component).getSelectedItem(); + props.setProperty(item.key, value); + } + else if (item.component instanceof JCheckBox) { + String value = (((JCheckBox) item.component).isSelected() ? "on" : "off"); + props.setProperty(item.key, value); + } + else if (item.component instanceof JToggleButton) { + String value = (((JToggleButton) item.component).isSelected() ? "on" : "off"); + props.setProperty(item.key, value); + } + else if (item.component instanceof JSpinner) { + String value = "" + ((JSpinner) item.component).getValue(); + props.setProperty(item.key, value); + } + else if (item.component instanceof JSlider) { + String value = "" + ((JSlider) item.component).getValue(); + props.setProperty(item.key, value); + } + } + + try { + FileOutputStream out = new FileOutputStream(fname); + props.store(out, project); + } + catch (Exception e) { + new Msg(project, "Impossible to store settings in (" + fname + ")"); + + } + } + + /** + * Private class to store one component and its key. + */ + private class Item { + public Object component; + public String defaultValue; + public String key; + + public Item(String key, Object component, String defaultValue) { + this.component = component; + this.defaultValue = defaultValue; + this.key = key; + } + } + + /** + * Private class to display an alert message when the file is not found. + */ + private class Msg extends JFrame { + + public Msg(String project, String msg) { + super(project); + GridBagLayout layout = new GridBagLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + Container contentPane = getContentPane(); + contentPane.setLayout(layout); + constraints.weightx = 0.0; + constraints.weighty = 1.0; + constraints.gridx = 0; + constraints.gridy = 0; + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.insets = new Insets(10, 10, 10, 10); + constraints.anchor = GridBagConstraints.CENTER; + JLabel newLabel = new JLabel(msg); + layout.setConstraints(newLabel, constraints); + contentPane.add(newLabel); + setResizable(false); + pack(); + setVisible(true); + Dimension dim = getToolkit().getScreenSize(); + Rectangle abounds = getBounds(); + setLocation((dim.width - abounds.width) / 2, (dim.height - abounds.height) / 2); + Timer timer = new Timer(1000, new DelayListener(this)); + timer.start(); + } + } + + /** + * Private class to dispose the message after 1 second. + */ + private class DelayListener implements ActionListener { + private Msg msg; + + public DelayListener(Msg msg) { + this.msg = msg; + } + + public void actionPerformed(ActionEvent evt) { + msg.dispose(); + } + } + +} diff --git a/src/bilib/src/additionaluserinterface/SpinnerDouble.java b/src/bilib/src/additionaluserinterface/SpinnerDouble.java new file mode 100644 index 0000000..94a5909 --- /dev/null +++ b/src/bilib/src/additionaluserinterface/SpinnerDouble.java @@ -0,0 +1,105 @@ +package additionaluserinterface; + +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; + +/** + * This class extends the generic JSpinner of Java for a specific JSpinner for + * double. It handles double type. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class SpinnerDouble extends JSpinner { + + private SpinnerNumberModel model; + + private double defValue; + private double minValue; + private double maxValue; + private double incValue; + + /** + * Constructor. + */ + public SpinnerDouble(double defValue, double minValue, double maxValue, double incValue) { + super(); + this.defValue = defValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.incValue = incValue; + + Double def = new Double(defValue); + Double min = new Double(minValue); + Double max = new Double(maxValue); + Double inc = new Double(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Set the minimal and the maximal limit. + */ + public void setLimit(double minValue, double maxValue) { + this.minValue = minValue; + this.maxValue = maxValue; + double value = get(); + Double min = new Double(minValue); + Double max = new Double(maxValue); + Double inc = new Double(incValue); + defValue = (value > maxValue ? maxValue : (value < minValue ? minValue : value)); + Double def = new Double(defValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Set the incremental step. + */ + public void setIncrement(double incValue) { + this.incValue = incValue; + Double def = (Double) getModel().getValue(); + Double min = new Double(minValue); + Double max = new Double(maxValue); + Double inc = new Double(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Returns the incremental step. + */ + public double getIncrement() { + return incValue; + } + + /** + * Set the value in the JSpinner with clipping in the range [min..max]. + */ + public void set(double value) { + value = (value > maxValue ? maxValue : (value < minValue ? minValue : value)); + model.setValue(value); + } + + /** + * Return the value with clipping the value in the range [min..max]. + */ + public double get() { + if (model.getValue() instanceof Integer) { + Integer i = (Integer) model.getValue(); + double ii = i.intValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + else if (model.getValue() instanceof Double) { + Double i = (Double) model.getValue(); + double ii = i.doubleValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + else if (model.getValue() instanceof Float) { + Float i = (Float) model.getValue(); + double ii = i.floatValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + return 0.0; + } +} diff --git a/src/bilib/src/additionaluserinterface/SpinnerFloat.java b/src/bilib/src/additionaluserinterface/SpinnerFloat.java new file mode 100644 index 0000000..1796e73 --- /dev/null +++ b/src/bilib/src/additionaluserinterface/SpinnerFloat.java @@ -0,0 +1,106 @@ +package additionaluserinterface; + +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; + +/** + * This class extends the generic JSpinner of Java for a specific JSpinner for + * float. It handles float type. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class SpinnerFloat extends JSpinner { + + private SpinnerNumberModel model; + + private float defValue; + private float minValue; + private float maxValue; + private float incValue; + + /** + * Constructor. + */ + public SpinnerFloat(float defValue, float minValue, float maxValue, float incValue) { + super(); + this.defValue = defValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.incValue = incValue; + + Float def = new Float(defValue); + Float min = new Float(minValue); + Float max = new Float(maxValue); + Float inc = new Float(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Set the minimal and the maximal limit. + */ + public void setLimit(float minValue, float maxValue) { + this.minValue = minValue; + this.maxValue = maxValue; + float value = get(); + Float min = new Float(minValue); + Float max = new Float(maxValue); + Float inc = new Float(incValue); + defValue = (value > maxValue ? maxValue : (value < minValue ? minValue : value)); + Float def = new Float(defValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Set the incremental step. + */ + public void setIncrement(float incValue) { + this.incValue = incValue; + Float def = (Float) getModel().getValue(); + Float min = new Float(minValue); + Float max = new Float(maxValue); + Float inc = new Float(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Returns the incremental step. + */ + public float getIncrement() { + return incValue; + } + + /** + * Set the value in the JSpinner with clipping in the range [min..max]. + */ + public void set(float value) { + value = (value > maxValue ? maxValue : (value < minValue ? minValue : value)); + model.setValue(value); + } + + /** + * Return the value without clipping the value in the range [min..max]. + */ + public float get() { + if (model.getValue() instanceof Integer) { + Integer i = (Integer) model.getValue(); + float ii = (float) i.intValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + else if (model.getValue() instanceof Double) { + Double i = (Double) model.getValue(); + float ii = (float) i.doubleValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + else if (model.getValue() instanceof Float) { + Float i = (Float) model.getValue(); + float ii = i.floatValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + return 0f; + } + +} diff --git a/src/bilib/src/additionaluserinterface/SpinnerInteger.java b/src/bilib/src/additionaluserinterface/SpinnerInteger.java new file mode 100644 index 0000000..b4c7cb9 --- /dev/null +++ b/src/bilib/src/additionaluserinterface/SpinnerInteger.java @@ -0,0 +1,104 @@ +package additionaluserinterface; + +import javax.swing.*; + +/** + * This class extends the generic JSpinner of Java for a specific JSpinner for + * integer. It handles int type. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class SpinnerInteger extends JSpinner { + + private SpinnerNumberModel model; + + private int defValue; + private int minValue; + private int maxValue; + private int incValue; + + /** + * Constructor. + */ + public SpinnerInteger(int defValue, int minValue, int maxValue, int incValue) { + super(); + this.defValue = defValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.incValue = incValue; + + Integer def = new Integer(defValue); + Integer min = new Integer(minValue); + Integer max = new Integer(maxValue); + Integer inc = new Integer(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Set the minimal and the maximal limit. + */ + public void setLimit(int minValue, int maxValue) { + this.minValue = minValue; + this.maxValue = maxValue; + int value = get(); + Integer min = new Integer(minValue); + Integer max = new Integer(maxValue); + Integer inc = new Integer(incValue); + defValue = (value > maxValue ? maxValue : (value < minValue ? minValue : value)); + Integer def = new Integer(defValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Set the incremental step. + */ + public void setIncrement(int incValue) { + this.incValue = incValue; + Integer def = (Integer) getModel().getValue(); + Integer min = new Integer(minValue); + Integer max = new Integer(maxValue); + Integer inc = new Integer(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Returns the incremental step. + */ + public int getIncrement() { + return incValue; + } + + /** + * Set the value in the JSpinner with clipping in the range [min..max]. + */ + public void set(int value) { + value = (value > maxValue ? maxValue : (value < minValue ? minValue : value)); + model.setValue(value); + } + + /** + * Return the value without clipping the value in the range [min..max]. + */ + public int get() { + if (model.getValue() instanceof Integer) { + Integer i = (Integer) model.getValue(); + int ii = i.intValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + else if (model.getValue() instanceof Double) { + Double i = (Double) model.getValue(); + int ii = (int) i.doubleValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + else if (model.getValue() instanceof Float) { + Float i = (Float) model.getValue(); + int ii = (int) i.floatValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + return 0; + } +} diff --git a/src/bilib/src/additionaluserinterface/WalkBar.java b/src/bilib/src/additionaluserinterface/WalkBar.java new file mode 100644 index 0000000..bd9496d --- /dev/null +++ b/src/bilib/src/additionaluserinterface/WalkBar.java @@ -0,0 +1,356 @@ +package additionaluserinterface; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JToolBar; +import javax.swing.SwingUtilities; +import javax.swing.text.DefaultCaret; + +/** + * This class extends the JToolbar of Java to create a status bar including some + * of the following component ProgressBar, Help Button About Button and Close + * Button + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class WalkBar extends JToolBar implements ActionListener { + + private JProgressBar progress = new JProgressBar(); + private JButton bnHelp = new JButton("Help"); + private JButton bnAbout = new JButton("About"); + private JButton bnClose = new JButton("Close"); + private String about[] = { "About", "Version", "Description", "Author", "Biomedical Image Group", "2008", "http://bigwww.epfl.ch" }; + private String help; + private double chrono; + private int xSizeAbout = 400; + private int ySizeAbout = 400; + private int xSizeHelp = 400; + private int ySizeHelp = 400; + + /** + * Class SetValue in the swing thread. + */ + private static class SetValue implements Runnable { + private int value; + private JProgressBar progress; + + public SetValue(JProgressBar progress, int value) { + this.progress = progress; + this.value = value; + } + + public void run() { + progress.setValue(value); + } + } + + /** + * Class IncValue in the swing thread. + */ + private static class IncValue implements Runnable { + private double inc; + private JProgressBar progress; + + public IncValue(JProgressBar progress, double inc) { + this.progress = progress; + this.inc = inc; + } + + public void run() { + progress.setValue((int) Math.round(progress.getValue() + inc)); + } + } + + /** + * Class SetMessage in the swing thread. + */ + private static class SetMessage implements Runnable { + private String msg; + private JProgressBar progress; + + public SetMessage(JProgressBar progress, String msg) { + this.progress = progress; + this.msg = msg; + } + + public void run() { + progress.setString(msg); + } + } + + /** + * Constructor. + */ + public WalkBar() { + super("Walk Bar"); + build("", false, false, false, 100); + } + + public WalkBar(String initialMessage, boolean isAbout, boolean isHelp, boolean isClose) { + super("Walk Bar"); + build(initialMessage, isAbout, isHelp, isClose, 100); + } + + public WalkBar(String initialMessage, boolean isAbout, boolean isHelp, boolean isClose, int size) { + super("Walk Bar"); + build(initialMessage, isAbout, isHelp, isClose, size); + + } + + private void build(String initialMessage, boolean isAbout, boolean isHelp, boolean isClose, int size) { + if (isAbout) + add(bnAbout); + if (isHelp) + add(bnHelp); + addSeparator(); + add(progress); + addSeparator(); + if (isClose) + add(bnClose); + + progress.setStringPainted(true); + progress.setString(initialMessage); + // progress.setFont(new Font("Arial", Font.PLAIN, 20)); + progress.setMinimum(0); + progress.setMaximum(100); + progress.setPreferredSize(new Dimension(size, 20)); + bnAbout.addActionListener(this); + bnHelp.addActionListener(this); + + setFloatable(false); + setRollover(true); + setBorderPainted(false); + chrono = System.currentTimeMillis(); + } + + /** + * Implements the actionPerformed for the ActionListener. + */ + public synchronized void actionPerformed(ActionEvent e) { + if (e.getSource() == bnHelp) { + showHelp(); + } + else if (e.getSource() == bnAbout) { + showAbout(); + } + else if (e.getSource() == bnClose) { + } + } + + /** + * Return a reference to the Close button. + */ + public JButton getButtonClose() { + return bnClose; + } + + /** + * Set a value and a message in the progress bar. + */ + public void progress(String msg, int value) { + double elapsedTime = System.currentTimeMillis() - chrono; + String t = " [" + (elapsedTime > 3000 ? Math.round(elapsedTime / 10) / 100.0 + "s." : elapsedTime + "ms") + "]"; + SwingUtilities.invokeLater(new SetValue(progress, value)); + SwingUtilities.invokeLater(new SetMessage(progress, msg + t)); + } + + /** + * Set a value and a message in the progress bar. + */ + public void increment(double inc) { + SwingUtilities.invokeLater(new IncValue(progress, inc)); + } + + /** + * Set a value in the progress bar. + */ + public void setValue(int value) { + SwingUtilities.invokeLater(new SetValue(progress, value)); + } + + /** + * Set a message in the progress bar. + */ + public void setMessage(String msg) { + SwingUtilities.invokeLater(new SetMessage(progress, msg)); + } + + /** + * Set a value and a message in the progress bar. + */ + public void progress(String msg, double value) { + progress(msg, (int) Math.round(value)); + } + + /** + * Set to 0 the progress bar. + */ + public void reset() { + chrono = System.currentTimeMillis(); + progress("Start", 0); + } + + /** + * Set to 100 the progress bar. + */ + public void finish() { + progress("End", 100); + } + + /** + * Set to 100 the progress bar with an additional message. + */ + public void finish(String msg) { + progress(msg, 100); + } + + /** + * Specify the content of the About window. + */ + public void fillAbout(String name, String version, String description, String author, String organisation, String date, String info) { + this.about[0] = name; + this.about[1] = version; + this.about[2] = description; + this.about[3] = author; + this.about[4] = organisation; + this.about[5] = date; + this.about[6] = info; + } + + /** + * Specify the content of the Help window. + */ + public void fillHelp(String help) { + this.help = help; + } + + /** + * Show the content of the About window. + */ + public void showAbout() { + + final JFrame frame = new JFrame("About " + about[0]); + JEditorPane pane = new JEditorPane(); + pane.setEditable(false); + pane.setContentType("text/html; charset=ISO-8859-1"); + pane.setText("<html><head><title>" + about[0] + "</title>" + getStyle() + "</head><body>" + (about[0] == "" ? "" : "<p class=\"name\">" + about[0] + "</p>") + + // Name + (about[1] == "" ? "" : "<p class=\"vers\">" + about[1] + "</p>") + + // Version + (about[2] == "" ? "" : "<p class=\"desc\">" + about[2] + "</p><hr>") + + // Description + (about[3] == "" ? "" : "<p class=\"auth\">" + about[3] + "</p>") + + // author + (about[4] == "" ? "" : "<p class=\"orga\">" + about[4] + "</p>") + (about[5] == "" ? "" : "<p class=\"date\">" + about[5] + "</p>") + + (about[6] == "" ? "" : "<p class=\"more\">" + about[6] + "</p>") + "</html>"); + + final JButton bnClose = new JButton("Close"); + bnClose.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + frame.dispose(); + } + }); + pane.setCaret(new DefaultCaret()); + JScrollPane scrollPane = new JScrollPane(pane); + // helpScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + scrollPane.setPreferredSize(new Dimension(xSizeAbout, ySizeAbout)); + frame.getContentPane().add(scrollPane, BorderLayout.NORTH); + frame.getContentPane().add(bnClose, BorderLayout.CENTER); + + frame.pack(); + frame.setResizable(false); + frame.setVisible(true); + center(frame); + } + + /** + * Show the content of the Help window of a given size. + */ + public void showHelp() { + final JFrame frame = new JFrame("Help " + about[0]); + JEditorPane pane = new JEditorPane(); + pane.setEditable(false); + pane.setContentType("text/html; charset=ISO-8859-1"); + pane.setText("<html><head><title>" + about[0] + "</title>" + getStyle() + "</head><body>" + (about[0] == "" ? "" : "<p class=\"name\">" + about[0] + "</p>") + // Name + (about[1] == "" ? "" : "<p class=\"vers\">" + about[1] + "</p>") + // Version + (about[2] == "" ? "" : "<p class=\"desc\">" + about[2] + "</p>") + // Description + "<hr><p class=\"help\">" + help + "</p>" + "</html>"); + final JButton bnClose = new JButton("Close"); + bnClose.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + frame.dispose(); + } + }); + pane.setCaret(new DefaultCaret()); + JScrollPane scrollPane = new JScrollPane(pane); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + scrollPane.setPreferredSize(new Dimension(xSizeHelp, ySizeHelp)); + frame.setPreferredSize(new Dimension(xSizeHelp, ySizeHelp)); + frame.getContentPane().add(scrollPane, BorderLayout.CENTER); + frame.getContentPane().add(bnClose, BorderLayout.SOUTH); + frame.setVisible(true); + frame.pack(); + center(frame); + } + + /* + * Place the window in the center of the screen. + */ + private void center(Window w) { + Dimension screenSize = new Dimension(0, 0); + boolean isWin = System.getProperty("os.name").startsWith("Windows"); + if (isWin) { // GraphicsEnvironment.getConfigurations is *very* slow on + // Windows + screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + } + if (GraphicsEnvironment.isHeadless()) + screenSize = new Dimension(0, 0); + else { + // Can't use Toolkit.getScreenSize() on Linux because it returns + // size of all displays rather than just the primary display. + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] gd = ge.getScreenDevices(); + GraphicsConfiguration[] gc = gd[0].getConfigurations(); + Rectangle bounds = gc[0].getBounds(); + if (bounds.x == 0 && bounds.y == 0) + screenSize = new Dimension(bounds.width, bounds.height); + else + screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + } + Dimension window = w.getSize(); + if (window.width == 0) + return; + int left = screenSize.width / 2 - window.width / 2; + int top = (screenSize.height - window.height) / 4; + if (top < 0) + top = 0; + w.setLocation(left, top); + } + + /* + * Defines the CSS style for the help and about window. + */ + private String getStyle() { + return "<style type=text/css>" + "body {backgroud-color:#222277}" + "hr {width:80% color:#333366; padding-top:7px }" + + "p, li {margin-left:10px;margin-right:10px; color:#000000; font-size:1em; font-family:Verdana,Helvetica,Arial,Geneva,Swiss,SunSans-Regular,sans-serif}" + + "p.name {color:#ffffff; font-size:1.2em; font-weight: bold; background-color: #333366; text-align:center;}" + "p.vers {color:#333333; text-align:center;}" + + "p.desc {color:#333333; font-weight: bold; text-align:center;}" + "p.auth {color:#333333; font-style: italic; text-align:center;}" + + "p.orga {color:#333333; text-align:center;}" + "p.date {color:#333333; text-align:center;}" + "p.more {color:#333333; text-align:center;}" + + "p.help {color:#000000; text-align:left;}" + "</style>"; + } +} diff --git a/src/bilib/src/bilib.zip b/src/bilib/src/bilib.zip new file mode 100644 index 0000000000000000000000000000000000000000..2ef6e196c2d25c8eb2717582cab0ae148956c022 GIT binary patch literal 137151 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 literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/.DS_Store b/src/bilib/src/bilib/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..2dae4405c103007dd70cd4f46b3f7f99458b776a GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8z4DAe90Z1N%F(jFwBFrH3z-Ht#<S`Vd6es5-<wNBN zQ6RnS49N`n47m)s3`qL(a~K&I7?>H-7}6L@AV%irfK20I$Y&^EC}GHC$Ysc6sA5P2 zv-279kQIRxj^fb}7!85Z5Eu=C(GVC70W2W^s$fC&KQ|V+QK8Wg7!84u9Ri^8K>^aX zXK;Yh4G<b6#lXnG0PX@XGBB{fLW&XG4`2YvfwY2XkX8^4(#pUHVu8&7Yh_@BYGnj> zLqPgKT@nxt*3Q5Pwi(0+YiD2t+sweg2+_{K2(_6J+CyQ4XlG!AXlGyq+YWQxC_Nei zqagqb0Z`u?)b|I~|E>%SxcdJv<)idy2#kinFbn}k7MEZbCvc^V-FKk67F3@mAW1WV y>STx>kTj@T2Uo>RkO4&rm{LXt29Q>eJh&=mWMBZ-<f9D%SO|^Mqai^55C8z*cp?e_ literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons.zip b/src/bilib/src/bilib/commons.zip new file mode 100644 index 0000000000000000000000000000000000000000..cd3f5e1a05f420e086e25203b7b333f7fabb00a1 GIT binary patch literal 88216 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 W2AXN|XYgm3AjQD&$(E6UfdK&ArJZH~ literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons/.DS_Store b/src/bilib/src/bilib/commons/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..05370983747f7d8fa1271a67dd854d587f24703c GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8zj35RBCIAV8Fop~hR0Jf+2sR_1A&;Rrr8qe!DIY2~ z*eQ_P*%^`;N*PKRN*IvbpPvJAIx|BWLmEQ~#7i)tEQWlBBxE5LhFpe3h7yJhWI;BD zB8EhUJcbm8e1=?P2@Zy0hE%Y1nGAUh>97EUn9j;j!jQ<2#E`>~imZnf?7&Qh98}dH zCywIL5Eu=C(GVC7fzc2kEd&@L$~m|p^e8zR0;3@?8UpYT0F@64puPeF1A_yUZh+7r zDF#Lc25=XEk%55)rU}vyU;xR1w1Q}mRuB!+%D@O>fz1GGWnhGAWdwIaK>9#k5)cj6 z&cFz^8N>%`XJ7=|%)r11(ayjKwHelVVT5RBV1#IAU<BI^bKNLC8Umvs01E+T2txo= z|GP3Uz_JiTbd($ofzc2c5h1|H;u7rQ1g?~^`xjK#g6h)*s5Gb!hE&I(iW#*Lzyujk ilz=J)RresRAR1g1GcquMwT?CfU?DV0kA?vKLjVAp@;W~N literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons/buttons/ButtonFactory.java b/src/bilib/src/bilib/commons/buttons/ButtonFactory.java new file mode 100644 index 0000000..67bb752 --- /dev/null +++ b/src/bilib/src/bilib/commons/buttons/ButtonFactory.java @@ -0,0 +1,89 @@ +package bilib.commons.buttons; + +import java.awt.Dimension; +import java.net.URL; + +import javax.swing.ImageIcon; +import javax.swing.JButton; + +public class ButtonFactory { + + private static JButton icon(JButton bn) { + URL url = ButtonFactory.class.getResource(bn.getText().toLowerCase() +".png"); + if (url != null) { + ImageIcon icon = new ImageIcon(url, ""); + if (icon != null) { + JButton bni = new JButton(icon); + Dimension d = new Dimension(icon.getIconWidth(), icon.getIconHeight()+2); + bni.setMaximumSize(d); + bni.setPreferredSize(d); + return bni; + } + } + return bn; + } + + public static JButton about(boolean tryIcon) { + JButton bn = new JButton("About"); + if (tryIcon == true) + bn = icon(bn); + bn.setToolTipText("About ..."); + return bn; + } + + public static JButton close(boolean tryIcon) { + JButton bn = new JButton("Close"); + if (tryIcon == true) + bn = icon(bn); + bn.setToolTipText("Save the settings and close"); + return bn; + } + + public static JButton prefs(boolean tryIcon) { + JButton bn = new JButton("Prefs"); + if (tryIcon == true) + bn = icon(bn); + bn.setToolTipText("Manage the settings"); + return bn; + } + + public static JButton help(boolean tryIcon) { + JButton bn = new JButton("Help"); + if (tryIcon == true) + bn = icon(bn); + bn.setToolTipText("Get online help"); + return bn; + } + + public static JButton run(boolean tryIcon) { + JButton bn = new JButton("Run"); + if (tryIcon == true) + bn = icon(bn); + bn.setToolTipText("Start the processing"); + return bn; + } + + public static JButton save(boolean tryIcon) { + JButton bn = new JButton("Save"); + if (tryIcon == true) + bn = icon(bn); + bn.setToolTipText("Save"); + return bn; + } + + public static JButton snapshot(boolean tryIcon) { + JButton bn = new JButton("Snapshot"); + if (tryIcon == true) + bn = icon(bn); + bn.setToolTipText("Snapshot"); + return bn; + } + + public static JButton stop(boolean tryIcon) { + JButton bn = new JButton("Stop"); + if (tryIcon == true) + bn = icon(bn); + bn.setToolTipText("Abort the processing"); + return bn; + } +} diff --git a/src/bilib/src/bilib/commons/buttons/about.png b/src/bilib/src/bilib/commons/buttons/about.png new file mode 100644 index 0000000000000000000000000000000000000000..8b79af592e1b717d318a6d70da2a964e608a24ab GIT binary patch literal 827 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4kiW$hJ4Xg>lqlBnLJ$_Ln?0dPW9K=T_|&0 z-ps7GcWaJua`Hc6Rd&@Fmp;EK+?qQDy!>W&b8ofS;bB=&Ebw%}ou1~hD8-XmM^jDz z{Lu`m+kgGr(~mjlKUB-loA-S4_ve3qe*asi_V=&c`}gnt{r&5ODk>@}YHR;C>MY%Q z@6Mf^oSdHi{_o$uZQH)RyriV0tc;J3@7($G^78Wg_wN0x=fb6}tzB_VB_KZj{)f(8 zyLPQwwd&ZhV+$89OfdK{Ilw|jEyvZ>H8(f+u(`voO`A6D+*w&$Yr9uK>+apVPoF+r zArMnnWixNnCZi(3`G?|SV|(v>`c$+^K$Mq<XUE-lQmhG&jm^x=%+1|5e>kG_|CIVG zYinyyCvR_WRaMnT=8}5TxkVi<EG-?UteVPVl$)KMY$We={rYuXUEL**J=-q7l#!Jc zb#ixiU*h~wQ>Oj!#tj=jC~n!dEi5GDi66Vo%!_Z{=uGtX_P)x}RA^_BA+$WNqT)u{ z=A9u2CO=91+_z}Ml7bA576%0dTRXdL+qO-b7|go*s+EyZQDtRiY3bJO+uaq99zB{B zm6-VO)vHw{)z#h~Q=SAI%rQHE=1hm2V*7-<dEu8oef}&iF1|Fw<kW_Mtt%OltE!?n z=FXoV>=ZTYVnz@5-TU{AJ-Ux3g)$zya%GB+OY;?zCB?<XkDc`O^<!gW)h3@@)O0Xm z#+*4KPU-3CTbh%un)F0RM}Pb%9Jc=Y?9dY@PtKe%V?t{N`^O-Qhz;>EF-KbY`1pP- z69_iiw0ZMQ6{i<pzD)6Z{`9FPr%B-Q%U{2KEo^(_^k(|Jd2)ApPF~2`x=FzBCC5=M z&Jy<1@7}$;9&2r5qvFqHqOss&#;@PMlQ-V@{Q2|0e|3(Ij7PONB_$=-%)Mgz==<-F zD*88m`3p>D44%TVxJAX&*Vp&av1NK@>#xtAI@ML?NeSnNMZOHXbDqeGhNWtV{3^Se zmzP&myM)Cfz=Pw%yqPmQ=c=o2Hm-~OH_@Zz^*P<6!Cq{^$vU@h-~RtwpXuGZcNStM oeO0w~^4^nDEbseR>He2z6~1{hU31oB1_lNOPgg&ebxsLQ0B1UeW&i*H literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons/buttons/close.png b/src/bilib/src/bilib/commons/buttons/close.png new file mode 100644 index 0000000000000000000000000000000000000000..253ac8afeb193e2825e59d50da8a80e8d763a925 GIT binary patch literal 3381 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 WtgU<c#vBF)1_n=8KbLh*2~7YbOHH=` literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons/buttons/help.png b/src/bilib/src/bilib/commons/buttons/help.png new file mode 100644 index 0000000000000000000000000000000000000000..3549c560cdb5700c0abc0c0caedcfb2149c72554 GIT binary patch literal 3075 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 JF6*2UngDQFvk3qI literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons/buttons/prefs.png b/src/bilib/src/bilib/commons/buttons/prefs.png new file mode 100644 index 0000000000000000000000000000000000000000..611406224177d9adcce8bf124ffca437c55db12b GIT binary patch literal 3696 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 literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons/buttons/run.png b/src/bilib/src/bilib/commons/buttons/run.png new file mode 100644 index 0000000000000000000000000000000000000000..b5f15e97e4ac0a80ea5c571b7e202948dfdb0417 GIT binary patch literal 3144 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJoI14-?iy0WWg+Z8+Vb&Z8 z1_mzwOlRkSfQ<Z-{NjxK0tHWJXN7{I{ItxRR0f8MTXTccLxNw5*?zzG#mT#)VTyNt zkSK>jkW+Vq$V4?QMNR>)4#kBxMFS5Cy1KGRpIFer+}GF8BO0i~)5Udz{fXkDzUIXp zU8jCOtX`abZO`{-Xa8-zZufl6=ee8DvmfB#ndV^}XxhND(n(J6LG$q=#~wb>b7<sJ zDPR!b*qowR{8yQg;YXc~jkeMO_I(VD6_>d=7!LT(K42iz{-6Jq)65vgh8D&HB@->Y zIT}P59IE=9mN7WEF*eMdp%%=rfQ2F9baK*Th5%iL1oir7ml+!7{+L<K$Phh6QIv7P zL<R-n)D9Ph3~Pop=fk>O86pfAMAE#ESTfwuWiapzPxD~du$|$+IRWlX3>-WR27w)| znhY$}3<)Qcl`9xpW-^GleG0evsJ%|$xfTOM#mr4MJ5?-Q8>2aN3)#cN_2e|pd5UX` zm?bhL&T`N@R5?@9sL_P~!RIp!3=1ZT3O;E5{I}v9-@0?>%=)(R#p}M>&-Py_De3XQ zr{`A}I503gEUCNrPe*^VRD(5JL+-yts_$4b;<y)-e(z4(#P6_`VZqB=;hzgYVdJ`R z!p@sFpFVkV!|RAsbmFP-+JEVX;tz!X&G}hp^XL252m2nWG#bS=y0iRvaOlhzpQSSA zCvmnU-FepU{%1S)|39*qm5z8O?TpY8c^oKI*yl2<=d;mLrB0_wnnzVH{ZDyxU*|Wo z-F;Jr4Ow=K)f07B#3nO;^Hvv#C^_)xI|IX~-T4QvXmBt$WHdiKU;h8S{a^P5stgTj zo|6h07~))1^g>Q;wm;6nz~Hi=QEP*v^p}GYItSQf4zk`k$o(h5ti?&?$RQ^IC$5wR znW6;EHBC+$ZD%*gY9;7jaga4R5SY<6xqwgaQ0NXm+XDVyi5&9|YV2v`WN|#fA)Kgq ztAjDAD?+)CLwsiI55<EXEJEEX3MV^60+qZcntB)pb%iK|ddPmVEn>24k8#^I(fR~~ zQPZEcJ?@568aD;nUg6p5ymN)QREygp?h7d;hO_#`7R<g-v_;Cc$86#A3#&8uZ}r<b zA2&GgLW14+NRENtvBORqEp#|1C!gM^w1#K(F{uq<YnXR;eoa2Vv7AMy(e(hQ8B4j7 zLZY0bM}$v=-wlry3Nk7tg10!$Ihk7m52-6nUg5n(ZI|HBBN9nQhV2m^I?C6C`i1oc zsym#In5zhNIvc6)oWSI{aY@J}p-XO;@`99K>a3h?;vuCx`Q)vX>`m;JQ*;!xPqaRn z`{eKw<0sNj6hB3C%}sJBT=FCMkw(y|EHBBY0#8kzvZc;2atvKOE9mZug&~5b{ZUS9 z7j6q$AK1P^*rarp<=JL2Pw%B`7frn^l_8$-J#+nwl3jv#r^@+FzYu=u^h@^_(_b)u zDd*vB6Ky`;k**;*TY_C8U9wza{S2RFl9#(BkB1nT8@`_Ld?x?Q{Ll!kZCW*=6HjqX z^_r?SRa#3wWaX-5tM0Ah4*eW@I;4MPTyX6=w@~BY$16FnOj{kgYHq;mVCmrWmDVfT zSEaAW53yfT7x}l7E%LC>VSnc(feSXqKi+dpfje1pbA^s#Ht*`T(nixGY8%hqtSQsl zJ>#$J?rz&vD;HWi8+#dtZw~rACC#4w@r=SBb4vU;pKC0i{<*H}p2&O6N1D>hOni14 zrp9Einl^n}#A=V#fveqL?=@TZcKO_8dtG-&{%+)RJnp3)qwb#Ge~xwT^WdAocb9W5 z7xT5X`npbURrXTpg}YtK_vbBtH)GzuxQ2N?`OEKB?tT7CrdI!N$lu&w%fGtwM6hk< zQDM8yb56qMsA*xojjaCh+aCj+V-GtxFHM}9IQ?PE#GMyy7GHJib>r{JJQnj<<#Fz@ z++&N?*whr&ynSE!zMB=~yK|Z5GHV}epW|oGoLzMG+Dy~gwuaY@p3i<h`}*wr+Y_QH zqI9;c*tR3pbJLTkWs&c;?a6D6vW#3B5gHY{*=_CIwW`;4uid>|aQRNZ$$qQnT}wZ_ z;p4WG+g5Iu-0<`U+wFDRa<|ncrYDJ~Z$G;3@Vm)-C)e((?fq-Z&f9*IZ?$ZcY+dn| zA`z=Ft9!G~&3R{Ed-Th+m-^`u=LOE+Fn+vE__XGc%(=m{FT1CUr|&Fo{@l^s>D_c% zZ@Qh{Z0TcnPwai-Rr0kk_F>hfuXAT_o_*-;ireeonZ3(=*M4XI8I$>%^CFFlFC0GM z{2=h@MSJe~Qw_53uc`BQ_HRtz`dnE*S$|^q+Upy(FD+m7J@LJ`-8Z|U`H$@$+nC=o zx)&LL#{9tc6WdSwAI_f_-+JG<Uip8@|Em8N8J0JwGNv<kHaa#&GyVQiRin7S&)({G z>bFfd-#Ydz*y$+h*x$6a>FL3>iWw)~OjxV<{rQY@6VGMxwFcC^xORa(@mXT`;W{4m z*7nx$Hv7JUjtHrXhSN>UFWzx3zVGtOWtz*q+3AM<mg{xBOttJ*>1Em6GT&vg%l?>N zTI89l5pyFvbG&6N^|aQR<>l<V|L5)J_D6FcaX+pWpP_#uwxDw1xkF!Cmy4~`6VkDo z=HS!eXEEi2qkz^5u^!b=!sV)cT$P7p52Y*VUo^Yeal<)AOV00;hn8>Eq&E}ym{zJB zHD0RobZ3kD$_aaXczy29wwfn4t4tx+$kuAFVe_Pk6AGPHDf&*{mi{XJSz6iYHL0ze z{vFNh-mWh<mHlM)<_T+9BXzm9IW2n;5~Xoni+xjiYIxfIvz@0br>&l+efI5{&8Kcp zFrV{&{{Oc^hlCzZ+P1XKJ6u;b;@RFcS(ck@3M+4OJr-rvZVO{uW3!`cn^x{s|J859 zr?0Eqt5)Tk-57W9RMWw&jM=ZbL%E)|*1o=T{cWs&gns1xt%<+d=1SX%c6O~3xh+0j zwEl+1or<T@?fIwn&%85b%gWV9cBY(9yOj3w**)EP(R|ZZO@B6>FOT=;tu4Iw!?wKr z`pxyW{`9jNw|6&hbKbdlzs-(4smo8EJ8NwpvLxh7$gdEycbD#NO}zdi@8Ub@>OJ2~ z-dw#Mz32O1yOaBFhcbrxUQN52`rGsO=I{3FI#<2?G3|G5>{`Fv!nennFERgRSLWMx zedm>{vX^W_g8Hv%zjo(4F2C>J-1p_Wyk@+iy>pH|QZ8_*eDd-;=l$<;yjHi$daBnv zWWOw5CYvs+pXD^`(=4~y!LxoxCq?h_KVx_C+N*n2f81}&^UpD{iLCVc9C9-A*2|{L zlhZ$)@0mMwZf%|3&pQdX7j3J4o_y|p7JvTxT>>Q!FS-8jer~<T`drMX9VMSO-F_+@ zz9W3rdaL-{S0%4({!PB@e|-0x-Luy1jLF{lu4?br>a_1KrRSNSyZdEt<wvpq*RIa9 zw7t9cb5;KLqu;;Y7rR?mr2hBZ>D@QW)!(h%<G&~Vi^E&To6dLJui8idyL03Ho%*Z; z<qJMvyyO0zt+o05zlmS_`JGw&XZD|N<86CA&%(~q@{xVf-<N-uJD*QEuQpHo-=cq~ z*NKbi*ZsKq`ue$xGcGQi{%QK;{kb(u|Gql#T5|pEV~38(?K@dvY#8???DyM)<#)<u z-!Hz;@BgGmp<d&E#?OskgD))qJy~+{<o_(pGd}-GHrBn(z`&r8>=ES4z)+>iz|hdl z!0_`w14F}028L1t28LG&3=CE?7#PI!C&eFiV_;w`_H=O!skrqf)G#B&QDn|dZVioP zLXOeQN!4@ru1#HKvhA9gn0wF)4zZ-f<cUgxDsz`kGi!3(B6QGihnlYg)1n_~$J82w zKivPjdx=1e!CAdMH)qybpH{IrnPu_t*LTio|6(Ng+NU$zVJxsa$$VQrN0~F{f1ApL zmXm)!R@_<ZaH(sFspsl#_f;yH9z5C@84|jDRaYph=knb9UZ3nv<}##<scrasxh%)| zlAzv=x)~|r2ecUU799v}-Tz?KlDR7D8DeI4Cp@|myCd&+=_Fr<>7H`dj4HaGyJkL# zom9&3JZ<X=aTVR$_dGs{9k_L+JYn~D4Y_AGMJJgv*t}MmVB-JTcGBW&w?41rTD8jm z|9h4BllJ%*<u6+?BhZmykAkHX51ah%Wf^i!v6Eh_#BaOyt39^eHQwW1_S)Que+<iK z`T1RZ$SOCX#^;mZ$-k|ScdgpTXu+_-r)c(**h%wu*SvF`@FaMWV8iCMTXQGHi~lV6 h@&EGtu>EWdPy2%p7?p<oVPIfj@O1TaS?83{1OVCB+5!Lo literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons/buttons/save.png b/src/bilib/src/bilib/commons/buttons/save.png new file mode 100644 index 0000000000000000000000000000000000000000..3133d5a0e2654587d7151057b1355d27cc4b006e GIT binary patch literal 3269 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJoI14-?iy0WWg+Z8+Vb&Z8 z1_mzwOlRkSfQ<Z-{NjxK0tHWJXN7{I{ItxRR0f8MTXTccLxNw5*?zzG#mT#)VTyNt zkSK>jkW+Vq$V4?QMNR>)4#kBxMFS5Cy1KGRpIFer+}GF8BO0i~)5Udz{fXkDzUIXp zU8jCOtX`abZO`{-Xa8-zZufl6=ee8DvmfB#ndV^}XxhND(n(J6LG$q=#~wb>b7<sJ zDPR!b*qowR{8yQg;YXc~jkeMO_I(VD6_>d=7!LT(K42iz{-6Jq)65vgh8D&HB@->Y zIT}P59IE=9mN7WEF*eMdp%%=rfQ2F9baK*Th5%iL1oir7ml+!7{+L<K$Phh6QIv7P zL<R-n)D9Ph3~Pop=fk>O86pfAMAE#ESTfwuWiapzPxD~du$|$+IRWlX3>-WR27w)| znhY$}3<)Qcl`9xpW-^GleG0evsJ%|$xfTOM#mr4MJ5?-Q8>2aN3)#cN_2e|pd5UX` zm?bhL&T`N@R5?@9sL_P~!RIp!3=1ZT3O;E5{I}v9-@0?>%=)(R#p}M>&-Py_De3XQ zr{`A}I503gEUCNrPe*^VRD(5JL+-yts_$4b;<y)-e(z4(#P6_`VZqB=;hzgYVdJ`R z!p@sFpFVkV!|RAsbmFP-+JEVX;tz!X&G}hp^XL252m2nWG#bS=y0iRvaOlhzpQSSA zCvmnU-FepU{%1S)|39*qm5z8O?TpY8c^oKI*yl2<=d;mLrB0_wnnzVH{ZDyxU*|Wo z-F;Jr4Ow=K)f07B#3nO;^Hvv#C^_)xI|IX~-T4QvXmBt$WHdiKU;h8S{a^P5stgTj zo|6h07~))1^g>Q;wm;6nz~Hi=QEP*v^p}GYItSQf4zk`k$o(h5ti?&?$RQ^IC$5wR znW6;EHBC+$ZD%*gY9;7jaga4R5SY<6xqwgaQ0NXm+XDVyi5&9|YV2v`WN|#fA)Kgq ztAjDAD?+)CLwsiI55<EXEJEEX3MV^60+qZcntB)pb%iK|ddPmVEn>24k8#^I(fR~~ zQPZEcJ?@568aD;nUg6p5ymN)QREygp?h7d;hO_#`7R<g-v_;Cc$86#A3#&8uZ}r<b zA2&GgLW14+NRENtvBORqEp#|1C!gM^w1#K(F{uq<YnXR;eoa2Vv7AMy(e(hQ8B4j7 zLZY0bM}$v=-wlry3Nk7tg10!$Ihk7m52-6nUg5n(ZI|HBBN9nQhV2m^I?C6C`i1oc zsym#In5zhNIvc6)oWSI{aY@J}p-XO;@`99K>a3h?;vuCx`Q)vX>`m;JQ*;!xPqaRn z`{eKw<0sNj6hB3C%}sJBT=FCMkw(y|EHBBY0#8kzvZc;2atvKOE9mZug&~5b{ZUS9 z7j6q$AK1P^*rarp<=JL2Pw%B`7frn^l_8$-J#+nwl3jv#r^@+FzYu=u^h@^_(_b)u zDd*vB6Ky`;k**;*TY_C8U9wza{S2RFl9#(BkB1nT8@`_Ld?x?Q{Ll!kZCW*=6HjqX z^_r?SRa#3wWaX-5tM0Ah4*eW@I;4MPTyX6=w@~BY$16FnOj{kgYHq;mVCmrWmDVfT zSEaAW53yfT7x}l7E%LC>VSnc(feSXqKi+dpfje1pbA^s#Ht*`T(nixGY8%hqtSQsl zJ>#$J?rz&vD;HWi8+#dtZw~rACC#4w@r=SBb4vU;pKC0i{<*H}p2&O6N1D>hOni14 zrp9Einl^n}#A=V#fveqL?=@TZcKO_8dtG-&{%+)RJnp3)qwb#Ge~xwT^WdAocb9W5 z7xT5X`npbURrXTpg}YtK_vbBtH)GzuxQ2N?`OEKB?tT7CrdI!N$lu&w%fGtwM6hk< zQDM8yb56qMsA*xojjaCh+aCj+V-GtxFHM}9IQ?PE#GMyy7GHJib>r{JJQnj<<#Fz@ z++&N?*whr&ynSE!zMB=~yK|Z5GHV}epW|oGoLzMG+Dy~gwuaY@p3i<h`}*wr+Y_QH zqI9;c*tR3pbJLTkWs&c;?a6D6vW#3B5gHY{*=_CIwW`;4uid>|aQRNZ$$qQnT}wZ_ z;p4WG+g5Iu-0<`U+wFDRa<|ncrYDJ~Z$G;3@Vm)-C)e((?fq-Z&f9*IZ?$ZcY+dn| zA`z=Ft9!G~&3R{Ed-Th+m-^`u=LOE+Fn+vE__XGc%(=m{FT1CUr|&Fo{@l^s>D_c% zZ@Qh{Z0TcnPwai-Rr0kk_F>hfuXAT_o_*-;ireeonZ3(=*M4XI8I$>%^CFFlFC0GM z{2=h@MSJe~Qw_53uc`BQ_HRtz`dnE*S$|^q+Upy(FD+m7J@LJ`-8Z|U`H$@$+nC=o zx)&LL#{9tc6WdSwAI_f_-+JG<Uip8@|Em8N8J0JwGNv<kHaa#&GyVQiRin7S&)({G z>bFfd-#Ydz*y$+h*x$6a>FL3>iWw)~OjxV<{rQY@6VGMxwFcC^xORa(@mXT`;W{4m z*7nx$Hv7JUjtHrXhSN>UFWzx3zVGtOWtz*q+3AM<mg{xBOttJ*>1Em6GT&vg%l?>N zTI89l5pyFvbG&6N^|aQR<>l<V|L5)J_D6FcaX+pWpP_#uwxDw1xkF!Cmy4~`6VkDo z=HS!eXEEi2qkz^5u^!b=!sV)cT$P7p52Y*VUo^Yeal<)AOV00;hn8>Eq&E}ym{zJB zHD0RobZ3kD$_aaXczy29wwfn4t4tx+$kuAFVe_Pk6AGPHDf&*{mi{XJSz6iYHL0ze z{vFNh-mWh<mHlM)<_T+9BXzm9IW2n;5~Xoni+xjiYIxfIvz@0br>&l+efI5{&8Kcp zFrV{&{{Oc^hlCzZ+P1XKJ6u;b;@RFcS(ck@3M+4OJr-rvZVO{uW3!`cn^x{s|J859 zr?0Eqt5)Tk-57W9RMWw&jM=ZbL%E)|*1o=T{cWs&gns1xt%<+d=1SX%c6O~3xh+0j zwEl+1or<T@?fIwn&%85b%gWV9cBY(9yOj3w**)EP(R|ZZO@B6>FOT=;tu4Iw!?wKr z`pxyW{`9jNw|6&hbKbdlzs-(4smo8EJ8NwpvLxh7$gdEycbD#NO}zdi@8Ub@>OJ2~ z-dw#Mz32O1yOaBFhcbrxUQN52`rGsO=I{3FI#<2?G3|G5>{`Fv!nennFERgRSLWMx zedm>{vX^W_g8Hv%zjo(4F2C>J-1p_Wyk@+iy>pH|QZ8_*eDd-;=l$<;yjHi$daBnv zWWOw5CYvs+pXD^`(=4~y!LxoxCq?h_KVx_C+N*n2f81}&^UpD{iLCVc9C9-A*2|{L zlhZ$)@0mMwZf%|3&pQdX7j3J4o_y|p7JvTxT>>Q!FS-8jer~<T`drMX9VMSO-F_+@ zz9W3rdaL-{S0%4({!PB@e|-0x-Luy1jLF{lu4?br>a_1KrRSNSyZdEt<wvpq*RIa9 zw7t9cb5;KLqu;;Y7rR?mr2hBZ>D@QW)!(h%<G&~Vi^E&To6dLJui8idyL03Ho%*Z; z<qJMvyyO0zt+o05zlmS_`JGw&XZD|N<86CA&%(~q@{xVf-<N-uJD*QEuQpHo-=cq~ z*NKbi*ZsKq`ue$xGcGQi{%QK;{kb(u|Gql#T5|pEV~38(?K@dvY#8???DyM)<#)<u z-!Hz;@BgGmp<d&E#?OskgD))qJy~+{<o_(pGd}-GHrBn(z`&r8>=ES4z)+>iz|hdl z!0_`w14F}028L1t28LG&3=CE?7#PI!C&eFiV_;zX;OXKRQgLg`G~2!_4g#(Fv;LoL zVqtfP5?tfbcEm$K(Pe|Ug31E{nYIS)2@{H1ebu8~8qAbfGQ*9(?RquwhI00?$1`Kw z)VJn@@BR}P8eV$)+2@~37EgEl(0kduJN}tqA=@08nGaMyoIP{zso+H(j$7|$`CHug z$~vpV-gQpRlQ+R3GmXK&QD0N(#j&7EuRDI2Y{>1C_I!ST+w-|#;pLm_8_f@Bd(KyO zn)}yNPH^ugsT~aW{8qB-ZPHnN_iy@l9)YxN2Ae}&YFO?u_cz?WbGJI(TEUj>jMAcM zCel9`LejQP3SoHFdzhoIRZ;UW2VY}@n^4*{o~iGg^fH4EvUz!?Pg-~8&6P8!YVD#m zCr$Acuwf9pTkZV&@K&qMo=uV;O8rG5&Gi?Yf3N?bs-<GK!qP1-Lhp$tvu^fO@Jxv; zTi@R=)Ds%$5+HJPF2}8HsY?=${XTGaqD$noB~!j><<Dx{mb+c7BAq48?Xt-Z>)%p_ zOIvm)r#;q`;`SDr6rneLORn`@k4nGL(8aIRw|Ch!)oK=%zuL7-=XA4l?%Y|itIRIn zT<<(Bb#kwoXQ@k1*kQ#~t<EVyxoL}czwBJ{e3IV#-MwB)O7mvk{Kc_tkwzVJKYRPR zSMu9mO9qsFum98`vChwQh7H@PJ9q2<wBMg57hccK@NHMb@#6{WK%Eo@Pgg&ebxsLQ E07g9!asU7T literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons/buttons/snapshot.png b/src/bilib/src/bilib/commons/buttons/snapshot.png new file mode 100644 index 0000000000000000000000000000000000000000..6cd56b514729b3e114e688cab15fe1f02fa81ab7 GIT binary patch literal 3373 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 My85}Sb4q9e0H=&f;Q#;t literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons/buttons/stop.png b/src/bilib/src/bilib/commons/buttons/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..ed24ea55c7d256a163ccc32d2d93cb5686371e75 GIT binary patch literal 2923 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 HDWM4fbj@z> literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons/components/BorderToggledButton.java b/src/bilib/src/bilib/commons/components/BorderToggledButton.java new file mode 100644 index 0000000..a3f4ccb --- /dev/null +++ b/src/bilib/src/bilib/commons/components/BorderToggledButton.java @@ -0,0 +1,54 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.components; + +import java.awt.Insets; + +import javax.swing.JButton; + +public class BorderToggledButton extends JButton { + + private String text = ""; + public BorderToggledButton(String text) { + super(text); + this.text = text; + setMargin(new Insets(1, 1, 1, 1)); + } + + @Override + public void setSelected(boolean selected) { + if (selected) + setText("<html><b>" + text + "</b></html>"); + else + setText(text); + } +} diff --git a/src/bilib/src/bilib/commons/components/DoubleScrollablePanel.java b/src/bilib/src/bilib/commons/components/DoubleScrollablePanel.java new file mode 100644 index 0000000..c91aedc --- /dev/null +++ b/src/bilib/src/bilib/commons/components/DoubleScrollablePanel.java @@ -0,0 +1,73 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.components; + +import java.awt.Dimension; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; + +/** + * This class extends the JSplitPane to create two scrollable panel splitted in + * the vertical direction. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ + +public class DoubleScrollablePanel extends JSplitPane { + + public DoubleScrollablePanel(int hpref1, JComponent panel1, int hpref2, JComponent panel2) { + super(JSplitPane.VERTICAL_SPLIT); + int hmin = 70; + JScrollPane scroll1 = new JScrollPane(panel1); + scroll1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scroll1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + scroll1.setBorder(BorderFactory.createEmptyBorder()); + scroll1.setMinimumSize(new Dimension(200, hmin)); + scroll1.setPreferredSize(new Dimension(200, hpref1)); + add(scroll1, 1); + + JScrollPane scroll2 = new JScrollPane(panel2); + scroll2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scroll2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + scroll2.setBorder(BorderFactory.createEmptyBorder()); + scroll2.setMinimumSize(new Dimension(200, hmin)); + scroll2.setPreferredSize(new Dimension(200, hpref2)); + add(scroll2, 1); + setOneTouchExpandable(true); + setBorder(BorderFactory.createEtchedBorder()); + } + +} diff --git a/src/bilib/src/bilib/commons/components/GridPanel.java b/src/bilib/src/bilib/commons/components/GridPanel.java new file mode 100644 index 0000000..3bdcba2 --- /dev/null +++ b/src/bilib/src/bilib/commons/components/GridPanel.java @@ -0,0 +1,200 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.components; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; + +/** + * This class extends the JPanel to create grid panel given the possibility to + * place Java components in an organized manner in the dialog box. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class GridPanel extends JPanel { + + private static final long serialVersionUID = 1L; + private GridBagLayout layout = new GridBagLayout(); + private GridBagConstraints constraint = new GridBagConstraints(); + private int defaultSpace = 3; + + /** + * Constructor. + */ + public GridPanel() { + super(); + setLayout(layout); + setBorder(BorderFactory.createEtchedBorder()); + } + + /** + * Constructor. + */ + public GridPanel(int defaultSpace) { + super(); + setLayout(layout); + this.defaultSpace = defaultSpace; + setBorder(BorderFactory.createEtchedBorder()); + } + + /** + * Constructor. + */ + public GridPanel(boolean border) { + super(); + setLayout(layout); + if (border) { + setBorder(BorderFactory.createEtchedBorder()); + } + } + + /** + * Constructor. + */ + public GridPanel(String title) { + super(); + setLayout(layout); + setBorder(BorderFactory.createTitledBorder(title)); + } + + /** + * Constructor. + */ + public GridPanel(boolean border, int defaultSpace) { + super(); + setLayout(layout); + this.defaultSpace = defaultSpace; + if (border) { + setBorder(BorderFactory.createEtchedBorder()); + } + } + + /** + * Constructor. + */ + public GridPanel(String title, int defaultSpace) { + super(); + setLayout(layout); + this.defaultSpace = defaultSpace; + setBorder(BorderFactory.createTitledBorder(title)); + } + + /** + * Specify the defaultSpace. + */ + public void setSpace(int defaultSpace) { + this.defaultSpace = defaultSpace; + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, String label) { + place(row, col, 1, 1, defaultSpace, new JLabel(label)); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int space, String label) { + place(row, col, 1, 1, space, new JLabel(label)); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int width, int height, String label) { + place(row, col, width, height, defaultSpace, new JLabel(label)); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, JComponent comp) { + place(row, col, 1, 1, defaultSpace, comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int space, JComponent comp) { + place(row, col, 1, 1, space, comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int width, int height, JComponent comp) { + place(row, col, width, height, defaultSpace, comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int width, int height, int space, JComponent comp) { + if (comp == null) + return; + constraint.gridx = col; + constraint.gridy = row; + constraint.gridwidth = width; + constraint.gridheight = height; + constraint.anchor = GridBagConstraints.NORTHWEST; + constraint.insets = new Insets(space, space, space, space); + constraint.fill = GridBagConstraints.HORIZONTAL; + layout.setConstraints(comp, constraint); + add(comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int width, int height, int spaceHorizontal, int spaceVertical, JComponent comp) { + if (comp == null) + return; + constraint.gridx = col; + constraint.gridy = row; + constraint.gridwidth = width; + constraint.gridheight = height; + constraint.anchor = GridBagConstraints.NORTHWEST; + constraint.insets = new Insets(spaceVertical, spaceHorizontal, spaceHorizontal, spaceVertical); + constraint.fill = GridBagConstraints.HORIZONTAL; + layout.setConstraints(comp, constraint); + add(comp); + } +} diff --git a/src/bilib/src/bilib/commons/components/GridToolbar.java b/src/bilib/src/bilib/commons/components/GridToolbar.java new file mode 100644 index 0000000..ffb8d5e --- /dev/null +++ b/src/bilib/src/bilib/commons/components/GridToolbar.java @@ -0,0 +1,246 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.components; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JToolBar; + +/** + * This class extends the JToolbar to create grid panel given the possibility to + * place Java components in an organized manner in the dialog box. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class GridToolbar extends JToolBar { + + private GridBagLayout layout = new GridBagLayout(); + private GridBagConstraints constraint = new GridBagConstraints(); + private int defaultSpace = 3; + + /** + * Constructor. + */ + public GridToolbar() { + super("Control"); + setLayout(layout); + setBorder(BorderFactory.createEtchedBorder()); + setFloatable(false); + } + + /** + * Constructor. + */ + public GridToolbar(boolean border) { + super("Control"); + setLayout(layout); + if (border) { + setBorder(BorderFactory.createEtchedBorder()); + } + setFloatable(false); + } + + /** + * Constructor. + */ + public GridToolbar(String title) { + super(title); + setLayout(layout); + setBorder(BorderFactory.createTitledBorder(title)); + setFloatable(false); + } + + /** + * Constructor. + */ + public GridToolbar(int defaultSpace) { + super("Control"); + setLayout(layout); + this.defaultSpace = defaultSpace; + setBorder(BorderFactory.createEtchedBorder()); + setFloatable(false); + } + + /** + * Constructor. + */ + public GridToolbar(boolean border, int defaultSpace) { + super("Control"); + setLayout(layout); + this.defaultSpace = defaultSpace; + if (border) { + setBorder(BorderFactory.createEtchedBorder()); + } + setFloatable(false); + } + + /** + * Constructor. + */ + public GridToolbar(boolean border, int defaultSpace, boolean floatable) { + super("Control"); + setLayout(layout); + this.defaultSpace = defaultSpace; + if (border) { + setBorder(BorderFactory.createEtchedBorder()); + } + setFloatable(floatable); + } + + /** + * Constructor. + */ + public GridToolbar(boolean border, boolean floatable) { + super("Control"); + setLayout(layout); + if (border) { + setBorder(BorderFactory.createEtchedBorder()); + } + setFloatable(floatable); + } + + /** + * Constructor. + */ + public GridToolbar(String title, boolean floatable) { + super(title); + setLayout(layout); + setBorder(BorderFactory.createTitledBorder(title)); + setFloatable(floatable); + } + + /** + * Constructor. + */ + public GridToolbar(int defaultSpace, boolean floatable) { + super("Control"); + setLayout(layout); + this.defaultSpace = defaultSpace; + setBorder(BorderFactory.createEtchedBorder()); + setFloatable(floatable); + } + + /** + * Constructor. + */ + public GridToolbar(String title, int defaultSpace) { + super(title); + setLayout(layout); + this.defaultSpace = defaultSpace; + setBorder(BorderFactory.createTitledBorder(title)); + setFloatable(false); + } + + /** + * Constructor. + */ + public GridToolbar(String title, int defaultSpace, boolean floatable) { + super(title); + setLayout(layout); + this.defaultSpace = defaultSpace; + setBorder(BorderFactory.createTitledBorder(title)); + setFloatable(floatable); + } + + /** + * Specify the defaultSpace. + */ + public void setSpace(int defaultSpace) { + this.defaultSpace = defaultSpace; + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, String label) { + place(row, col, 1, 1, defaultSpace, new JLabel(label)); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int space, String label) { + place(row, col, 1, 1, space, new JLabel(label)); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int width, int height, String label) { + place(row, col, width, height, defaultSpace, new JLabel(label)); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, JComponent comp) { + place(row, col, 1, 1, defaultSpace, comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int space, JComponent comp) { + place(row, col, 1, 1, space, comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int width, int height, JComponent comp) { + place(row, col, width, height, defaultSpace, comp); + } + + /** + * Place a component in the northwest of the cell. + */ + public void place(int row, int col, int width, int height, int space, JComponent comp) { + if (comp == null) + return; + constraint.gridx = col; + constraint.gridy = row; + constraint.gridwidth = width; + constraint.gridheight = height; + constraint.anchor = GridBagConstraints.NORTHWEST; + constraint.insets = new Insets(space, space, space, space); + constraint.fill = GridBagConstraints.HORIZONTAL; + layout.setConstraints(comp, constraint); + add(comp); + } + +} diff --git a/src/bilib/src/bilib/commons/components/HTMLPane.java b/src/bilib/src/bilib/commons/components/HTMLPane.java new file mode 100644 index 0000000..676e079 --- /dev/null +++ b/src/bilib/src/bilib/commons/components/HTMLPane.java @@ -0,0 +1,141 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.components; + +import java.awt.Dimension; + +import javax.swing.JEditorPane; +import javax.swing.JScrollPane; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; + +/** + * This class extends the Java JEditorPane to make a easy to use panel to + * display HTML information. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class HTMLPane extends JEditorPane { + + private String html = ""; + private String header = ""; + private String footer = ""; + private Dimension dim; + private String font = "verdana"; + private String color = "#222222"; + private String background = "#f8f8f8"; + + public HTMLPane() { + create(); + } + + public HTMLPane(String font) { + this.font = font; + create(); + } + + public HTMLPane(int width, int height) { + this.dim = new Dimension(width, height); + create(); + } + + public HTMLPane(String font, int width, int height) { + this.font = font; + this.dim = new Dimension(width, height); + create(); + } + + public HTMLPane(String font, String color, String background, int width, int height) { + this.font = font; + this.dim = new Dimension(width, height); + this.color = color; + this.background = background; + create(); + } + + @Override + public String getText() { + Document doc = this.getDocument(); + try { + return doc.getText(0, doc.getLength()); + } + catch (BadLocationException e) { + e.printStackTrace(); + return getText(); + } + } + + public void clear() { + html = ""; + append(""); + } + + private void create() { + header += "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n"; + header += "<html><head>\n"; + header += "<style>body {background-color:" + background + "; color:" + color + "; font-family: " + font + ";margin:4px}</style>\n"; + header += "<style>h1 {color:#555555; font-size:1.0em; font-weight:bold; padding:1px; margin:1px;}</style>\n"; + header += "<style>h2 {color:#333333; font-size:0.9em; font-weight:bold; padding:1px; margin:1px;}</style>\n"; + header += "<style>h3 {color:#000000; font-size:0.9em; font-weight:italic; padding:1px; margin:1px;}</style>\n"; + header += "<style>p {color:" + color + "; font-size:0.9em; padding:1px; margin:0px;}</style>\n"; + header += "</head>\n"; + header += "<body>\n"; + footer += "</body></html>\n"; + setEditable(false); + setContentType("text/html; charset=ISO-8859-1"); + } + + public void append(String content) { + html += content; + setText(header + html + footer); + if (dim != null) { + setPreferredSize(dim); + } + setCaretPosition(0); + } + + public void append(String tag, String content) { + html += "<" + tag + ">" + content + "</" + tag + ">"; + setText(header + html + footer); + if (dim != null) { + setPreferredSize(dim); + } + setCaretPosition(0); + } + + public JScrollPane getPane() { + JScrollPane scroll = new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scroll.setPreferredSize(dim); + return scroll; + } +} diff --git a/src/bilib/src/bilib/commons/components/SpinnerRangeDouble.java b/src/bilib/src/bilib/commons/components/SpinnerRangeDouble.java new file mode 100644 index 0000000..f67a8cb --- /dev/null +++ b/src/bilib/src/bilib/commons/components/SpinnerRangeDouble.java @@ -0,0 +1,197 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.components; + +import javax.swing.JFormattedTextField; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; + +/** + * This class extends the Java Swing JSpinner to make a easy to use spinner for + * double. It handles double type. The size can be control by the number of + * visible chars or by a specific format (NumberEditor). + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class SpinnerRangeDouble extends JSpinner { + + private SpinnerNumberModel model; + + private double defValue; + private double minValue; + private double maxValue; + private double incValue; + + /** + * Constructor. + */ + public SpinnerRangeDouble(double defValue, double minValue, double maxValue, double incValue) { + super(); + this.defValue = defValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.incValue = incValue; + + Double def = new Double(defValue); + Double min = new Double(minValue); + Double max = new Double(maxValue); + Double inc = new Double(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField(); + tf.setColumns(7); + } + + /** + * Constructor. + */ + public SpinnerRangeDouble(double defValue, double minValue, double maxValue, double incValue, String format) { + super(); + this.defValue = defValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.incValue = incValue; + + Double def = new Double(defValue); + Double min = new Double(minValue); + Double max = new Double(maxValue); + Double inc = new Double(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + setEditor(new JSpinner.NumberEditor(this, format)); + JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField(); + tf.setColumns(7); + } + + /** + * Constructor. + */ + public SpinnerRangeDouble(double defValue, double minValue, double maxValue, double incValue, int visibleChars) { + super(); + this.defValue = defValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.incValue = incValue; + + Double def = new Double(defValue); + Double min = new Double(minValue); + Double max = new Double(maxValue); + Double inc = new Double(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField(); + tf.setColumns(visibleChars); + } + + /** + * Set the format of the numerical value. + */ + public void setFormat(String format) { + setEditor(new JSpinner.NumberEditor(this, format)); + } + + /** + * Set the minimal and the maximal limit. + */ + public void setLimit(double minValue, double maxValue) { + this.minValue = minValue; + this.maxValue = maxValue; + double value = get(); + Double min = new Double(minValue); + Double max = new Double(maxValue); + Double inc = new Double(incValue); + defValue = (value > maxValue ? maxValue : (value < minValue ? minValue : value)); + Double def = new Double(defValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Set the incremental step. + */ + public void setIncrement(double incValue) { + this.incValue = incValue; + Double def = (Double) getModel().getValue(); + Double min = new Double(minValue); + Double max = new Double(maxValue); + Double inc = new Double(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Returns the incremental step. + */ + public double getIncrement() { + return incValue; + } + + /** + * Set the value in the JSpinner with clipping in the range [min..max]. + */ + public void set(double value) { + value = (value > maxValue ? maxValue : (value < minValue ? minValue : value)); + model.setValue(new Double(value)); + } + + /** + * Return the value with clipping the value in the range [min..max]. + */ + public double get() { + if (model.getValue() instanceof Integer) { + Integer i = (Integer) model.getValue(); + double ii = i.intValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + else if (model.getValue() instanceof Double) { + Double i = (Double) model.getValue(); + double ii = i.doubleValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + else if (model.getValue() instanceof Float) { + Float i = (Float) model.getValue(); + double ii = i.floatValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + return 0.0; + } + + public double getRangeMaximum() { + return maxValue; + } + + public double getRangeMinimum() { + return minValue; + } + +} diff --git a/src/bilib/src/bilib/commons/components/SpinnerRangeFloat.java b/src/bilib/src/bilib/commons/components/SpinnerRangeFloat.java new file mode 100644 index 0000000..3ee802a --- /dev/null +++ b/src/bilib/src/bilib/commons/components/SpinnerRangeFloat.java @@ -0,0 +1,197 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.components; + +import javax.swing.JFormattedTextField; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; + +/** + * This class extends the Java Swing JSpinner to make a easy to use spinner for + * float. It handles float type. The size can be control by the number of + * visible chars or by a specific format (NumberEditor). + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class SpinnerRangeFloat extends JSpinner { + + private SpinnerNumberModel model; + + private float defValue; + private float minValue; + private float maxValue; + private float incValue; + + /** + * Constructor. + */ + public SpinnerRangeFloat(float defValue, float minValue, float maxValue, float incValue) { + super(); + this.defValue = defValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.incValue = incValue; + + Float def = new Float(defValue); + Float min = new Float(minValue); + Float max = new Float(maxValue); + Float inc = new Float(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField(); + tf.setColumns(7); + } + + /** + * Constructor. + */ + public SpinnerRangeFloat(float defValue, float minValue, float maxValue, float incValue, String format) { + super(); + this.defValue = defValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.incValue = incValue; + + Double def = new Double(defValue); + Double min = new Double(minValue); + Double max = new Double(maxValue); + Double inc = new Double(incValue); + this.model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + setEditor(new JSpinner.NumberEditor(this, format)); + JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField(); + tf.setColumns(7); + } + + /** + * Constructor. + */ + public SpinnerRangeFloat(float defValue, float minValue, float maxValue, float incValue, int visibleChars) { + super(); + this.defValue = defValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.incValue = incValue; + + Float def = new Float(defValue); + Float min = new Float(minValue); + Float max = new Float(maxValue); + Float inc = new Float(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField(); + tf.setColumns(visibleChars); + } + + /** + * Set the format of the numerical value. + */ + public void setFormat(String format) { + setEditor(new JSpinner.NumberEditor(this, format)); + } + + /** + * Set the minimal and the maximal limit. + */ + public void setLimit(float minValue, float maxValue) { + this.minValue = minValue; + this.maxValue = maxValue; + float value = get(); + Float min = new Float(minValue); + Float max = new Float(maxValue); + Float inc = new Float(incValue); + defValue = (value > maxValue ? maxValue : (value < minValue ? minValue : value)); + Float def = new Float(defValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Set the incremental step. + */ + public void setIncrement(float incValue) { + this.incValue = incValue; + Float def = (Float) getModel().getValue(); + Float min = new Float(minValue); + Float max = new Float(maxValue); + Float inc = new Float(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Returns the incremental step. + */ + public float getIncrement() { + return incValue; + } + + /** + * Set the value in the JSpinner with clipping in the range [min..max]. + */ + public void set(float value) { + value = (value > maxValue ? maxValue : (value < minValue ? minValue : value)); + model.setValue(new Float(value)); + } + + /** + * Return the value without clipping the value in the range [min..max]. + */ + public float get() { + if (model.getValue() instanceof Integer) { + Integer i = (Integer) model.getValue(); + float ii = (float) i.intValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + else if (model.getValue() instanceof Double) { + Double i = (Double) model.getValue(); + float ii = (float) i.doubleValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + else if (model.getValue() instanceof Float) { + Float i = (Float) model.getValue(); + float ii = i.floatValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + return 0f; + } + + public float getRangeMaximum() { + return maxValue; + } + + public float getRangeMinimum() { + return minValue; + } + +} diff --git a/src/bilib/src/bilib/commons/components/SpinnerRangeInteger.java b/src/bilib/src/bilib/commons/components/SpinnerRangeInteger.java new file mode 100644 index 0000000..a9b345f --- /dev/null +++ b/src/bilib/src/bilib/commons/components/SpinnerRangeInteger.java @@ -0,0 +1,196 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.components; + +import javax.swing.JFormattedTextField; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; + +/** + * This class extends the Java Swing JSpinner to make a easy to use spinner for + * integer. It handles int type. The size can be control by the number of + * visible chars or by a specific format (NumberEditor). + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class SpinnerRangeInteger extends JSpinner { + + private SpinnerNumberModel model; + + private int defValue; + private int minValue; + private int maxValue; + private int incValue; + + /** + * Constructor. + */ + public SpinnerRangeInteger(int defValue, int minValue, int maxValue, int incValue) { + super(); + this.defValue = defValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.incValue = incValue; + + Integer def = new Integer(defValue); + Integer min = new Integer(minValue); + Integer max = new Integer(maxValue); + Integer inc = new Integer(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField(); + tf.setColumns(7); + } + + /** + * Constructor. + */ + public SpinnerRangeInteger(int defValue, int minValue, int maxValue, int incValue, String format) { + super(); + this.defValue = defValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.incValue = incValue; + + Integer def = new Integer(defValue); + Integer min = new Integer(minValue); + Integer max = new Integer(maxValue); + Integer inc = new Integer(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + setEditor(new JSpinner.NumberEditor(this, format)); + JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField(); + tf.setColumns(format.length()); + } + + /** + * Constructor. + */ + public SpinnerRangeInteger(int defValue, int minValue, int maxValue, int incValue, int visibleChars) { + super(); + this.defValue = defValue; + this.minValue = minValue; + this.maxValue = maxValue; + this.incValue = incValue; + + Integer def = new Integer(defValue); + Integer min = new Integer(minValue); + Integer max = new Integer(maxValue); + Integer inc = new Integer(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + JFormattedTextField tf = ((JSpinner.DefaultEditor) getEditor()).getTextField(); + tf.setColumns(visibleChars); + } + + /** + * Set the format of the numerical value. + */ + public void setFormat(String format) { + setEditor(new JSpinner.NumberEditor(this, format)); + } + + /** + * Set the minimal and the maximal limit. + */ + public void setLimit(int minValue, int maxValue) { + this.minValue = minValue; + this.maxValue = maxValue; + int value = get(); + Integer min = new Integer(minValue); + Integer max = new Integer(maxValue); + Integer inc = new Integer(incValue); + defValue = (value > maxValue ? maxValue : (value < minValue ? minValue : value)); + Integer def = new Integer(defValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Set the incremental step. + */ + public void setIncrement(int incValue) { + this.incValue = incValue; + Integer def = (Integer) getModel().getValue(); + Integer min = new Integer(minValue); + Integer max = new Integer(maxValue); + Integer inc = new Integer(incValue); + model = new SpinnerNumberModel(def, min, max, inc); + setModel(model); + } + + /** + * Returns the incremental step. + */ + public int getIncrement() { + return incValue; + } + + /** + * Set the value in the JSpinner with clipping in the range [min..max]. + */ + public void set(int value) { + value = (value > maxValue ? maxValue : (value < minValue ? minValue : value)); + model.setValue(new Integer(value)); + } + + /** + * Return the value without clipping the value in the range [min..max]. + */ + public int get() { + if (model.getValue() instanceof Integer) { + Integer i = (Integer) model.getValue(); + int ii = i.intValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + else if (model.getValue() instanceof Double) { + Double i = (Double) model.getValue(); + int ii = (int) i.doubleValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + else if (model.getValue() instanceof Float) { + Float i = (Float) model.getValue(); + int ii = (int) i.floatValue(); + return (ii > maxValue ? maxValue : (ii < minValue ? minValue : ii)); + } + return 0; + } + + public int getRangeMaximum() { + return maxValue; + } + + public int getRangeMinimum() { + return minValue; + } +} diff --git a/src/bilib/src/bilib/commons/fft/BasicFFT.java b/src/bilib/src/bilib/commons/fft/BasicFFT.java new file mode 100644 index 0000000..c60e101 --- /dev/null +++ b/src/bilib/src/bilib/commons/fft/BasicFFT.java @@ -0,0 +1,234 @@ +package bilib.commons.fft; + +/** + * Performs a 2D or 1D (double) Fourier transformation using a basic FFT + * algorithm. It is only a a size of signal which in a power of 2. The 2D signal + * are store in a 1D double array in row-major convention. The complex value are + * store in a additional dimension [2]. + * + * @author Daniel Sage + */ + +public class BasicFFT { + + /** + * Creates the real frequency part of the Fourier space with the array + * realFunction using hermitian symmetry. The imaginary part is set to zero. + */ + public double[] fillHermitian2D(double[][] realFunction) { + int xsize = (realFunction.length - 1); + int ysize = (realFunction[0].length - 1); + int nx = xsize * 2; + int ny = ysize * 2; + double[] signal = new double[nx * ny]; + for (int y = 0; y <= ysize; y++) + for (int x = 0; x <= xsize; x++) { + signal[x + nx * (y)] = realFunction[x][y]; + } + for (int y = 0; y < ysize; y++) + for (int x = 0; x < xsize; x++) { + signal[nx - 1 - x + nx * (y)] = realFunction[x + 1][y]; + signal[nx - 1 - x + nx * (ny - 1 - y)] = realFunction[x + 1][y + 1]; + signal[x + nx * (ny - 1 - y)] = realFunction[x][y + 1]; + } + return signal; + } + + /** + * Performs the 2D FFT (Cooley Tukey). Signal's size has to be a power of + * two : 2^n. Return a complex signal. + */ + public double[][] transform2D(double real[], double imag[], int nx, int ny) { + + double[][] signal = new double[2][nx * ny]; + + double rowReal[] = new double[nx]; + double rowImag[] = new double[nx]; + + for (int y = 0; y < ny; y++) { + for (int x = 0; x < nx; x++) { + rowReal[x] = real[x + y * nx]; + rowImag[x] = imag[x + y * nx]; + } + transform1D(rowReal, rowImag); + for (int x = 0; x < nx; x++) { + signal[0][x + nx * (y)] = rowReal[x]; + signal[1][x + nx * (y)] = rowImag[x]; + } + } + double colReal[] = new double[ny]; + double colImag[] = new double[ny]; + for (int x = 0; x < nx; x++) { + for (int y = 0; y < ny; y++) { + colReal[y] = signal[0][x + nx * (y)]; + colImag[y] = signal[1][x + nx * (y)]; + } + transform1D(colReal, colImag); + for (int y = 0; y < ny; y++) { + signal[0][x + nx * (y)] = colReal[y]; + signal[1][x + nx * (y)] = colImag[y]; + } + } + return signal; + } + + /** + * Performs the 2D inverse FFT (Cooley Tukey). Signal's size has to be a + * power of two : 2^n. + */ + public double[][] inverse2D(double real[], double imag[], int nx, int ny) { + + double[][] tmpreal = new double[nx][ny]; + double[][] tmpimag = new double[nx][ny]; + + double colReal[] = new double[ny]; + double colImag[] = new double[ny]; + + for (int x = 0; x < nx; x++) { + for (int y = 0; y < ny; y++) { + colReal[y] = real[x + nx * (y)]; + colImag[y] = imag[x + nx * (y)]; + } + inverseFFT1D(colReal, colImag); + for (int y = 0; y < ny; y++) { + tmpreal[x][y] = colReal[y]; + tmpimag[x][y] = colImag[y]; + } + } + + double rowReal[] = new double[nx]; + double rowImag[] = new double[nx]; + + double signal[][] = new double[2][nx * ny]; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < nx; x++) { + rowReal[x] = tmpreal[x][y]; + rowImag[x] = tmpimag[x][y]; + } + inverseFFT1D(rowReal, rowImag); + for (int x = 0; x < nx; x++) { + signal[0][x + nx * y] = rowReal[x]; + signal[1][x + nx * y] = rowImag[x]; + } + } + return signal; + } + + /** + * Perform the Fourier transform of the FFT 1D. + * + * 2D shift of the half size of the input. + */ + public void transform1D(double real[], double imag[]) { + fft1D(real, imag); + } + + /** + * Perform the Inverse of the FFT 1D. + * + * Signal's size has to be a power of two : 2^m + */ + public void inverseFFT1D(double real[], double imag[]) { + int size = real.length; + for (int i = 0; i < size; i++) { + imag[i] = -imag[i]; + } + + fft1D(real, imag); + + for (int i = 0; i < size; i++) { + real[i] = real[i] / size; + imag[i] = -imag[i] / size; + } + } + + /** + * 2D shift of the half size of the input. + */ + public void shift2D(double plane[], int nx, int ny) { + int nx2 = nx / 2; + int ny2 = ny / 2; + double tmp[] = new double[nx * ny]; + + for (int x = 0; x < nx; x++) { + int i = (x >= nx2 ? x - nx2 : x + nx2); + for (int y = 0; y < ny; y++) + tmp[i + nx * y] = plane[x + nx * y]; + } + + for (int y = 0; y < ny; y++) { + int j = (y >= ny2 ? y - ny2 : y + ny2); + for (int x = 0; x < nx; x++) + plane[x + nx * j] = tmp[x + nx * y]; + } + } + + /** + * Perform the FFT 1D Method Decimation in time (Cooley Tukey) + * + * Signal's size has to be a power of two : 2^m + */ + private void fft1D(double real[], double imaginary[]) { + int shift = 0; // start of the computation + int size = real.length; // length of the FFT + + int m = (int) Math.floor((Math.log((double) size) / Math.log(2.0))); + + double Retmp, Imtmp, arg; + int i, j, k, stepsize, shifter, n; + int i_j, i_j_s; + + n = 1 << m; + + double[] Imarg = new double[n]; + double[] Rearg = new double[n]; + + // compute W coefficients + double arg0 = 2.0 * Math.PI / (double) n; + for (i = 0; i < n; i++) { + arg = arg0 * (float) i; + Rearg[i] = Math.cos(arg); + Imarg[i] = -Math.sin(arg); + } + + // bit inversion + for (i = j = shift; i < shift + n - 1; i++) { + if (i < j) { + Retmp = real[i]; + Imtmp = imaginary[i]; + real[i] = real[j]; + imaginary[i] = imaginary[j]; + real[j] = Retmp; + imaginary[j] = Imtmp; + } + k = n >> 1; + while (k + shift <= j) { + j -= k; + k /= 2; + } + j += k; + } + + // Perform the FFT + for (stepsize = 1, shifter = m - 1; stepsize < n; stepsize <<= 1, --shifter) { + for (j = shift; j < shift + n; j += stepsize << 1) { + for (i = 0; i < stepsize; i++) { + i_j = i + j; + i_j_s = i_j + stepsize; + if (i > 0) { + Retmp = Rearg[i << shifter] * real[i_j_s] - Imarg[i << shifter] * imaginary[i_j_s]; + imaginary[i_j_s] = Rearg[i << shifter] * imaginary[i_j_s] + Imarg[i << shifter] * real[i_j_s]; + real[i_j_s] = Retmp; + } + Retmp = real[i_j] - real[i_j_s]; + Imtmp = imaginary[i_j] - imaginary[i_j_s]; + real[i_j] += real[i_j_s]; + imaginary[i_j] += imaginary[i_j_s]; + real[i_j_s] = Retmp; + imaginary[i_j_s] = Imtmp; + } + } + } + + } +} diff --git a/src/bilib/src/bilib/commons/job/.DS_Store b/src/bilib/src/bilib/commons/job/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..b853ace37669751e41d0bd03e4c7f851f8d05f5c GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8z4DAe90Z1N%F(jFwBFrH3z-A;eBr)VLq^1-n=RoBM zQ6Rk>49N_M3^@!r3`qKu^7C^T!LBP}C}qfF$U~O|nasve&XCVg#E{L9%1{Kd6{ZU$ zIf_R^U^E0qLtr!nMnhmU1h9kvsCos}u-sVWMukR0U^E0qb_jsV2L(vmp1}c1H$Z5R z6aymz1Go#o$iTn?3n@l$KY#%w2hs|nL0UmHNGk&)hy^wStd)Tgs+AGk4FTx`bxA-p zSUUqF*k%wPtet@oY%>D`BSbp`Bh+R_Xb*)EqMd;eqMd;eY&*<#qx5J9jD`R#1VDXn zP~RU^|GP3U;OhUwl#kM*Aut*O!!QIGSzLl$oWPYbcHe>OT2OtOfF#Wbs*@plK+>RU q9b6SNK?W2hU`iPo7(iM<^5CkNk%0kRlaDq8U?DV0kA?vKLjVBZG$A|y literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons/job/ExecutionMode.java b/src/bilib/src/bilib/commons/job/ExecutionMode.java new file mode 100644 index 0000000..675b949 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/ExecutionMode.java @@ -0,0 +1,17 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job; + +public enum ExecutionMode { + MULTITHREAD_NO, MULTITHREAD_SYNCHRONIZED, MULTITHREAD_ASYNCHRONIZED +} diff --git a/src/bilib/src/bilib/commons/job/JobAbstract.java b/src/bilib/src/bilib/commons/job/JobAbstract.java new file mode 100644 index 0000000..eab12ad --- /dev/null +++ b/src/bilib/src/bilib/commons/job/JobAbstract.java @@ -0,0 +1,69 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job; + +import java.util.ArrayList; + +/** + * This class is an interface for three implementations of the Job concept: + * Runnable Job, Callable Job, SwingWorker Job. + * + * @author sage + * + */ +public interface JobAbstract { + + public abstract void addMonitor(MonitorAbstract monitor); + + public abstract void removeMonitor(MonitorAbstract monitor); + + public abstract void clearMonitor(); + + public abstract ArrayList<MonitorAbstract> getMonitor(); + + public abstract boolean isNotTimeOut(); + + public abstract boolean isJobLive(); + + public abstract boolean isJobDone(); + + public abstract boolean isJobIdle(); + + public abstract boolean isJobProcessing(); + + public abstract boolean isJobIncomplete(); + + @Override + public abstract String toString(); + + public abstract String getName(); + + public abstract void setName(String name); + + public abstract void setTimeOut(double timeoutms); + + public abstract void abort(); + + public abstract void interrupt(); + + public abstract void abort(String message); + + public abstract void interrupt(String message); + + public abstract void init(); + + public abstract PoolAbstract getPool(); + + public abstract void setPool(PoolAbstract pool); + +} diff --git a/src/bilib/src/bilib/commons/job/JobEvent.java b/src/bilib/src/bilib/commons/job/JobEvent.java new file mode 100644 index 0000000..a6ee541 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/JobEvent.java @@ -0,0 +1,94 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job; + +import java.util.EventObject; + +public class JobEvent extends EventObject { + + public static byte VOID = 0; + public static byte STARTED = 1; + public static byte COMPLETED = 2; + public static byte ABORTED = 3; + public static byte TIMEOUT = 4; + public static byte EXCEPTION = 5; + public static byte INTERRUPTED = 6; + + private byte typeEvent = VOID; + private String message = ""; + private Exception exception; + private JobAbstract job; + + public JobEvent(JobAbstract source, byte typeEvent, String message) { + super(source); + this.job = source; + this.typeEvent = typeEvent; + this.exception = null; + this.message = message; + } + + public JobEvent(JobAbstract source, Exception exception, String message) { + super(source); + this.job = source; + this.typeEvent = EXCEPTION; + this.exception = exception; + this.message = message; + } + + public JobEvent(JobAbstract source, byte typeEvent) { + super(source); + this.job = source; + this.typeEvent = typeEvent; + this.message = ""; + } + + public JobAbstract getJob() { + return job; + } + + public String getMessage() { + return message; + } + + public Exception getException() { + return exception; + } + + public byte getTypeEvent() { + return typeEvent; + } + + public String getEventString() { + if (typeEvent == VOID) + return "VOID"; + if (typeEvent == STARTED) + return "STARTED"; + if (typeEvent == COMPLETED) + return "COMPLETED"; + if (typeEvent == ABORTED) + return "ABORTED"; + if (typeEvent == TIMEOUT) + return "TIMEOUT"; + if (typeEvent == EXCEPTION) + return "EXCEPTION"; + if (typeEvent == INTERRUPTED) + return "INTERRUPTED"; + return "UNDEFINED"; + } + + @Override + public String toString() { + return "JobEvent, type=(" + getEventString() + "), source=(" + source.toString() + "), message=(" + message + ") " + source.getClass().getName(); + } + +} diff --git a/src/bilib/src/bilib/commons/job/MonitorAbstract.java b/src/bilib/src/bilib/commons/job/MonitorAbstract.java new file mode 100644 index 0000000..da98b57 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/MonitorAbstract.java @@ -0,0 +1,32 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job; + +public interface MonitorAbstract { + + public abstract void rewind(); + + public abstract void print(String message); + + public abstract void error(String message); + + public abstract void warning(String message); + + public abstract void progress(double percentage); + + public abstract void progress(double percentage, String message); + + public abstract void increment(double percentageIncrement); + + public abstract void increment(double percentageIncrement, String message); +} diff --git a/src/bilib/src/bilib/commons/job/MonitorConsole.java b/src/bilib/src/bilib/commons/job/MonitorConsole.java new file mode 100644 index 0000000..3453533 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/MonitorConsole.java @@ -0,0 +1,55 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job; + +public class MonitorConsole implements MonitorAbstract { + + @Override + public void rewind() { + } + + @Override + public void print(String message) { + System.out.println(message); + } + + @Override + public void progress(double percentage) { + System.out.println("Progression:" + percentage); + } + + @Override + public void progress(double percentage, String message) { + System.out.println("Progression:" + percentage + " Message:" + message); + } + + @Override + public void increment(double percentageIncrement) { + System.out.println("+ Progression:" + percentageIncrement); + } + + @Override + public void increment(double percentageIncrement, String message) { + System.out.println("+ Progression:" + percentageIncrement + " Message:" + message); + } + + @Override + public void error(String message) { + System.out.println("Error:" + message); + } + + @Override + public void warning(String message) { + System.out.println("Warning:" + message); + } +} diff --git a/src/bilib/src/bilib/commons/job/MonitorProgressBar.java b/src/bilib/src/bilib/commons/job/MonitorProgressBar.java new file mode 100644 index 0000000..b1a91e7 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/MonitorProgressBar.java @@ -0,0 +1,76 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job; + +import javax.swing.JProgressBar; + +public class MonitorProgressBar extends JProgressBar implements MonitorAbstract { + + private double percentage = 0.0; + + public MonitorProgressBar() { + setStringPainted(true); + setMaximum(100); + setPercentage(0); + } + + @Override + public void rewind() { + setPercentage(0); + setString(""); + } + + @Override + public void print(String message) { + setString(message); + } + + @Override + public void progress(double percentage) { + setPercentage(percentage); + } + + @Override + public void progress(double percentage, String message) { + setPercentage(percentage); + setString(message); + } + + @Override + public void increment(double percentageIncrement) { + setPercentage(percentageIncrement + percentage); + } + + @Override + public void increment(double percentageIncrement, String message) { + setPercentage(percentageIncrement + percentage); + setString(message); + } + + @Override + public void error(String message) { + setString("Error:" + message); + } + + @Override + public void warning(String message) { + setString("Warning:" + message); + } + + private void setPercentage(double percentage) { + this.percentage = percentage; + setValue((int) Math.max(0, Math.min(100, percentage))); + + } + +} diff --git a/src/bilib/src/bilib/commons/job/MonitorTimedLog.java b/src/bilib/src/bilib/commons/job/MonitorTimedLog.java new file mode 100644 index 0000000..e14784e --- /dev/null +++ b/src/bilib/src/bilib/commons/job/MonitorTimedLog.java @@ -0,0 +1,100 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job; + +import java.awt.Insets; +import java.text.DecimalFormat; + +import javax.swing.JTextArea; +import javax.swing.text.DefaultCaret; + +public class MonitorTimedLog extends JTextArea implements MonitorAbstract { + + private double chrono = System.nanoTime(); + + public MonitorTimedLog(int rows, int columns) { + super(rows, columns); + setMargin(new Insets(5, 5, 5, 5)); + setEditable(false); + DefaultCaret caret = (DefaultCaret) getCaret(); + caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); + } + + @Override + public void rewind() { + chrono = System.nanoTime(); + setText(""); + } + + @Override + public void print(String message) { + update(message); + } + + @Override + public void error(String message) { + update(message); + } + + @Override + public void warning(String message) { + update(message); + } + + @Override + public void progress(double percentage) { + update("Progression: " + percentage); + } + + @Override + public void progress(double percentage, String message) { + update("Progression: " + percentage + " - "); + } + + @Override + public void increment(double percentage) { + update("+ Progression: " + percentage); + } + + @Override + public void increment(double percentage, String message) { + update("+ Progression: " + percentage + " - "); + } + + private void update(String message) { + append(toc() + " " + message + "\n"); + setCaretPosition(getDocument().getLength()); + } + + private String toc() { + double te = System.nanoTime() - chrono; + String s = " "; + DecimalFormat df = new DecimalFormat("####.##"); + if (te < 1000.0) + return s + df.format(te) + " ns"; + te /= 1000; + if (te < 1000.0) + return s + df.format(te) + " us"; + te /= 1000; + if (te < 3000.0) + return s + df.format(te) + " ms"; + te /= 1000; + if (te < 600.1) + return s + df.format(te) + " s"; + te /= 60; + if (te < 600.1) + return s + df.format(te) + " min."; + te /= 60; + return s + df.format(te) + " h."; + } +} diff --git a/src/bilib/src/bilib/commons/job/MonitorTimedProgressBar.java b/src/bilib/src/bilib/commons/job/MonitorTimedProgressBar.java new file mode 100644 index 0000000..9431a91 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/MonitorTimedProgressBar.java @@ -0,0 +1,106 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job; + +import java.text.DecimalFormat; + +import javax.swing.JProgressBar; + +public class MonitorTimedProgressBar extends JProgressBar implements MonitorAbstract { + + private double chrono = System.nanoTime(); + private double percentage = 0.0; + + public MonitorTimedProgressBar() { + setStringPainted(true); + setMaximum(100); + setPercentage(0); + } + + @Override + public void rewind() { + setPercentage(0); + chrono = System.nanoTime(); + setText(""); + } + + @Override + public void print(String message) { + setText(message); + } + + @Override + public void progress(double percentage) { + setPercentage(percentage); + } + + @Override + public void progress(double percentage, String message) { + setPercentage(percentage); + setText(message); + } + + @Override + public void increment(double percentageIncrement) { + setPercentage(percentageIncrement + percentage); + } + + @Override + public void increment(double percentageIncrement, String message) { + setPercentage(percentageIncrement + percentage); + setText(message); + } + + @Override + public void error(String message) { + setText("Error:" + message); + } + + @Override + public void warning(String message) { + setText("Warning:" + message); + } + + private void setText(String message) { + setString(toc() + " - " + message); + } + + private String toc() { + double te = System.nanoTime() - chrono; + String s = " "; + DecimalFormat df = new DecimalFormat("####.##"); + if (te < 1000.0) + return s + df.format(te) + " ns"; + te /= 1000; + if (te < 1000.0) + return s + df.format(te) + " us"; + te /= 1000; + if (te < 3000.0) + return s + df.format(te) + " ms"; + te /= 1000; + if (te < 600.1) + return s + df.format(te) + " s"; + te /= 60; + if (te < 600.1) + return s + df.format(te) + " min."; + te /= 60; + return s + df.format(te) + " h."; + } + + private void setPercentage(double percentage) { + this.percentage = percentage; + setValue((int) Math.max(0, Math.min(100, percentage))); + + } + +} diff --git a/src/bilib/src/bilib/commons/job/PoolAbstract.java b/src/bilib/src/bilib/commons/job/PoolAbstract.java new file mode 100644 index 0000000..aa7e3e8 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/PoolAbstract.java @@ -0,0 +1,52 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job; + +import java.util.concurrent.Future; + +public interface PoolAbstract { + + public abstract String getName(); + + public abstract void execute(ExecutionMode mode); + + public abstract void execute(ExecutionMode mode, int nThreads); + + public abstract Future<Object> getFuture(String jobName); + + public abstract int size(); + + public abstract void cancel(); + + public abstract void register(JobAbstract job); + + public abstract void register(JobAbstract job, double timeoutms); + + public abstract void unregister(JobAbstract job); + + public abstract void unregisterAll(); + + public abstract boolean isJobDone(); + + public abstract boolean isJobLive(); + + public abstract boolean isJobIdle(); + + public abstract void waitTermination(); + + public abstract void die(); + + public abstract void fire(JobEvent event); + + public abstract void fire(JobAbstract parent, JobEvent event); +} diff --git a/src/bilib/src/bilib/commons/job/callable/CallableDemo.java b/src/bilib/src/bilib/commons/job/callable/CallableDemo.java new file mode 100644 index 0000000..7e2bf62 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/callable/CallableDemo.java @@ -0,0 +1,303 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job.callable; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import bilib.commons.job.ExecutionMode; +import bilib.commons.job.JobEvent; +import bilib.commons.job.MonitorAbstract; +import bilib.commons.job.MonitorProgressBar; +import bilib.commons.job.MonitorTimedLog; +import bilib.commons.job.MonitorTimedProgressBar; +import bilib.commons.job.callable.PoolResponder; + +public class CallableDemo extends JFrame implements PoolResponder, ActionListener { + + public JRadioButton rbSequential = new JRadioButton("Sequential", false); + public JRadioButton rbAsynchronized = new JRadioButton("Parallel - Asynchronized jobs (not wait)", false); + public JRadioButton rbSynchronized = new JRadioButton("Parallel - Synchronized jobs (wait termination)", true); + + public JButton execute = new JButton("Execute"); + public JTextField nthreads = new JTextField("0"); + public JTextField njobs = new JTextField("10"); + public JButton simulateAbort = new JButton("Abort"); + public JButton simulateException = new JButton("Simulate an Exception"); + public JButton simulateJobException = new JButton("Simulate an JobException"); + public JButton simulateInterruption = new JButton("Simulate an Interruption"); + public JButton simulateCancellation = new JButton("Simulate an Cancellation"); + public JButton simulateTimeOut = new JButton("Simulate an TimeOut"); + + public MonitorTimedProgressBar bar = new MonitorTimedProgressBar(); + public MonitorProgressBar bar1 = new MonitorProgressBar(); + public MonitorTimedLog log = new MonitorTimedLog(10, 10); + + private double chrono; + private MainProcessing main; + + public static void main(String args[]) { + new CallableDemo(); + } + + public CallableDemo() { + super("Callable Demo"); + main = new MainProcessing(this); + + ButtonGroup bg1 = new ButtonGroup(); + bg1.add(rbSequential); + bg1.add(rbAsynchronized); + bg1.add(rbSynchronized); + + JPanel panel1 = new JPanel(new GridLayout(20, 1)); + + panel1.add(rbSequential); + panel1.add(rbAsynchronized); + panel1.add(rbSynchronized); + + panel1.add(new JLabel("Number of threads (0 = nb of jobs)")); + panel1.add(nthreads); + panel1.add(new JLabel("Number of jobs")); + panel1.add(njobs); + panel1.add(execute); + panel1.add(new JLabel("")); + panel1.add(simulateAbort); + panel1.add(simulateException); + panel1.add(simulateJobException); + panel1.add(simulateInterruption); + panel1.add(simulateCancellation); + panel1.add(simulateTimeOut); + + JPanel panel3 = new JPanel(new BorderLayout()); + panel3.add(bar, BorderLayout.NORTH); + panel3.add(bar1, BorderLayout.SOUTH); + + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + panel.add(panel1, BorderLayout.NORTH); + panel.add(panel3, BorderLayout.SOUTH); + panel.add(log, BorderLayout.CENTER); + + execute.addActionListener(this); + simulateAbort.addActionListener(this); + simulateException.addActionListener(this); + simulateJobException.addActionListener(this); + simulateInterruption.addActionListener(this); + simulateCancellation.addActionListener(this); + simulateTimeOut.addActionListener(this); + getContentPane().add(panel); + pack(); + setVisible(true); + } + + private ExecutionMode getMode() { + ExecutionMode mode = ExecutionMode.MULTITHREAD_NO; + if (rbSynchronized.isSelected()) + mode = ExecutionMode.MULTITHREAD_SYNCHRONIZED; + if (rbAsynchronized.isSelected()) + mode = ExecutionMode.MULTITHREAD_ASYNCHRONIZED; + return mode; + } + + @Override + public void actionPerformed(ActionEvent event) { + + if (event.getSource() == execute) { + main.setMode(getMode(), Integer.parseInt(nthreads.getText()), Integer.parseInt(njobs.getText())); + System.out.println("\n\n"); + Pool pool = new Pool("Main", this); + pool.register(main); + pool.execute(ExecutionMode.MULTITHREAD_ASYNCHRONIZED); + chrono = System.nanoTime(); + } + + if (event.getSource() == simulateAbort) + main.simulateAbort(); + + if (event.getSource() == simulateException) + main.simulateException(); + + if (event.getSource() == simulateJobException) + main.simulateJobException(); + + if (event.getSource() == simulateInterruption) + main.simulateInterruption(); + + if (event.getSource() == simulateCancellation) + main.simulateCancellation(); + + if (event.getSource() == simulateTimeOut) + main.simulateTimeOut(); + } + + // + // MainProcessing + // + public class MainProcessing extends Job implements PoolResponder { + private ExecutionMode mode; + private int nthreads = 0; + private int njobs; + private PoolResponder responder; + private Pool pool; + private int niter = 15; + + public MainProcessing(PoolResponder responder) { + super(); + this.responder = responder; + } + + public void setMode(ExecutionMode mode, int nthreads, int njobs) { + this.mode = mode; + this.njobs = njobs; + this.nthreads = nthreads; + } + + @Override + public Object process() { + bar.rewind(); + bar1.rewind(); + log.rewind(); + + pool = new Pool("my", responder); + MyCallable p1 = new MyCallable(niter, bar); + p1.addMonitor(log); + p1.addMonitor(bar1); + pool.register(p1); + for (int i = 0; i < njobs; i++) + pool.register(new MyCallable(niter, bar)); + pool.execute(mode, nthreads); + + System.out.println("End of main"); + return "End"; + + } + + public void simulateAbort() { + if (pool != null) + pool.getRegisteredJob(pool.size() - 1).abort(); + } + + public void simulateCancellation() { + if (pool != null) + pool.cancel(); + } + + public void simulateException() { + if (pool != null) + ((MyCallable) pool.getRegisteredJob(pool.size() / 2 - 1)).withException = true; + } + + public void simulateJobException() { + if (pool != null) + ((MyCallable) pool.getRegisteredJob(pool.size() - 1)).withJobException = true; + } + + public void simulateInterruption() { + if (pool != null) + ((MyCallable) pool.getRegisteredJob(pool.size() - 1)).withInterruption = true; + } + + public void simulateTimeOut() { + if (pool != null) + ((MyCallable) pool.getRegisteredJob(pool.size() - 1)).withTimeOut = true; + } + + @Override + public void onEvent(Pool pool, JobEvent event) { + responder.onEvent(pool, event); + } + + @Override + public void onSuccess(Pool pool, JobEvent event) { + responder.onSuccess(pool, event); + } + + @Override + public void onFailure(Pool pool, JobEvent event) { + responder.onFailure(pool, event); + } + + } + + // + // MyCallable + // + public class MyCallable extends Job { + public boolean withTimeOut = false; + public boolean withInterruption = false; + public boolean withException = false; + public boolean withJobException = false; + private int niter = 15; + + public MyCallable(int niter, MonitorAbstract monitor) { + super(monitor); + this.niter = niter; + } + + @Override + public Object process() throws RuntimeException { + int i = 0; + for (i = 0; i < niter && live; i++) { + increment(2, getName() + " " + live + " " + done); + for (int j = 0; j < 600000 && live; j++) + Math.round(Math.cos(Math.exp(Math.pow(j, -3)))); + if (withJobException) + throw new RuntimeException("It is a programming exception"); + if (withInterruption) { + error("Interrupted messsage"); + interrupt(); + } + if (withTimeOut) + niter += 2; + if (withException) { + double a[] = new double[2]; + a[3] = 1; + } + if (!live) + return "no live"; + } + System.out.println("End of " + getName() + " iterations:" + i + " " + live + " " + done); + return "End of " + getName() + " iterations:" + i + " " + live + " " + done; + } + } + + @Override + public void onEvent(Pool pool, JobEvent event) { + System.out.println("Main side: Job Event " + event); + } + + @Override + public void onSuccess(Pool pool, JobEvent event) { + System.out.println(">>> Success " + event + " from pool " + pool.getName()); + System.out.println("Computation time: " + (System.nanoTime() - chrono) / 1000000.0); + } + + @Override + public void onFailure(Pool pool, JobEvent event) { + System.out.println(">>> Failure " + event + " from pool " + pool.getName()); + if (event.getException() != null) + event.getException().printStackTrace(); + } + +} diff --git a/src/bilib/src/bilib/commons/job/callable/Job.java b/src/bilib/src/bilib/commons/job/callable/Job.java new file mode 100644 index 0000000..7f35fc5 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/callable/Job.java @@ -0,0 +1,253 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job.callable; + +import java.util.ArrayList; +import java.util.concurrent.Callable; + +import bilib.commons.job.JobAbstract; +import bilib.commons.job.JobEvent; +import bilib.commons.job.MonitorAbstract; +import bilib.commons.job.PoolAbstract; + +public abstract class Job implements JobAbstract, MonitorAbstract, Callable<Object> { + + public boolean live = false; + public boolean done = false; + public boolean idle = false; + private double chrono = -1; + private double timeoutns = -1; + private boolean stopTimeOut = false; + private ArrayList<MonitorAbstract> monitors = new ArrayList<MonitorAbstract>(); + + public PoolAbstract pool; + + public abstract Object process() throws RuntimeException; + + private String name; + + public Job() { + } + + public Job(MonitorAbstract monitor) { + monitors.add(monitor); + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public PoolAbstract getPool() { + return pool; + } + + @Override + public void setPool(PoolAbstract pool) { + this.pool = pool; + } + + @Override + public void setTimeOut(double timeoutms) { + this.timeoutns = timeoutms * 1000000.0; + this.stopTimeOut = true; + } + + @Override + public void abort() { + idle = false; + live = false; + done = false; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.ABORTED)); + } + + @Override + public void interrupt() { + idle = false; + live = false; + done = false; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.INTERRUPTED)); + } + + @Override + public void abort(String message) { + idle = false; + live = false; + done = false; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.ABORTED, message)); + } + + @Override + public void interrupt(String message) { + idle = false; + live = false; + done = false; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.INTERRUPTED, message)); + } + + @Override + public void init() { + idle = true; + live = true; + done = false; + chrono = System.nanoTime(); + } + + @Override + public Object call() { + Object o = null; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.STARTED)); + try { + idle = false; + o = process(); + if (live) { + done = true; + live = false; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.COMPLETED)); + } + } + catch (RuntimeException ex) { + if (pool != null) + pool.fire(new JobEvent(this, ex, ex.getMessage())); + } + live = false; + idle = true; + return o; + } + + @Override + public boolean isNotTimeOut() { + if (!live) + return false; + if (stopTimeOut) + if ((System.nanoTime() - chrono) > timeoutns) { + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.TIMEOUT)); + live = false; + done = false; + return false; + } + return true; + } + + @Override + public boolean isJobLive() { + return live; + } + + @Override + public boolean isJobDone() { + return done; + } + + @Override + public boolean isJobIdle() { + return idle; + } + + @Override + public boolean isJobProcessing() { + return !idle; + } + + @Override + public boolean isJobIncomplete() { + return !done; + } + + @Override + public String toString() { + return "JobCallable: " + getName(); + } + + @Override + public void rewind() { + for (MonitorAbstract monitor : monitors) + monitor.rewind(); + } + + @Override + public void print(String message) { + for (MonitorAbstract monitor : monitors) + monitor.print(message); + } + + @Override + public void error(String message) { + for (MonitorAbstract monitor : monitors) + monitor.error(message); + } + + @Override + public void warning(String message) { + for (MonitorAbstract monitor : monitors) + monitor.warning(message); + } + + @Override + public void progress(double percentage) { + for (MonitorAbstract monitor : monitors) + monitor.progress(percentage); + } + + @Override + public void progress(double percentage, String message) { + for (MonitorAbstract monitor : monitors) + monitor.progress(percentage, message); + } + + @Override + public void increment(double percentageIncrement) { + for (MonitorAbstract monitor : monitors) + monitor.increment(percentageIncrement); + } + + @Override + public void increment(double percentageIncrement, String message) { + for (MonitorAbstract monitor : monitors) + monitor.increment(percentageIncrement, message); + } + + @Override + public void addMonitor(MonitorAbstract monitor) { + monitors.add(monitor); + } + + @Override + public void removeMonitor(MonitorAbstract monitor) { + monitors.remove(monitor); + } + + @Override + public void clearMonitor() { + monitors.clear(); + } + + @Override + public ArrayList<MonitorAbstract> getMonitor() { + return monitors; + } + +} diff --git a/src/bilib/src/bilib/commons/job/callable/Pool.java b/src/bilib/src/bilib/commons/job/callable/Pool.java new file mode 100644 index 0000000..7448679 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/callable/Pool.java @@ -0,0 +1,288 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job.callable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import bilib.commons.job.ExecutionMode; +import bilib.commons.job.JobAbstract; +import bilib.commons.job.JobEvent; +import bilib.commons.job.PoolAbstract; +import bilib.commons.job.callable.PoolResponder; +import bilib.commons.job.callable.Job; + +public class Pool implements PoolAbstract { + + private ArrayList<Job> jobs; + protected String name; + protected PoolResponder responder; + + protected HashMap<String, Future<Object>> results; + + /** + * Create a new pool with one registered job (Callable). + * + * @param job + * the job to register + * @param responder + * the responder handling the job event + */ + public Pool(Job job, PoolResponder responder) { + jobs = new ArrayList<Job>(); + results = new HashMap<String, Future<Object>>(); + this.name = job.getName(); + this.responder = responder; + register(job); + } + + /** + * Create a new pool without any registered job (Callable). + * + * @param name + * name of the pool + * @param responder + * the responder handling the job event + */ + public Pool(String name, PoolResponder responder) { + jobs = new ArrayList<Job>(); + results = new HashMap<String, Future<Object>>(); + this.name = name; + this.responder = responder; + } + + @Override + public void fire(JobEvent event) { + + int t = event.getTypeEvent(); + if (responder != null) + responder.onEvent(this, event); + + if (t == JobEvent.EXCEPTION) { + die(); + if (responder != null) { + responder.onFailure(this, event); + } + } + else if (t == JobEvent.ABORTED) { + die(); + if (responder != null) + responder.onFailure(this, event); + } + else if (t == JobEvent.TIMEOUT) { + die(); + if (responder != null) + responder.onFailure(this, event); + } + else if (t == JobEvent.COMPLETED) { + if (isJobDone()) { + die(); + if (responder != null) + responder.onSuccess(this, event); + } + } + } + + @Override + public void fire(JobAbstract parent, JobEvent event) { + if (parent == null) + return; + if (parent.getPool() == null) + return; + if (event.getTypeEvent() == JobEvent.EXCEPTION) + parent.getPool().fire(new JobEvent(parent, event.getException(), event.getMessage())); + else + parent.getPool().fire(new JobEvent(parent, event.getTypeEvent(), event.getMessage())); + + } + + public ArrayList<Job> getRegisteredJobs() { + return jobs; + } + + public Job getRegisteredJob(int i) { + return jobs.get(i); + } + + @Override + public void execute(ExecutionMode mode) { + execute(mode, jobs.size()); + } + + @Override + @SuppressWarnings("unchecked") + public void execute(ExecutionMode mode, int nThreads) { + for (Job job : jobs) + job.init(); + if (mode == ExecutionMode.MULTITHREAD_NO) { + for (Job job : jobs) { + job.call(); + } + } + else { + if (nThreads <= 0) + nThreads = jobs.size(); + ExecutorService executor = Executors.newFixedThreadPool(nThreads); + for (Job job : jobs) { + Future<?> future = executor.submit(job); + results.put(job.getName(), (Future<Object>) future); + } + executor.shutdown(); + if (mode == ExecutionMode.MULTITHREAD_SYNCHRONIZED) + waitTermination(); + } + } + + @Override + public Future<Object> getFuture(String jobName) { + if (jobs != null) + for (int i = 0; i < jobs.size(); i++) { + if (jobs.get(i).getName().equals(jobName)) + return results.get(jobs.get(i).getName()); + } + return null; + } + + @Override + public int size() { + int s = 0; + if (jobs != null) + s += jobs.size(); + return s; + } + + @Override + public void cancel() { + if (jobs != null) + for (Job job : jobs) + results.get(job.getName()).cancel(true); + } + + @Override + public void register(JobAbstract job) { + if (jobs == null) + return; + if (job instanceof Job) + if (jobs.add((Job) job)) { + job.setName(name + "-" + jobs.size()); + job.setPool(this); + } + } + + @Override + public void register(JobAbstract job, double timeoutms) { + if (jobs == null) + return; + if (job instanceof Job) + if (jobs.add((Job) job)) { + job.setTimeOut(timeoutms); + job.setName(name + "-" + jobs.size()); + job.setPool(this); + } + } + + @Override + public void unregister(JobAbstract job) { + if (jobs == null) + return; + if (job instanceof Job) { + jobs.remove((Job) job); + job.setPool(null); + } + } + + @Override + public void unregisterAll() { + if (jobs != null) + jobs.clear(); + } + + @Override + public boolean isJobDone() { + int count = 0; + if (jobs != null) { + for (Job job : jobs) { + if (job.done) + count++; + } + return (count == jobs.size()); + } + return false; + } + + @Override + public boolean isJobLive() { + int count = 0; + if (jobs != null) { + for (Job job : jobs) { + if (job.live) + count++; + } + return (count == jobs.size()); + } + return false; + } + + @Override + public boolean isJobIdle() { + int count = 0; + if (jobs != null) { + for (Job job : jobs) { + if (job.idle) + count++; + } + return (count == jobs.size()); + } + return false; + } + + @Override + public void waitTermination() { + boolean termination = false; + + if (jobs != null) + while (!termination) { + int count = 0; + for (Job job : jobs) + if (!job.live) + count++; + termination = (count >= jobs.size()); + } + } + + @Override + public synchronized void die() { + if (jobs != null) + for (Job job : jobs) { + job.live = false; + job.done = true; + } + } + + @Override + public String toString() { + String form = ""; + if (jobs != null) + form = jobs.getClass().getName(); + return " Pool=(" + name + ", " + size() + " " + form + ") "; + } + + @Override + public String getName() { + return name; + } + +} diff --git a/src/bilib/src/bilib/commons/job/callable/PoolResponder.java b/src/bilib/src/bilib/commons/job/callable/PoolResponder.java new file mode 100644 index 0000000..c9d59c8 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/callable/PoolResponder.java @@ -0,0 +1,24 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job.callable; + +import bilib.commons.job.JobEvent; + +public interface PoolResponder { + + public abstract void onEvent(Pool pool, JobEvent event); + + public abstract void onSuccess(Pool pool, JobEvent event); + + public abstract void onFailure(Pool pool, JobEvent event); +} diff --git a/src/bilib/src/bilib/commons/job/runnable/Job.java b/src/bilib/src/bilib/commons/job/runnable/Job.java new file mode 100644 index 0000000..00ddb95 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/runnable/Job.java @@ -0,0 +1,251 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job.runnable; + +import java.util.ArrayList; + +import bilib.commons.job.JobAbstract; +import bilib.commons.job.JobEvent; +import bilib.commons.job.MonitorAbstract; +import bilib.commons.job.PoolAbstract; + +public abstract class Job implements JobAbstract, MonitorAbstract, Runnable { + + public boolean live = false; + public boolean done = false; + public boolean idle = false; + + private double chrono = -1; + private double timeoutns = -1; + private boolean stopTimeOut = false; + private ArrayList<MonitorAbstract> monitors = new ArrayList<MonitorAbstract>(); + + private PoolAbstract pool; + private String name; + + public abstract void process() throws RuntimeException; + + public Job() { + } + + public Job(MonitorAbstract monitor) { + monitors.add(monitor); + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public PoolAbstract getPool() { + return pool; + } + + @Override + public void setPool(PoolAbstract pool) { + this.pool = pool; + } + + @Override + public void setTimeOut(double timeoutms) { + this.timeoutns = timeoutms * 1000000.0; + this.stopTimeOut = true; + } + + @Override + public void abort() { + idle = false; + live = false; + done = true; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.ABORTED)); + } + + @Override + public void interrupt() { + idle = false; + live = false; + done = true; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.INTERRUPTED, "Interruption")); + } + + @Override + public void abort(String message) { + idle = false; + live = false; + done = true; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.ABORTED, message)); + } + + @Override + public void interrupt(String message) { + idle = false; + live = false; + done = true; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.INTERRUPTED, message)); + } + + @Override + public void init() { + idle = true; + live = true; + done = false; + chrono = System.nanoTime(); + } + + @Override + public void run() { + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.STARTED)); + try { + idle = false; + process(); + if (live) { + done = true; + live = false; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.COMPLETED)); + } + } + catch (IllegalArgumentException ex) { // Workaround a unexplained Icy exception + } + catch (RuntimeException ex) { + if (pool != null) + pool.fire(new JobEvent(this, ex, ex.getMessage())); + } + live = false; + idle = true; + } + + @Override + public boolean isNotTimeOut() { + if (!live) + return false; + if (stopTimeOut) + if ((System.nanoTime() - chrono) > timeoutns) { + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.TIMEOUT)); + live = false; + done = true; + return false; + } + return true; + } + + @Override + public boolean isJobLive() { + return live; + } + + @Override + public boolean isJobDone() { + return done; + } + + @Override + public boolean isJobIdle() { + return idle; + } + + @Override + public boolean isJobProcessing() { + return !idle; + } + + @Override + public boolean isJobIncomplete() { + return !done; + } + + @Override + public String toString() { + return "JobRunnable: " + getName(); + } + + @Override + public void rewind() { + for (MonitorAbstract monitor : monitors) + monitor.rewind(); + } + + @Override + public void print(String message) { + for (MonitorAbstract monitor : monitors) + monitor.print(message); + } + + @Override + public void error(String message) { + for (MonitorAbstract monitor : monitors) + monitor.error(message); + } + + @Override + public void warning(String message) { + for (MonitorAbstract monitor : monitors) + monitor.warning(message); + } + + @Override + public void progress(double percentage) { + for (MonitorAbstract monitor : monitors) + monitor.progress(percentage); + } + + @Override + public void progress(double percentage, String message) { + for (MonitorAbstract monitor : monitors) + monitor.progress(percentage, message); + } + + @Override + public void increment(double percentageIncrement) { + for (MonitorAbstract monitor : monitors) + monitor.increment(percentageIncrement); + } + + @Override + public void increment(double percentageIncrement, String message) { + for (MonitorAbstract monitor : monitors) + monitor.increment(percentageIncrement, message); + } + + @Override + public void addMonitor(MonitorAbstract monitor) { + monitors.add(monitor); + } + + @Override + public void removeMonitor(MonitorAbstract monitor) { + monitors.remove(monitor); + } + + @Override + public void clearMonitor() { + monitors.clear(); + } + + @Override + public ArrayList<MonitorAbstract> getMonitor() { + return monitors; + } +} diff --git a/src/bilib/src/bilib/commons/job/runnable/Pool.java b/src/bilib/src/bilib/commons/job/runnable/Pool.java new file mode 100644 index 0000000..23fbd24 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/runnable/Pool.java @@ -0,0 +1,286 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job.runnable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import bilib.commons.job.ExecutionMode; +import bilib.commons.job.JobAbstract; +import bilib.commons.job.JobEvent; +import bilib.commons.job.PoolAbstract; + +public class Pool implements PoolAbstract { + + private ArrayList<Job> jobs; + protected String name; + protected PoolResponder responder; + + protected HashMap<String, Future<Object>> results; + + /** + * Create a new pool with one registered job (Runnable). + * + * @param job + * the job to register + * @param responder + * the responder handling the job event + */ + public Pool(Job job, PoolResponder responder) { + jobs = new ArrayList<Job>(); + results = new HashMap<String, Future<Object>>(); + this.name = job.getName(); + this.responder = responder; + register(job); + } + + /** + * Create a new pool without any registered job (Runnable). + * + * @param name + * name of the pool + * @param responder + * the responder handling the job event + */ + public Pool(String name, PoolResponder responder) { + jobs = new ArrayList<Job>(); + results = new HashMap<String, Future<Object>>(); + this.name = name; + this.responder = responder; + } + + @Override + public void fire(JobEvent event) { + + int t = event.getTypeEvent(); + if (responder != null) + responder.onEvent(this, event); + + if (t == JobEvent.EXCEPTION) { + die(); + if (responder != null) { + responder.onFailure(this, event); + } + } + else if (t == JobEvent.ABORTED) { + die(); + if (responder != null) + responder.onFailure(this, event); + } + else if (t == JobEvent.TIMEOUT) { + die(); + if (responder != null) + responder.onFailure(this, event); + } + else if (t == JobEvent.COMPLETED) { + if (isJobDone()) { + die(); + if (responder != null) + responder.onSuccess(this, event); + } + } + } + + @Override + public void fire(JobAbstract parent, JobEvent event) { + if (parent == null) + return; + if (parent.getPool() == null) + return; + if (event.getTypeEvent() == JobEvent.EXCEPTION) + parent.getPool().fire(new JobEvent(parent, event.getException(), event.getMessage())); + else + parent.getPool().fire(new JobEvent(parent, event.getTypeEvent(), event.getMessage())); + + } + + public ArrayList<Job> getRegisteredJobs() { + return jobs; + } + + public Job getRegisteredJob(int i) { + return jobs.get(i); + } + + @Override + public void execute(ExecutionMode mode) { + execute(mode, jobs.size()); + } + + @Override + @SuppressWarnings("unchecked") + public void execute(ExecutionMode mode, int nThreads) { + for (Job job : jobs) + job.init(); + if (mode == ExecutionMode.MULTITHREAD_NO) { + for (Job job : jobs) { + job.run(); + } + } + else { + if (nThreads <= 0) + nThreads = jobs.size(); + ExecutorService executor = Executors.newFixedThreadPool(nThreads); + for (Job job : jobs) { + Future<?> future = executor.submit(job); + results.put(job.getName(), (Future<Object>) future); + } + executor.shutdown(); + if (mode == ExecutionMode.MULTITHREAD_SYNCHRONIZED) + waitTermination(); + } + } + + @Override + public Future<Object> getFuture(String jobName) { + if (jobs != null) + for (int i = 0; i < jobs.size(); i++) { + if (jobs.get(i).getName().equals(jobName)) + return results.get(jobs.get(i).getName()); + } + return null; + } + + @Override + public int size() { + int s = 0; + if (jobs != null) + s += jobs.size(); + return s; + } + + @Override + public void cancel() { + if (jobs != null) + for (Job job : jobs) + results.get(job.getName()).cancel(true); + } + + @Override + public void register(JobAbstract job) { + if (jobs == null) + return; + if (job instanceof Job) + if (jobs.add((Job) job)) { + job.setName(name + "-" + jobs.size()); + job.setPool(this); + } + } + + @Override + public void register(JobAbstract job, double timeoutms) { + if (jobs == null) + return; + if (job instanceof Job) + if (jobs.add((Job) job)) { + job.setTimeOut(timeoutms); + job.setName(name + "-" + jobs.size()); + job.setPool(this); + } + } + + @Override + public void unregister(JobAbstract job) { + if (jobs == null) + return; + if (job instanceof Job) { + jobs.remove((Job) job); + job.setPool(null); + } + } + + @Override + public void unregisterAll() { + if (jobs != null) + jobs.clear(); + } + + @Override + public boolean isJobDone() { + int count = 0; + if (jobs != null) { + for (Job job : jobs) { + if (job.done) + count++; + } + return (count == jobs.size()); + } + return false; + } + + @Override + public boolean isJobLive() { + int count = 0; + if (jobs != null) { + for (Job job : jobs) { + if (job.live) + count++; + } + return (count == jobs.size()); + } + return false; + } + + @Override + public boolean isJobIdle() { + int count = 0; + if (jobs != null) { + for (Job job : jobs) { + if (job.idle) + count++; + } + return (count == jobs.size()); + } + return false; + } + + @Override + public void waitTermination() { + boolean termination = false; + + if (jobs != null) + while (!termination) { + int count = 0; + for (Job job : jobs) + if (!job.live) + count++; + termination = (count >= jobs.size()); + } + } + + @Override + public synchronized void die() { + if (jobs != null) + for (Job job : jobs) { + job.live = false; + job.done = true; + } + } + + @Override + public String toString() { + String form = ""; + if (jobs != null) + form = jobs.getClass().getName(); + return " Pool=(" + name + ", " + size() + " " + form + ") "; + } + + @Override + public String getName() { + return name; + } + +} diff --git a/src/bilib/src/bilib/commons/job/runnable/PoolResponder.java b/src/bilib/src/bilib/commons/job/runnable/PoolResponder.java new file mode 100644 index 0000000..640c786 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/runnable/PoolResponder.java @@ -0,0 +1,24 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job.runnable; + +import bilib.commons.job.JobEvent; + +public interface PoolResponder { + + public abstract void onEvent(Pool pool, JobEvent event); + + public abstract void onSuccess(Pool pool, JobEvent event); + + public abstract void onFailure(Pool pool, JobEvent event); +} diff --git a/src/bilib/src/bilib/commons/job/runnable/RunnableDemo.java b/src/bilib/src/bilib/commons/job/runnable/RunnableDemo.java new file mode 100644 index 0000000..80a4340 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/runnable/RunnableDemo.java @@ -0,0 +1,301 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job.runnable; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import bilib.commons.job.ExecutionMode; +import bilib.commons.job.JobEvent; +import bilib.commons.job.MonitorAbstract; +import bilib.commons.job.MonitorProgressBar; +import bilib.commons.job.MonitorTimedLog; +import bilib.commons.job.MonitorTimedProgressBar; +import bilib.commons.job.runnable.Pool; + +public class RunnableDemo extends JFrame implements PoolResponder, ActionListener { + + public JRadioButton rbSequential = new JRadioButton("Sequential", false); + public JRadioButton rbAsynchronized = new JRadioButton("Parallel - Asynchronized jobs (not wait)", false); + public JRadioButton rbSynchronized = new JRadioButton("Parallel - Synchronized jobs (wait termination)", true); + + public JButton execute = new JButton("Execute"); + public JTextField nthreads = new JTextField("0"); + public JTextField njobs = new JTextField("10"); + public JButton simulateAbort = new JButton("Abort"); + public JButton simulateException = new JButton("Simulate an Exception"); + public JButton simulateJobException = new JButton("Simulate an JobException"); + public JButton simulateInterruption = new JButton("Simulate an Interruption"); + public JButton simulateCancellation = new JButton("Simulate an Cancellation"); + public JButton simulateTimeOut = new JButton("Simulate an TimeOut"); + + public MonitorTimedProgressBar bar = new MonitorTimedProgressBar(); + public MonitorProgressBar bar1 = new MonitorProgressBar(); + public MonitorTimedLog log = new MonitorTimedLog(10, 10); + + private double chrono; + private MainProcessing main; + + public static void main(String args[]) { + new RunnableDemo(); + } + + public RunnableDemo() { + super("Runnable Demo"); + main = new MainProcessing(this); + + ButtonGroup bg1 = new ButtonGroup(); + bg1.add(rbSequential); + bg1.add(rbAsynchronized); + bg1.add(rbSynchronized); + + JPanel panel1 = new JPanel(new GridLayout(20, 1)); + + panel1.add(rbSequential); + panel1.add(rbAsynchronized); + panel1.add(rbSynchronized); + + panel1.add(new JLabel("Number of threads (0 = nb of jobs)")); + panel1.add(nthreads); + panel1.add(new JLabel("Number of jobs")); + panel1.add(njobs); + panel1.add(execute); + panel1.add(new JLabel("")); + panel1.add(simulateAbort); + panel1.add(simulateException); + panel1.add(simulateJobException); + panel1.add(simulateInterruption); + panel1.add(simulateCancellation); + panel1.add(simulateTimeOut); + + JPanel panel3 = new JPanel(new BorderLayout()); + panel3.add(bar, BorderLayout.NORTH); + panel3.add(bar1, BorderLayout.SOUTH); + + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + panel.add(panel1, BorderLayout.NORTH); + panel.add(panel3, BorderLayout.SOUTH); + panel.add(log, BorderLayout.CENTER); + + execute.addActionListener(this); + simulateAbort.addActionListener(this); + simulateException.addActionListener(this); + simulateJobException.addActionListener(this); + simulateInterruption.addActionListener(this); + simulateCancellation.addActionListener(this); + simulateTimeOut.addActionListener(this); + getContentPane().add(panel); + pack(); + setVisible(true); + } + + private ExecutionMode getMode() { + ExecutionMode mode = ExecutionMode.MULTITHREAD_NO; + if (rbSynchronized.isSelected()) + mode = ExecutionMode.MULTITHREAD_SYNCHRONIZED; + if (rbAsynchronized.isSelected()) + mode = ExecutionMode.MULTITHREAD_ASYNCHRONIZED; + return mode; + } + + @Override + public void actionPerformed(ActionEvent event) { + + if (event.getSource() == execute) { + main.setMode(getMode(), Integer.parseInt(nthreads.getText()), Integer.parseInt(njobs.getText())); + System.out.println("\n\n"); + Thread thread = new Thread(main); + thread.start(); + chrono = System.nanoTime(); + } + + if (event.getSource() == simulateAbort) + main.simulateAbort(); + + if (event.getSource() == simulateException) + main.simulateException(); + + if (event.getSource() == simulateJobException) + main.simulateJobException(); + + if (event.getSource() == simulateInterruption) + main.simulateInterruption(); + + if (event.getSource() == simulateCancellation) + main.simulateCancellation(); + + if (event.getSource() == simulateTimeOut) + main.simulateTimeOut(); + } + + // + // MainProcessing + // + public class MainProcessing extends Job implements PoolResponder { + private ExecutionMode mode; + private int nthreads = 0; + private int njobs; + private PoolResponder responder; + private Pool pool; + private int niter = 15; + + public MainProcessing(PoolResponder responder) { + super(); + this.responder = responder; + } + + public void setMode(ExecutionMode mode, int nthreads, int njobs) { + this.mode = mode; + this.njobs = njobs; + this.nthreads = nthreads; + } + + @Override + public void process() { + bar.rewind(); + bar1.rewind(); + log.rewind(); + pool = new Pool("my", responder); + MyRunnable p1 = new MyRunnable(niter, bar); + p1.addMonitor(log); + p1.addMonitor(bar1); + pool.register(p1); + for (int i = 0; i < njobs; i++) + pool.register(new MyRunnable(niter, bar)); + pool.execute(mode, nthreads); + System.out.println("End of main"); + } + + public void simulateAbort() { + if (pool != null) + pool.getRegisteredJob(pool.size() - 1).abort(); + } + + public void simulateCancellation() { + if (pool != null) + pool.cancel(); + } + + public void simulateException() { + if (pool != null) + ((MyRunnable) pool.getRegisteredJob(pool.size() / 2 - 1)).withException = true; + } + + public void simulateJobException() { + if (pool != null) + ((MyRunnable) pool.getRegisteredJob(pool.size() - 1)).withJobException = true; + } + + public void simulateInterruption() { + if (pool != null) + ((MyRunnable) pool.getRegisteredJob(pool.size() - 1)).withInterruption = true; + } + + public void simulateTimeOut() { + if (pool != null) + ((MyRunnable) pool.getRegisteredJob(pool.size() - 1)).withTimeOut = true; + } + + @Override + public void onEvent(Pool pool, JobEvent event) { + responder.onEvent(pool, event); + } + + @Override + public void onSuccess(Pool pool, JobEvent event) { + responder.onSuccess(pool, event); + } + + @Override + public void onFailure(Pool pool, JobEvent event) { + responder.onFailure(pool, event); + } + + } + + // + // MyRunnable + // + public class MyRunnable extends Job { + public boolean withTimeOut = false; + public boolean withInterruption = false; + public boolean withException = false; + public boolean withJobException = false; + private int niter = 15; + + public MyRunnable(int niter, MonitorAbstract monitor) { + super(monitor); + this.niter = niter; + } + + @Override + public void process() { + int i = 0; + for (i = 0; i < niter && live; i++) { + increment(2, getName() + " " + live + " " + done); + for (int j = 0; j < 600000 && live; j++) + Math.round(Math.cos(Math.exp(Math.pow(j, -3)))); + if (withJobException) + throw new RuntimeException("It is a programming exception"); + if (withInterruption) + interrupt(); + if (withTimeOut) + this.setTimeOut(1000); + if (withException) { + double a[] = new double[2]; + a[3] = 1; + } + if (!live) + return; + if (!isNotTimeOut()) { + return; + } + } + System.out.println("End of " + getName() + " iterations:" + i + " " + live + " " + done); + } + + } + + @Override + public void onEvent(Pool pool, JobEvent event) { + if (event.getTypeEvent() == JobEvent.INTERRUPTED) + System.out.println("Main side: Job Event " + event); + } + + @Override + public void onSuccess(Pool pool, JobEvent event) { + System.out.println(">>> Success " + event + " from pool " + pool.getName()); + System.out.println("Computation time: " + (System.nanoTime() - chrono) / 1000000.0); + } + + @Override + public void onFailure(Pool pool, JobEvent event) { + System.out.println(">>> Failure " + event + " from pool " + pool.getName()); + if (event.getException() != null) + event.getException().printStackTrace(); + } + + +} diff --git a/src/bilib/src/bilib/commons/job/worker/Job.java b/src/bilib/src/bilib/commons/job/worker/Job.java new file mode 100644 index 0000000..f71a121 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/worker/Job.java @@ -0,0 +1,255 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job.worker; + +import java.util.ArrayList; + +import javax.swing.SwingWorker; + +import bilib.commons.job.JobAbstract; +import bilib.commons.job.JobEvent; +import bilib.commons.job.MonitorAbstract; +import bilib.commons.job.PoolAbstract; + +public abstract class Job extends SwingWorker<Object, String> implements JobAbstract, MonitorAbstract { + + protected boolean live = false; + protected boolean done = false; + protected boolean idle = false; + + private double chrono = -1; + private double timeoutns = -1; + private boolean stopTimeOut = false; + private ArrayList<MonitorAbstract> monitors = new ArrayList<MonitorAbstract>(); + + private PoolAbstract pool; + + public abstract Object process() throws RuntimeException; + + private String name; + + public Job() { + } + + public Job(MonitorAbstract monitor) { + monitors.add(monitor); + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public PoolAbstract getPool() { + return pool; + } + + @Override + public void setPool(PoolAbstract pool) { + this.pool = pool; + } + + @Override + public void setTimeOut(double timeoutms) { + this.timeoutns = timeoutms * 1000000.0; + this.stopTimeOut = true; + } + + @Override + public void abort() { + idle = false; + live = false; + done = false; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.ABORTED)); + } + + @Override + public void interrupt() { + idle = false; + live = false; + done = false; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.INTERRUPTED)); + } + + @Override + public void abort(String message) { + idle = false; + live = false; + done = false; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.ABORTED, message)); + } + + @Override + public void interrupt(String message) { + idle = false; + live = false; + done = false; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.INTERRUPTED, message)); + } + + @Override + public void init() { + idle = true; + live = true; + done = false; + chrono = System.nanoTime(); + } + + @Override + public Object doInBackground() { + Object o = null; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.STARTED)); + try { + idle = false; + o = process(); + if (live) { + done = true; + live = false; + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.COMPLETED)); + } + } + catch (RuntimeException ex) { + if (pool != null) + pool.fire(new JobEvent(this, ex, ex.getMessage())); + } + live = false; + idle = true; + return o; + } + + @Override + public boolean isNotTimeOut() { + if (!live) + return false; + if (stopTimeOut) + if ((System.nanoTime() - chrono) > timeoutns) { + if (pool != null) + pool.fire(new JobEvent(this, JobEvent.TIMEOUT)); + live = false; + done = false; + return false; + } + return true; + } + + @Override + public boolean isJobLive() { + return live; + } + + @Override + public boolean isJobDone() { + return done; + } + + @Override + public boolean isJobIdle() { + return idle; + } + + @Override + public boolean isJobProcessing() { + return !idle; + } + + @Override + public boolean isJobIncomplete() { + return !done; + } + + @Override + public String toString() { + return "JobWorker: " + getName(); + } + + @Override + public void rewind() { + for (MonitorAbstract monitor : monitors) + monitor.rewind(); + } + + @Override + public void print(String message) { + for (MonitorAbstract monitor : monitors) + monitor.print(message); + } + + @Override + public void error(String message) { + for (MonitorAbstract monitor : monitors) + monitor.error(message); + } + + @Override + public void warning(String message) { + for (MonitorAbstract monitor : monitors) + monitor.warning(message); + } + + @Override + public void progress(double percentage) { + for (MonitorAbstract monitor : monitors) + monitor.progress(percentage); + } + + @Override + public void progress(double percentage, String message) { + for (MonitorAbstract monitor : monitors) + monitor.progress(percentage, message); + } + + @Override + public void increment(double percentageIncrement) { + for (MonitorAbstract monitor : monitors) + monitor.increment(percentageIncrement); + } + + @Override + public void increment(double percentageIncrement, String message) { + for (MonitorAbstract monitor : monitors) + monitor.increment(percentageIncrement, message); + } + + @Override + public void addMonitor(MonitorAbstract monitor) { + monitors.add(monitor); + } + + @Override + public void removeMonitor(MonitorAbstract monitor) { + monitors.remove(monitor); + } + + @Override + public void clearMonitor() { + monitors.clear(); + } + + @Override + public ArrayList<MonitorAbstract> getMonitor() { + return monitors; + } + +} diff --git a/src/bilib/src/bilib/commons/job/worker/Pool.java b/src/bilib/src/bilib/commons/job/worker/Pool.java new file mode 100644 index 0000000..30fd246 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/worker/Pool.java @@ -0,0 +1,292 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job.worker; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import bilib.commons.job.ExecutionMode; +import bilib.commons.job.JobAbstract; +import bilib.commons.job.JobEvent; +import bilib.commons.job.PoolAbstract; +import bilib.commons.job.worker.Job; +import bilib.commons.job.worker.PoolResponder; + +public class Pool implements PoolAbstract { + + private ArrayList<Job> jobs; + protected String name; + protected PoolResponder responder; + + protected HashMap<String, Future<Object>> results; + + /** + * Create a new pool with one registered job (Worker). + * + * @param job + * the job to register + * @param responder + * the responder handling the job event + */ + public Pool(Job job, PoolResponder responder) { + jobs = new ArrayList<Job>(); + results = new HashMap<String, Future<Object>>(); + this.name = job.getName(); + this.responder = responder; + register(job); + } + + /** + * Create a new pool without any registered job (Worker). + * + * @param name + * name of the pool + * @param responder + * the responder handling the job event + */ + public Pool(String name, PoolResponder responder) { + jobs = new ArrayList<Job>(); + results = new HashMap<String, Future<Object>>(); + this.name = name; + this.responder = responder; + } + + @Override + public void fire(JobEvent event) { + + int t = event.getTypeEvent(); + if (responder != null) + responder.onEvent(this, event); + + if (t == JobEvent.EXCEPTION) { + die(); + if (responder != null) { + responder.onFailure(this, event); + } + } + else if (t == JobEvent.ABORTED) { + die(); + if (responder != null) + responder.onFailure(this, event); + } + else if (t == JobEvent.TIMEOUT) { + die(); + if (responder != null) + responder.onFailure(this, event); + } + else if (t == JobEvent.COMPLETED) { + if (isJobDone()) { + die(); + if (responder != null) + responder.onSuccess(this, event); + } + } + } + + @Override + public void fire(JobAbstract parent, JobEvent event) { + if (parent == null) + return; + if (parent.getPool() == null) + return; + if (event.getTypeEvent() == JobEvent.EXCEPTION) + parent.getPool().fire(new JobEvent(parent, event.getException(), event.getMessage())); + else + parent.getPool().fire(new JobEvent(parent, event.getTypeEvent(), event.getMessage())); + + } + + public ArrayList<Job> getRegisteredJobs() { + return jobs; + } + + public Job getRegisteredJob(int i) { + return jobs.get(i); + } + + @Override + public void execute(ExecutionMode mode) { + execute(mode, jobs.size()); + } + + @SuppressWarnings("unchecked") + @Override + public void execute(ExecutionMode mode, int nThreads) { + for (Job job : jobs) + job.init(); + if (mode == ExecutionMode.MULTITHREAD_NO) { + try { + for (Job job : jobs) + job.doInBackground(); + } + catch (Exception ex) { + ex.printStackTrace(); + } + } + else { + if (nThreads <= 0) + nThreads = jobs.size(); + ExecutorService executor = Executors.newFixedThreadPool(nThreads); + for (Job job : jobs) { + Future<?> future = executor.submit(job); + results.put(job.getName(), (Future<Object>) future); + } + executor.shutdown(); + if (mode == ExecutionMode.MULTITHREAD_SYNCHRONIZED) + waitTermination(); + } + } + + @Override + public Future<Object> getFuture(String jobName) { + if (jobs != null) + for (int i = 0; i < jobs.size(); i++) { + if (jobs.get(i).getName().equals(jobName)) + return results.get(jobs.get(i).getName()); + } + return null; + } + + @Override + public int size() { + int s = 0; + if (jobs != null) + s += jobs.size(); + return s; + } + + @Override + public void cancel() { + if (jobs != null) + for (Job job : jobs) + results.get(job.getName()).cancel(true); + } + + @Override + public void register(JobAbstract job) { + if (jobs == null) + return; + if (job instanceof Job) + if (jobs.add((Job) job)) { + job.setName(name + "-" + jobs.size()); + job.setPool(this); + } + } + + @Override + public void register(JobAbstract job, double timeoutms) { + if (jobs == null) + return; + if (job instanceof Job) + if (jobs.add((Job) job)) { + job.setTimeOut(timeoutms); + job.setName(name + "-" + jobs.size()); + job.setPool(this); + } + } + + @Override + public void unregister(JobAbstract job) { + if (jobs == null) + return; + if (job instanceof Job) { + jobs.remove((Job) job); + job.setPool(null); + } + } + + @Override + public void unregisterAll() { + if (jobs != null) + jobs.clear(); + } + + @Override + public boolean isJobDone() { + int count = 0; + if (jobs != null) { + for (Job job : jobs) { + if (job.done) + count++; + } + return (count == jobs.size()); + } + return false; + } + + @Override + public boolean isJobLive() { + int count = 0; + if (jobs != null) { + for (Job job : jobs) { + if (job.live) + count++; + } + return (count == jobs.size()); + } + return false; + } + + @Override + public boolean isJobIdle() { + int count = 0; + if (jobs != null) { + for (Job job : jobs) { + if (job.idle) + count++; + } + return (count == jobs.size()); + } + return false; + } + + @Override + public void waitTermination() { + boolean termination = false; + + if (jobs != null) + while (!termination) { + int count = 0; + for (Job job : jobs) + if (!job.live) + count++; + termination = (count >= jobs.size()); + } + } + + @Override + public synchronized void die() { + if (jobs != null) + for (Job job : jobs) { + job.live = false; + job.done = true; + } + } + + @Override + public String toString() { + String form = ""; + if (jobs != null) + form = jobs.getClass().getName(); + return " Pool=(" + name + ", " + size() + " " + form + ") "; + } + + @Override + public String getName() { + return name; + } + +} diff --git a/src/bilib/src/bilib/commons/job/worker/PoolResponder.java b/src/bilib/src/bilib/commons/job/worker/PoolResponder.java new file mode 100644 index 0000000..b5c23ad --- /dev/null +++ b/src/bilib/src/bilib/commons/job/worker/PoolResponder.java @@ -0,0 +1,24 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job.worker; + +import bilib.commons.job.JobEvent; + +public interface PoolResponder { + + public abstract void onEvent(Pool pool, JobEvent event); + + public abstract void onSuccess(Pool pool, JobEvent event); + + public abstract void onFailure(Pool pool, JobEvent event); +} diff --git a/src/bilib/src/bilib/commons/job/worker/WorkerDemo.java b/src/bilib/src/bilib/commons/job/worker/WorkerDemo.java new file mode 100644 index 0000000..4a76d03 --- /dev/null +++ b/src/bilib/src/bilib/commons/job/worker/WorkerDemo.java @@ -0,0 +1,303 @@ +//==================================================================================================== +// Project: Bilib Job +// +// Authors: Daniel Sage +// Organization: Biomedical Imaging Group (BIG), Ecole Polytechnique Federale de Lausanne +// Address: EPFL-STI-IMT-LIB, 1015 Lausanne, Switzerland +// +// Conditions of use: +// You'll be free to use this software for research purposes, but you should not redistribute +// it without our consent. In addition, we expect you to include a citation whenever you +// present or publish results that are based on it. +//==================================================================================================== +package bilib.commons.job.worker; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import bilib.commons.job.ExecutionMode; +import bilib.commons.job.JobEvent; +import bilib.commons.job.MonitorAbstract; +import bilib.commons.job.MonitorProgressBar; +import bilib.commons.job.MonitorTimedLog; +import bilib.commons.job.MonitorTimedProgressBar; +import bilib.commons.job.worker.PoolResponder; +import bilib.commons.job.worker.Job; + +public class WorkerDemo extends JFrame implements PoolResponder, ActionListener { + + public JRadioButton rbSequential = new JRadioButton("Sequential", false); + public JRadioButton rbAsynchronized = new JRadioButton("Parallel - Asynchronized jobs (not wait)", false); + public JRadioButton rbSynchronized = new JRadioButton("Parallel - Synchronized jobs (wait termination)", true); + + public JButton execute = new JButton("Execute"); + public JTextField nthreads = new JTextField("0"); + public JTextField njobs = new JTextField("10"); + public JButton simulateAbort = new JButton("Abort"); + public JButton simulateException = new JButton("Simulate an Exception"); + public JButton simulateJobException = new JButton("Simulate an JobException"); + public JButton simulateInterruption = new JButton("Simulate an Interruption"); + public JButton simulateCancellation = new JButton("Simulate an Cancellation"); + public JButton simulateTimeOut = new JButton("Simulate an TimeOut"); + + public MonitorTimedProgressBar bar = new MonitorTimedProgressBar(); + public MonitorProgressBar bar1 = new MonitorProgressBar(); + public MonitorTimedLog log = new MonitorTimedLog(10, 10); + + private double chrono; + private MainProcessing main; + + public static void main(String args[]) { + new WorkerDemo(); + } + + public WorkerDemo() { + super("Worker Demo"); + main = new MainProcessing(this); + + ButtonGroup bg1 = new ButtonGroup(); + bg1.add(rbSequential); + bg1.add(rbAsynchronized); + bg1.add(rbSynchronized); + + JPanel panel1 = new JPanel(new GridLayout(20, 1)); + + panel1.add(rbSequential); + panel1.add(rbAsynchronized); + panel1.add(rbSynchronized); + + panel1.add(new JLabel("Number of threads (0 = nb of jobs)")); + panel1.add(nthreads); + panel1.add(new JLabel("Number of jobs")); + panel1.add(njobs); + panel1.add(execute); + panel1.add(new JLabel("")); + panel1.add(simulateAbort); + panel1.add(simulateException); + panel1.add(simulateJobException); + panel1.add(simulateInterruption); + panel1.add(simulateCancellation); + panel1.add(simulateTimeOut); + + JPanel panel3 = new JPanel(new BorderLayout()); + panel3.add(bar, BorderLayout.NORTH); + panel3.add(bar1, BorderLayout.SOUTH); + + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + panel.add(panel1, BorderLayout.NORTH); + panel.add(panel3, BorderLayout.SOUTH); + panel.add(log, BorderLayout.CENTER); + + execute.addActionListener(this); + simulateAbort.addActionListener(this); + simulateException.addActionListener(this); + simulateJobException.addActionListener(this); + simulateInterruption.addActionListener(this); + simulateCancellation.addActionListener(this); + simulateTimeOut.addActionListener(this); + getContentPane().add(panel); + pack(); + setVisible(true); + } + + private ExecutionMode getMode() { + ExecutionMode mode = ExecutionMode.MULTITHREAD_NO; + if (rbSynchronized.isSelected()) + mode = ExecutionMode.MULTITHREAD_SYNCHRONIZED; + if (rbAsynchronized.isSelected()) + mode = ExecutionMode.MULTITHREAD_ASYNCHRONIZED; + return mode; + } + + @Override + public void actionPerformed(ActionEvent event) { + + if (event.getSource() == execute) { + main.setMode(getMode(), Integer.parseInt(nthreads.getText()), Integer.parseInt(njobs.getText())); + System.out.println("\n\n"); + Pool pool = new Pool("Main", this); + pool.register(main); + pool.execute(ExecutionMode.MULTITHREAD_ASYNCHRONIZED); + chrono = System.nanoTime(); + } + + if (event.getSource() == simulateAbort) + main.simulateAbort(); + + if (event.getSource() == simulateException) + main.simulateException(); + + if (event.getSource() == simulateJobException) + main.simulateJobException(); + + if (event.getSource() == simulateInterruption) + main.simulateInterruption(); + + if (event.getSource() == simulateCancellation) + main.simulateCancellation(); + + if (event.getSource() == simulateTimeOut) + main.simulateTimeOut(); + } + + // + // MainProcessing + // + public class MainProcessing extends Job implements PoolResponder { + private ExecutionMode mode; + private int nthreads = 0; + private int njobs; + private PoolResponder responder; + private Pool pool; + private int niter = 15; + + public MainProcessing(PoolResponder responder) { + super(); + this.responder = responder; + } + + public void setMode(ExecutionMode mode, int nthreads, int njobs) { + this.mode = mode; + this.njobs = njobs; + this.nthreads = nthreads; + } + + @Override + public Object process() { + bar.rewind(); + bar1.rewind(); + log.rewind(); + + pool = new Pool("my", responder); + MyWorker p1 = new MyWorker(niter, bar); + p1.addMonitor(log); + p1.addMonitor(bar1); + pool.register(p1); + for (int i = 0; i < njobs; i++) + pool.register(new MyWorker(niter, bar)); + pool.execute(mode, nthreads); + + System.out.println("End of main"); + return "End"; + + } + + public void simulateAbort() { + if (pool != null) + pool.getRegisteredJob(pool.size() - 1).abort(); + } + + public void simulateCancellation() { + if (pool != null) + pool.cancel(); + } + + public void simulateException() { + if (pool != null) + ((MyWorker) pool.getRegisteredJob(pool.size() / 2 - 1)).withException = true; + } + + public void simulateJobException() { + if (pool != null) + ((MyWorker) pool.getRegisteredJob(pool.size() - 1)).withJobException = true; + } + + public void simulateInterruption() { + if (pool != null) + ((MyWorker) pool.getRegisteredJob(pool.size() - 1)).withInterruption = true; + } + + public void simulateTimeOut() { + if (pool != null) + ((MyWorker) pool.getRegisteredJob(pool.size() - 1)).withTimeOut = true; + } + + @Override + public void onEvent(Pool pool, JobEvent event) { + responder.onEvent(pool, event); + } + + @Override + public void onSuccess(Pool pool, JobEvent event) { + responder.onSuccess(pool, event); + } + + @Override + public void onFailure(Pool pool, JobEvent event) { + responder.onFailure(pool, event); + } + + } + + // + // MyWorker + // + public class MyWorker extends Job { + public boolean withTimeOut = false; + public boolean withInterruption = false; + public boolean withException = false; + public boolean withJobException = false; + private int niter = 15; + + public MyWorker(int niter, MonitorAbstract monitor) { + super(monitor); + this.niter = niter; + } + + @Override + public Object process() throws RuntimeException { + int i = 0; + for (i = 0; i < niter && live; i++) { + increment(2, getName() + " " + live + " " + done); + for (int j = 0; j < 600000 && live; j++) + Math.round(Math.cos(Math.exp(Math.pow(j, -3)))); + if (withJobException) + throw new RuntimeException("It is a programming exception"); + if (withInterruption) + interrupt(); + if (withTimeOut) + niter += 2; + if (withException) { + double a[] = new double[2]; + a[3] = 1; + } + if (!live) + return "KO"; + } + System.out.println("End of " + getName() + " iterations:" + i + " " + live + " " + done); + return "End of " + getName() + " iterations:" + i + " " + live + " " + done; + } + } + + @Override + public void onEvent(Pool pool, JobEvent event) { + if (event.getTypeEvent() == JobEvent.INTERRUPTED) + System.out.println("Main side: Job Event " + event); + } + + @Override + public void onSuccess(Pool pool, JobEvent event) { + System.out.println(">>> Success " + event + " from pool " + pool.getName()); + System.out.println("Computation time: " + (System.nanoTime() - chrono) / 1000000.0); + } + + @Override + public void onFailure(Pool pool, JobEvent event) { + System.out.println(">>> Failure " + event + " from pool " + pool.getName()); + if (event.getException() != null) + event.getException().printStackTrace(); + } + +} diff --git a/src/bilib/src/bilib/commons/math/.DS_Store b/src/bilib/src/bilib/commons/math/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c9486404a74a7352f8b49aa3dd1e300876d9d298 GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8z3~dZp0Z1N%F(jFwA|RR(Y(^?WPD*idPEvk;4pfd1 z1=7pLki?M6P|Q%wfTo|5p`0O;A&()2As@<0hZ)aE$ly`=(GVC7fzc2c4S~@R7!3ie zAposIIk>UPjtY*3z-S1J<PZRr4+@~Z0s{ks1C(xn&>$%WMg|5@^BqL9z`_aA4`2X^ zfwY2XkX8^4(#pUHVu8&7Yh_?$fM{g|cSAt>KwT0L4c5-U2(}r-2Ww|w1l!EOzzEUK zzzEUKzzFT3FhaC5FhaC5FoJD|xo(sm4S~@RfQ0}vgdqT`|6LgvaP|Kosz%At5Eu=C zVHpC9EH1$=PT)!zyZ=CSEvP<CfT}qFRmKRammy*xC7`MuTpcq(1{Ecs%0S{E?I0Rl Y9Wyd8fNS&7#sDmYM(NQIpl=8O07Us5y8r+H literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/commons/math/bessel/Bessel.java b/src/bilib/src/bilib/commons/math/bessel/Bessel.java new file mode 100644 index 0000000..c0bb987 --- /dev/null +++ b/src/bilib/src/bilib/commons/math/bessel/Bessel.java @@ -0,0 +1,82 @@ +package bilib.commons.math.bessel; + +/** + * This class evaluates the Bessel function J0(x). It uses the polynomial + * approximations on p. 369-70 of Abramowitz & Stegun. The error in J0 is + * supposed to be less than or equal to 5 x 10^-8. The error in J1 is supposed + * to be less than or equal to 4 x 10^-8, relative to the value of x. The error + * in J2 depends on the error of J0 and J1, as J2 = 2*J1/x + J0. + * + * @author Hagai Kirshner, Biomedical Imaging Group, Ecole Polytechnique + * Federale de Lausanne (EPFL) + */ + +public class Bessel { + + /** + * Constants for Bessel function approximation according Abramowitz & Stegun + */ + private static double[] tJ0 = { 1.0, -2.2499997, 1.2656208, -0.3163866, 0.0444479, -0.0039444, 0.0002100 }; + private static double[] pJ0 = { -.78539816, -.04166397, -.00003954, 0.00262573, -.00054125, -.00029333, .00013558 }; + private static double[] fJ0 = { .79788456, -0.00000077, -.00552740, -.00009512, 0.00137237, -0.00072805, 0.00014476 }; + + private static double[] tJ1 = { 0.5, -0.56249985, 0.21093573, -0.03954289, 0.0443319, -0.00031761, 0.0001109 }; + private static double[] pJ1 = { -2.35619449, 0.12499612, 0.00005650, -0.00637879, 0.00074348, 0.00079824, -0.00029166 }; + private static double[] fJ1 = { 0.79788456, 0.00000156, 0.01689667, 0.00017105, -0.00249511, 0.00113653, -0.00020033 }; + + /** + * Returns the value of the Bessel function of the first kind of order zero + * (J0) at x. + */ + public static double J0(double x) { + if (x < 0.0) + x *= -1.0; + if (x <= 3.0) { + double y = x * x / 9.0; + return tJ0[0] + y * (tJ0[1] + y * (tJ0[2] + y * (tJ0[3] + y * (tJ0[4] + y * (tJ0[5] + y * tJ0[6]))))); + } + + double y = 3.0 / x; + double theta0 = x + pJ0[0] + y * (pJ0[1] + y * (pJ0[2] + y * (pJ0[3] + y * (pJ0[4] + y * (pJ0[5] + y * pJ0[6]))))); + double f0 = fJ0[0] + y * (fJ0[1] + y * (fJ0[2] + y * (fJ0[3] + y * (fJ0[4] + y * (fJ0[5] + y * fJ0[6]))))); + return Math.sqrt(1.0 / x) * f0 * Math.cos(theta0); + } + + /** + * Returns the value of the Bessel function of the first kind of order one + * (J1) at x. + */ + public static double J1(double x) { + + int sign = 1; + + if (x < 0.0) { + x *= -1.0; + sign = -1; + } + + if (x <= 3.0) { + double y = x * x / 9.0; + return sign * x * (tJ1[0] + y * (tJ1[1] + y * (tJ1[2] + y * (tJ1[3] + y * (tJ1[4] + y * (tJ1[5] + y * tJ1[6])))))); + } + + double y = 3.0 / x; + double theta1 = x + pJ1[0] + y * (pJ1[1] + y * (pJ1[2] + y * (pJ1[3] + y * (pJ1[4] + y * (pJ1[5] + y * pJ1[6]))))); + double f1 = fJ1[0] + y * (fJ1[1] + y * (fJ1[2] + y * (fJ1[3] + y * (fJ1[4] + y * (fJ1[5] + y * fJ1[6]))))); + return sign * Math.sqrt(1.0 / x) * f1 * Math.cos(theta1); + } + + /** + * Returns the value of the Bessel function of the first kind of order two + * (J2) at x. + */ + public static double J2(double x) { + + double value0 = J0(x); + double value1 = J1(x); + if (x == 0.0) + return 0.0; + else + return 2.0 * value1 / x + value0; + } +} \ No newline at end of file diff --git a/src/bilib/src/bilib/commons/math/windowing/Windowing.java b/src/bilib/src/bilib/commons/math/windowing/Windowing.java new file mode 100644 index 0000000..de517f2 --- /dev/null +++ b/src/bilib/src/bilib/commons/math/windowing/Windowing.java @@ -0,0 +1,177 @@ +package bilib.commons.math.windowing; + +public class Windowing { + + static public double rectangle(double x, double N) { + return elliptical(x, N); + } + + static public double elliptical(double x, double N) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + return 1.0; + } + + static public double bartlett(double x, double N) { + return triangle(x, N); + } + + static public double triangle(double x, double N) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + return 1.0 - Math.abs(2.0 * x / N); + } + + static public double gaussian(double x, double N, double sigma) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + return Math.exp(-0.5 * x * x / (sigma * sigma)); + } + + static public double gaussian(double x, double N) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + double sigma = N / 3.0; + return Math.exp(-0.5 * x * x / (sigma * sigma)); + } + + static public double supergaussian(double x, double N, double sigma) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + return Math.exp(-0.5 * x * x * x * x * x * x / (sigma * sigma)); + } + + static public double supergaussian(double x, double N) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + double sigma = N / 3.0; + return Math.exp(-0.5 * x * x * x * x * x * x / (sigma * sigma)); + } + + static public double cosine(double x, double N) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + double t = 2.0 * x / N; + return Math.cos(Math.PI * 0.5 * t); + } + + static public double raisedcosine(double x, double N, double power) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + double t = 2.0 * x / N; + return Math.pow(Math.cos(Math.PI * 0.5 * t), power); + } + + static public double cubicspline(double x, double N) { + double t = 2.0 * x / (0.5 * N); + if (t < -2.0) + return 0.0; + if (t < -1.0) + return 0.25 * (t + 2.0) * (t + 2.0) * (t + 2.0); + if (t < 0.0) + return 0.25 * (-3.0 * t * t * t - 6.0 * t * t + 4.0); + if (t < 1.0) + return 0.25 * (3.0 * t * t * t - 6.0 * t * t + 4.0); + if (t < 2.0) + return 0.25 * (2.0 - t) * (2.0 - t) * (2.0 - t); + return 0; + } + + static public double parzen(double x, double N) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + double t = 2.0 * (x < 0 ? -x : x) / N; + if (t < 0.5) + return 1.0 - 6.0 * t * t + 6.0 * t * t * t; + if (t < 1.0) + return 2.0 * (1.0 - t) * (1.0 - t) * (1.0 - t); + return 0; + } + + static public double sigmoid(double x, double N, double slope) { + return 1.0 / (1.0 + Math.exp(-slope * (x + N * 0.5))) - 1.0 / (1.0 + Math.exp(-slope * (x - N * 0.5))); + } + + static public double hann(double x, double N) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + double t = 2.0 * x / N; + return Math.cos(0.5 * Math.PI * t); + } + + static public double hamming(double x, double N, double alpha) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + double t = 2.0 * x / N; + return alpha + (1.0 - alpha) * Math.cos(Math.PI * t); + } + + static public double tukey(double x, double N, double alpha) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + if (x > -0.5 * N + alpha && x < 0.5 * N - alpha) + return 1.0; + return Math.cos(Math.PI * ((2 * x - N) / (4 * alpha) + 0.5)); + } + + static public double exponential(double x, double N) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + double k = 4.60517018599 / N; + return Math.exp(-((x < 0 ? -x : x)) * k); + } + + static public double lanczos(double x, double N) { + if (x < -0.5 * N) + return 0; + if (x > 0.5 * N) + return 0; + double t = 2.0 * x / N; + if (t == 0) + return 1.0; + return Math.sin(Math.PI * t) / (Math.PI * t); + } + + public static void main(String args[]) { + + double N = 20; + + String title = "t\trectangle\ttriangle\tgaussian3\tgaussian\tsupergaussian3\tsupergaussian\tcosine\traisedcosine\tcubicspline\tparzen\tsigmoid\thann\thamming\ttukey\texponential\tlanczos"; + System.out.println(title); + for (double x = -25; x < 25; x += 0.1) { + double[] results = new double[] { x, rectangle(x, N), triangle(x, N), gaussian(x, N, 3), gaussian(x, N), supergaussian(x, N, 3), supergaussian(x, N), cosine(x, N), + raisedcosine(x, N, 6), cubicspline(x, N), parzen(x, N), sigmoid(x, N, 10), hann(x, N), hamming(x, N, 0.5), tukey(x, N, 2), exponential(x, N), lanczos(x, N) }; + String s = ""; + for (int i = 0; i < results.length; i++) + s += String.format("%2.3f\t ", results[i]); + System.out.println(s); + } + } + +} diff --git a/src/bilib/src/bilib/commons/random/Noise.java b/src/bilib/src/bilib/commons/random/Noise.java new file mode 100644 index 0000000..e33bf7a --- /dev/null +++ b/src/bilib/src/bilib/commons/random/Noise.java @@ -0,0 +1,29 @@ +package bilib.commons.random; + +import java.util.Random; + +import javax.swing.JPanel; + +import bilib.commons.settings.Settings; + +public abstract class Noise { + + public Random random = new Random(); + + public abstract void fetchParameters(); + + public abstract String getName(); + + public abstract double nextValue(); + + public abstract JPanel buildPanel(Settings settings); + + public void load(Settings settings) { + settings.loadRecordedItems(); + } + + public void store(Settings settings) { + settings.storeRecordedItems(); + } + +} diff --git a/src/bilib/src/bilib/commons/random/NoiseExponential.java b/src/bilib/src/bilib/commons/random/NoiseExponential.java new file mode 100644 index 0000000..ecb1be2 --- /dev/null +++ b/src/bilib/src/bilib/commons/random/NoiseExponential.java @@ -0,0 +1,50 @@ +package bilib.commons.random; + +import javax.swing.JPanel; + +import bilib.commons.components.GridPanel; +import bilib.commons.components.SpinnerRangeDouble; +import bilib.commons.settings.Settings; + +public class NoiseExponential extends Noise { + + private double mean = 0.0; + private double stdev = 1.0; + private SpinnerRangeDouble spnMean = new SpinnerRangeDouble(0, -Double.MAX_VALUE, Double.MAX_VALUE, 1, "0.000"); + private SpinnerRangeDouble spnStd = new SpinnerRangeDouble(1, 0, Double.MAX_VALUE, 0.5, "0.000"); + + public NoiseExponential(double mean, double stdev) { + this.mean = mean; + this.stdev = stdev; + } + + @Override + public String getName() { + return "Exponential"; + } + + @Override + public void fetchParameters() { + this.mean = spnMean.get(); + this.stdev = spnStd.get(); + } + + @Override + public double nextValue() { + return mean + stdev * random.nextGaussian(); + } + + @Override + public JPanel buildPanel(Settings settings) { + GridPanel panel = new GridPanel(false); + panel.place(1, 0, "Mean"); + panel.place(1, 1, spnMean); + panel.place(2, 0, "Standard deviation"); + panel.place(2, 1, spnStd); + settings.record("random-" + getName() + "-mean", spnMean, "0"); + settings.record("random-" + getName() + "-stdev", spnStd, "1"); + settings.loadRecordedItems(); + return panel; + } + +} diff --git a/src/bilib/src/bilib/commons/random/NoiseGaussian.java b/src/bilib/src/bilib/commons/random/NoiseGaussian.java new file mode 100644 index 0000000..f8d4b2a --- /dev/null +++ b/src/bilib/src/bilib/commons/random/NoiseGaussian.java @@ -0,0 +1,50 @@ +package bilib.commons.random; + +import javax.swing.JPanel; + +import bilib.commons.components.GridPanel; +import bilib.commons.components.SpinnerRangeDouble; +import bilib.commons.settings.Settings; + +public class NoiseGaussian extends Noise { + + private double mean = 0.0; + private double stdev = 1.0; + private SpinnerRangeDouble spnMean = new SpinnerRangeDouble(0, -Double.MAX_VALUE, Double.MAX_VALUE, 1); + private SpinnerRangeDouble spnStd = new SpinnerRangeDouble(1, 0, Double.MAX_VALUE, 0.5); + + public NoiseGaussian(double mean, double stdev) { + this.mean = mean; + this.stdev = stdev; + } + + @Override + public String getName() { + return "Gaussian"; + } + + @Override + public void fetchParameters() { + this.mean = spnMean.get(); + this.stdev = spnStd.get(); + } + + @Override + public double nextValue() { + return mean + stdev * random.nextGaussian(); + } + + @Override + public JPanel buildPanel(Settings settings) { + GridPanel panel = new GridPanel(false); + panel.place(1, 0, "Mean"); + panel.place(1, 1, spnMean); + panel.place(2, 0, "Standard deviation"); + panel.place(2, 1, spnStd); + settings.record("random-" + getName() + "-mean", spnMean, "0"); + settings.record("random-" + getName() + "-stdev", spnStd, "1"); + settings.loadRecordedItems(); + return panel; + } + +} diff --git a/src/bilib/src/bilib/commons/random/NoisePoisson.java b/src/bilib/src/bilib/commons/random/NoisePoisson.java new file mode 100644 index 0000000..930d666 --- /dev/null +++ b/src/bilib/src/bilib/commons/random/NoisePoisson.java @@ -0,0 +1,98 @@ +package bilib.commons.random; + +import javax.swing.JPanel; + +import bilib.commons.components.GridPanel; +import bilib.commons.components.SpinnerRangeDouble; +import bilib.commons.settings.Settings; + +public class NoisePoisson extends Noise { + + private double mean = 0.0; + private SpinnerRangeDouble spnMean = new SpinnerRangeDouble(1, -Double.MAX_VALUE, Double.MAX_VALUE, 1, "0.000"); + + public NoisePoisson(double mean) { + this.mean = mean; + } + + @Override + public String getName() { + return "Poisson"; + } + + @Override + public void fetchParameters() { + this.mean = spnMean.get(); + } + + @Override + public double nextValue() { + return poidev(mean); + } + + @Override + public JPanel buildPanel(Settings settings) { + GridPanel panel = new GridPanel(false); + panel.place(1, 0, "Mean/Variance"); + panel.place(1, 1, spnMean); + settings.record("random-" + getName() + "-mean", spnMean, "1"); + settings.loadRecordedItems(); + return panel; + } + + private double poidev(double xm) { + + double sq = 0, alxm = 0, g = 0, oldm = (-1.0); + double em, t, y; + if (xm < 12.0) { + if (xm != oldm) { + oldm = xm; + g = Math.exp(-xm); + } + em = -1; + t = 1.0; + do { + ++em; + t *= random.nextDouble(); + ; + } + while (t > g); + } + else { + if (xm != oldm) { + oldm = xm; + sq = Math.sqrt(2.0 * xm); + alxm = Math.log(xm); + g = xm * alxm - gammln(xm + 1.0); + } + do { + do { + y = Math.tan(Math.PI * random.nextDouble()); + em = sq * y + xm; + } + while (em < 0.0); + em = Math.floor(em); + t = 0.9 * (1.0 + y * y) * Math.exp(em * alxm - gammln(em + 1.0) - g); + + } + while (random.nextDouble() > t); + } + return em; + } + + // Internal arithmetic will be done in double precision, a nicety that you + // can omit if five-figure + // accuracy is good enough. + double gammln(double xx) { + double x, y, tmp, ser; + double cof[] = new double[] { 76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5 }; + int j; + y = x = xx; + tmp = x + 5.5; + tmp -= (x + 0.5) * Math.log(tmp); + ser = 1.000000000190015; + for (j = 0; j <= 5; j++) + ser += cof[j] / ++y; + return -tmp + Math.log(2.5066282746310005 * ser / x); + } +} diff --git a/src/bilib/src/bilib/commons/random/NoiseRayleigh.java b/src/bilib/src/bilib/commons/random/NoiseRayleigh.java new file mode 100644 index 0000000..c547f63 --- /dev/null +++ b/src/bilib/src/bilib/commons/random/NoiseRayleigh.java @@ -0,0 +1,43 @@ +package bilib.commons.random; + +import javax.swing.JPanel; + +import bilib.commons.components.GridPanel; +import bilib.commons.components.SpinnerRangeDouble; +import bilib.commons.settings.Settings; + +public class NoiseRayleigh extends Noise { + + private double sigma = 1.0; + private SpinnerRangeDouble spnSigma = new SpinnerRangeDouble(1, -Double.MAX_VALUE, Double.MAX_VALUE, 1); + + public NoiseRayleigh(double sigma) { + this.sigma = sigma; + } + + @Override + public String getName() { + return "Rayleigh"; + } + + @Override + public void fetchParameters() { + this.sigma = spnSigma.get(); + } + + @Override + public double nextValue() { + return Math.sqrt(-2.0D * Math.log(1.0D - random.nextDouble())) * sigma; + } + + @Override + public JPanel buildPanel(Settings settings) { + GridPanel panel = new GridPanel(false); + panel.place(1, 0, "Sigma"); + panel.place(1, 1, spnSigma); + settings.record("random-" + getName() + "-sigma", spnSigma, "1"); + settings.loadRecordedItems(); + return panel; + } + +} diff --git a/src/bilib/src/bilib/commons/random/NoiseUniform.java b/src/bilib/src/bilib/commons/random/NoiseUniform.java new file mode 100644 index 0000000..f3f1fbb --- /dev/null +++ b/src/bilib/src/bilib/commons/random/NoiseUniform.java @@ -0,0 +1,51 @@ +package bilib.commons.random; + +import javax.swing.JPanel; + +import bilib.commons.components.GridPanel; +import bilib.commons.components.SpinnerRangeDouble; +import bilib.commons.settings.Settings; + +public class NoiseUniform extends Noise { + + private double lower = 0.0; + private double upper = 1.0; + private SpinnerRangeDouble spnLower = new SpinnerRangeDouble(0.0, -Double.MAX_VALUE, Double.MAX_VALUE, 1, "0.000"); + private SpinnerRangeDouble spnUpper = new SpinnerRangeDouble(1.0, -Double.MAX_VALUE, Double.MAX_VALUE, 1, "0.000"); + + public NoiseUniform(double lower, double upper) { + this.lower = lower; + this.upper = upper; + } + + @Override + public String getName() { + return "Uniform"; + } + + @Override + public void fetchParameters() { + this.lower = spnLower.get(); + this.upper = spnUpper.get(); + } + + @Override + public double nextValue() { + return (upper - lower) * random.nextDouble() + upper; + } + + @Override + public JPanel buildPanel(Settings settings) { + GridPanel panel = new GridPanel(false); + panel.place(1, 0, "Lower value"); + panel.place(1, 1, spnLower); + panel.place(2, 0, "Upper value"); + panel.place(2, 1, spnUpper); + settings.record("random-" + getName() + "-lower", spnLower, "0"); + settings.record("random-" + getName() + "-upper", spnUpper, "1"); + settings.loadRecordedItems(); + + return panel; + } + +} diff --git a/src/bilib/src/bilib/commons/settings/Settings.java b/src/bilib/src/bilib/commons/settings/Settings.java new file mode 100644 index 0000000..489b452 --- /dev/null +++ b/src/bilib/src/bilib/commons/settings/Settings.java @@ -0,0 +1,430 @@ +package bilib.commons.settings; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.Properties; + +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JSlider; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.JToggleButton; + +import bilib.commons.components.SpinnerRangeDouble; +import bilib.commons.components.SpinnerRangeFloat; +import bilib.commons.components.SpinnerRangeInteger; + +/** + * This class allows to store and load key-associated values in a text file. The + * class has methods to load and store single value linked to a string key + * describing the value. Furthermore, this class has methods to record a GUI + * component to a specified key. By this way this class allows to load and store + * all recorded items. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class Settings { + + private String filename; + private String project; + private ArrayList<Item> items; + private Properties props; + + /** + * Constructors a Settings abject for a given project name and a given + * filename. + * + * @param project + * a string describing the project + * @param filename + * a string give the full name of the file, including the path + */ + public Settings(String project, String filename) { + this.filename = filename; + this.project = project; + items = new ArrayList<Item>(); + props = new Properties(); + } + + public String getProject() { + return project; + } + + public String getFilename() { + return filename; + } + + /** + * Records a JTextField component to store/load automatically. + * + * @param key + * a string describing the value + * @param component + * the component to record + * @param defaultValue + * the default value + */ + public void record(String key, JTextField component, String defaultValue) { + Item item = new Item(key, component, defaultValue); + items.add(item); + } + + /** + * Records a JComboBox component to store/load automatically. + * + * @param key + * a string describing the value + * @param component + * the component to record + * @param defaultValue + * the default value + */ + public void record(String key, JComboBox component, String defaultValue) { + Item item = new Item(key, component, defaultValue); + items.add(item); + } + + /** + * Records a JSpinner component to store/load automatically. + * + * @param key + * a string describing the value + * @param component + * the component to record + * @param defaultValue + * the default value + */ + public void record(String key, JSpinner component, String defaultValue) { + Item item = new Item(key, component, defaultValue); + items.add(item); + } + + /** + * Records a JToggleButton component to store/load automatically. + * + * @param key + * a string describing the value + * @param component + * the component to record + * @param defaultValue + * the default value + */ + public void record(String key, JToggleButton component, boolean defaultValue) { + Item item = new Item(key, component, (defaultValue ? "on" : "off")); + items.add(item); + } + + /** + * Records a JCheckBox component to store/load automatically. + * + * @param key + * a string describing the value + * @param component + * the component to record + * @param defaultValue + * the default value + */ + public void record(String key, JCheckBox component, boolean defaultValue) { + Item item = new Item(key, component, (defaultValue ? "on" : "off")); + items.add(item); + } + + /** + * Records a JSlider component to store/load automatically. + * + * @param key + * a int value + * @param component + * the component to record + * @param defaultValue + * the default value + */ + public void record(String key, JSlider component, String defaultValue) { + Item item = new Item(key, component, defaultValue); + items.add(item); + } + + /** + * Load an individual double value given a specified key + * + * @param key + * a string describing the value + * @param defaultValue + * the default value + * @return the value get from the file + */ + public String loadValue(String key, String defaultValue) { + String s = ""; + try { + FileInputStream in = new FileInputStream(filename); + props.load(in); + s = props.getProperty(key, "" + defaultValue); + } + catch (Exception e) { + s = defaultValue; + } + return s; + } + + /** + * Load an individual double value given a specified key + * + * @param key + * a string describing the value + * @param defaultValue + * the default value + * @return the value get from the file + */ + public double loadValue(String key, double defaultValue) { + double d = 0; + try { + FileInputStream in = new FileInputStream(filename); + props.load(in); + String value = props.getProperty(key, "" + defaultValue); + d = (new Double(value)).doubleValue(); + } + catch (Exception e) { + d = defaultValue; + } + return d; + } + + /** + * Load an individual integer value given a specified key + * + * @param key + * a string describing the value + * @param defaultValue + * the default value + * @return the value get from the file + */ + public int loadValue(String key, int defaultValue) { + int i = 0; + try { + FileInputStream in = new FileInputStream(filename); + props.load(in); + String value = props.getProperty(key, "" + defaultValue); + i = (new Integer(value)).intValue(); + } + catch (Exception e) { + i = defaultValue; + } + return i; + } + + /** + * Load an individual boolean value given a specified key + * + * @param key + * a string describing the value + * @param defaultValue + * the default value + * @return the value get from the file + */ + public boolean loadValue(String key, boolean defaultValue) { + boolean b = false; + try { + FileInputStream in = new FileInputStream(filename); + props.load(in); + String value = props.getProperty(key, "" + defaultValue); + b = (new Boolean(value)).booleanValue(); + } + catch (Exception e) { + b = defaultValue; + } + return b; + } + + /** + * Store an individual double value given a specified key + * + * @param key + * a string describing the value + * @param value + * the value to store + */ + public void storeValue(String key, String value) { + props.setProperty(key, value); + try { + FileOutputStream out = new FileOutputStream(filename); + props.store(out, project); + } + catch (Exception e) { + System.out.println(project + ": Impossible to store settings in (" + filename + ")"); + } + } + + /** + * Store an individual double value given a specified key + * + * @param key + * a string describing the value + * @param value + * the value to store + */ + public void storeValue(String key, double value) { + props.setProperty(key, "" + value); + try { + FileOutputStream out = new FileOutputStream(filename); + props.store(out, project); + } + catch (Exception e) { + System.out.println(project + ": Impossible to store settings in (" + filename + ")"); + } + } + + /** + * Store an individual integer value given a specified key + * + * @param key + * a string describing the value + * @param value + * the value to store + */ + public void storeValue(String key, int value) { + props.setProperty(key, "" + value); + try { + FileOutputStream out = new FileOutputStream(filename); + props.store(out, project); + } + catch (Exception e) { + System.out.println(project + ": Impossible to store settings in (" + filename + ")"); + } + } + + /** + * Store an individual boolean value given a specified key + * + * @param key + * a string describing the value + * @param value + * the value to store + */ + public void storeValue(String key, boolean value) { + props.setProperty(key, "" + value); + try { + FileOutputStream out = new FileOutputStream(filename); + props.store(out, project); + } + catch (Exception e) { + System.out.println(project + ": Impossible to store settings in (" + filename + ")"); + } + } + + /** + * Load all recorded values. + */ + public void loadRecordedItems() { + loadRecordedItems(filename); + } + + /** + * Load all recorded values from a specified filename. + */ + public void loadRecordedItems(String fname) { + try { + FileInputStream in = new FileInputStream(fname); + props.load(in); + } + catch (Exception e) { + props = new Properties(); + } + + for (int i = 0; i < items.size(); i++) { + Item item = (Item) items.get(i); + String value = props.getProperty(item.key, item.defaultValue); + if (item.component instanceof JTextField) { + ((JTextField) item.component).setText(value); + } + else if (item.component instanceof JComboBox) { + ((JComboBox) item.component).setSelectedItem(value); + } + else if (item.component instanceof JCheckBox) { + ((JCheckBox) item.component).setSelected(value.equals("on") ? true : false); + } + else if (item.component instanceof JToggleButton) { + ((JToggleButton) item.component).setSelected(value.equals("on") ? true : false); + } + else if (item.component instanceof SpinnerRangeInteger) { + ((SpinnerRangeInteger) item.component).set(Math.round((new Double(value)).intValue())); + } + else if (item.component instanceof SpinnerRangeDouble) { + ((SpinnerRangeDouble) item.component).set((new Double(value)).doubleValue()); + } + else if (item.component instanceof SpinnerRangeFloat) { + ((SpinnerRangeFloat) item.component).set((new Float(value)).floatValue()); + } + else if (item.component instanceof JSlider) { + ((JSlider) item.component).setValue((new Integer(value)).intValue()); + } + } + } + + /** + * Store all recorded values. + */ + public void storeRecordedItems() { + storeRecordedItems(filename); + } + + /** + * Store all recorded values into a specified filename + */ + public void storeRecordedItems(String fname) { + + for (int i = 0; i < items.size(); i++) { + Item item = (Item) items.get(i); + if (item.component instanceof JTextField) { + String value = ((JTextField) item.component).getText(); + props.setProperty(item.key, value); + } + else if (item.component instanceof JComboBox) { + String value = (String) ((JComboBox) item.component).getSelectedItem(); + props.setProperty(item.key, value); + } + else if (item.component instanceof JCheckBox) { + String value = (((JCheckBox) item.component).isSelected() ? "on" : "off"); + props.setProperty(item.key, value); + } + else if (item.component instanceof JToggleButton) { + String value = (((JToggleButton) item.component).isSelected() ? "on" : "off"); + props.setProperty(item.key, value); + } + else if (item.component instanceof JSpinner) { + String value = "" + ((JSpinner) item.component).getValue(); + props.setProperty(item.key, value); + } + else if (item.component instanceof JSlider) { + String value = "" + ((JSlider) item.component).getValue(); + props.setProperty(item.key, value); + } + } + + try { + FileOutputStream out = new FileOutputStream(fname); + props.store(out, project); + } + catch (Exception e) { + System.out.println(project + ": Impossible to store settings in (" + fname + ")"); + } + } + + /** + * Private class to store one component and its key. + */ + private class Item { + public Object component; + public String defaultValue; + public String key; + + public Item(String key, Object component, String defaultValue) { + this.component = component; + this.defaultValue = defaultValue; + this.key = key; + } + } + +} diff --git a/src/bilib/src/bilib/commons/settings/SettingsFileDialog.java b/src/bilib/src/bilib/commons/settings/SettingsFileDialog.java new file mode 100644 index 0000000..3fda912 --- /dev/null +++ b/src/bilib/src/bilib/commons/settings/SettingsFileDialog.java @@ -0,0 +1,107 @@ +package bilib.commons.settings; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.filechooser.FileNameExtensionFilter; + +public class SettingsFileDialog extends JDialog implements ActionListener { + private JTextField txt; + private JButton bnCancel = new JButton("Cancel"); + private JButton bnReset = new JButton("Default"); + private JButton bnSaveAs = new JButton("Save As"); + private JButton bnLoad = new JButton("Load"); + private JButton bnSave = new JButton("Save"); + + private Settings settings; + + public SettingsFileDialog(Settings settings) { + super(new JFrame(), "Settings of " + settings.getProject()); + this.settings = settings; + txt = new JTextField(settings.getFilename()); + + txt.setEditable(false); + Container contentPane = getContentPane(); + contentPane.setLayout(new BorderLayout()); + + JPanel pn1 = new JPanel(new FlowLayout()); + pn1.add(txt); + + JPanel pn2 = new JPanel(new FlowLayout()); + pn2.add(bnCancel); + pn2.add(bnReset); + pn2.add(bnSave); + pn2.add(bnSaveAs); + pn2.add(bnLoad); + + contentPane.add(pn1, BorderLayout.NORTH); + contentPane.add(pn2, BorderLayout.SOUTH); + bnCancel.addActionListener(this); + bnReset.addActionListener(this); + bnSaveAs.addActionListener(this); + bnSave.addActionListener(this); + bnLoad.addActionListener(this); + pack(); + + setModalityType(JDialog.ModalityType.APPLICATION_MODAL); + setResizable(false); + Dimension dim = getToolkit().getScreenSize(); + Rectangle abounds = getBounds(); + setLocation((dim.width - abounds.width) / 2, (dim.height - abounds.height) / 2); + setVisible(true); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == bnSaveAs) { + JFileChooser chooser = new JFileChooser(txt.getText()); + chooser.setSelectedFile(new File("config.txt")); + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + chooser.setFileFilter(new FileNameExtensionFilter("Configuration file", "txt")); + int returnVal = chooser.showSaveDialog(new JFrame()); + if (returnVal == JFileChooser.APPROVE_OPTION) { + String name = chooser.getSelectedFile().getAbsolutePath(); + if (!name.endsWith(".txt")) + name += ".txt"; + txt.setText(name); + settings.storeRecordedItems(txt.getText()); + } + } + else if (e.getSource() == bnLoad) { + JFileChooser chooser = new JFileChooser(txt.getText()); + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + chooser.setFileFilter(new FileNameExtensionFilter("Configuration file", "txt")); + int returnVal = chooser.showOpenDialog(new JFrame()); + if (returnVal == JFileChooser.APPROVE_OPTION) { + String name = chooser.getSelectedFile().getAbsolutePath(); + txt.setText(name); + } + settings.loadRecordedItems(txt.getText()); + } + else if (e.getSource() == bnSave) { + settings.storeRecordedItems(txt.getText()); + } + else if (e.getSource() == bnReset) { + settings.loadRecordedItems("default-no-file"); + } + + bnCancel.removeActionListener(this); + bnReset.removeActionListener(this); + bnLoad.removeActionListener(this); + bnSave.removeActionListener(this); + bnSaveAs.removeActionListener(this); + dispose(); + } +} \ No newline at end of file diff --git a/src/bilib/src/bilib/commons/table/CustomizedColumn.java b/src/bilib/src/bilib/commons/table/CustomizedColumn.java new file mode 100644 index 0000000..f37537a --- /dev/null +++ b/src/bilib/src/bilib/commons/table/CustomizedColumn.java @@ -0,0 +1,102 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.table; + +/** + * This class allows to customized the columns of the CustomizedTable tables. + * + * @author Daniel Sage + * + */ +public class CustomizedColumn { + + private Class<?> columnClasse; // usually it is a String + private String header; + private boolean editable; + private int width; + private String[] choices; // ComboBox + private String button; // Button + private String tooltip; + + public CustomizedColumn(String header, Class<?> columnClasse, int width, boolean editable) { + this.columnClasse = columnClasse; + this.header = header; + this.width = width; + this.editable = editable; + } + + public CustomizedColumn(String header, Class<?> classe, int width, String[] choices, String tooltip) { + this.columnClasse = classe; + this.header = header; + this.width = width; + this.editable = true; + this.choices = choices; + this.tooltip = tooltip; + } + + public CustomizedColumn(String header, Class<?> columnClasse, int width, String button, String tooltip) { + this.columnClasse = columnClasse; + this.header = header; + this.width = width; + this.editable = false; + this.button = button; + this.tooltip = tooltip; + } + + public Class<?> getColumnClass() { + return columnClasse; + } + + public String getHeader() { + return header; + } + + public boolean isEditable() { + return editable; + } + + public int getWidth() { + return width; + } + + public String[] getChoices() { + return choices; + } + + public String getButton() { + return button; + } + + public String getTooltip() { + return tooltip; + } +} diff --git a/src/bilib/src/bilib/commons/table/CustomizedTable.java b/src/bilib/src/bilib/commons/table/CustomizedTable.java new file mode 100644 index 0000000..9f8dfa7 --- /dev/null +++ b/src/bilib/src/bilib/commons/table/CustomizedTable.java @@ -0,0 +1,336 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.table; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.Rectangle; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; + +import javax.swing.DefaultCellEditor; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; + +/** + * This class build a table by extending JTable. Usually the contructor expects + * a list of CustomizedColumn objects that defines the columns of the table. + * + * @author Daniel Sage + * + */ + +public class CustomizedTable extends JTable { + + private JScrollPane pane = null; + private ArrayList<CustomizedColumn> columns; + + public CustomizedTable(ArrayList<CustomizedColumn> columns, boolean sortable) { + create(columns); + setAutoCreateRowSorter(sortable); + setRowHeight(20); + } + + + public CustomizedTable(String headers[], boolean sortable) { + ArrayList<CustomizedColumn> colums = new ArrayList<CustomizedColumn>(); + for (int i = 0; i < headers.length; i++) + colums.add(new CustomizedColumn(headers[i], String.class, 150, false)); + create(colums); + setAutoCreateRowSorter(sortable); + setRowHeight(20); + } + + private void create(ArrayList<CustomizedColumn> column) { + columns = column; + DefaultTableModel model = new DefaultTableModel() { + @Override + public boolean isCellEditable(int row, int col) { + return columns.get(col).isEditable(); + } + + @Override + public Class<?> getColumnClass(int col) { + return columns.get(col).getColumnClass(); + } + }; + + setModel(model); + int n = columns.size(); + String headers[] = new String[n]; + for (int col = 0; col < n; col++) + headers[col] = columns.get(col).getHeader(); + + model.setColumnIdentifiers(headers); + setFillsViewportHeight(true); + + for (int col = 0; col < n; col++) { + TableColumn tc = getColumnModel().getColumn(col); + tc.setPreferredWidth(columns.get(col).getWidth()); + + if (columns.get(col).getChoices() != null) { + JComboBox cmb = new JComboBox(); + for (String p : columns.get(col).getChoices()) { + cmb.addItem(p); + cmb.setToolTipText(columns.get(col).getTooltip()); + tc.setCellEditor(new DefaultCellEditor(cmb)); + } + } + if (columns.get(col).getButton() != null) { + ButtonRenderer bn = new ButtonRenderer(); + bn.setToolTipText(columns.get(col).getTooltip()); + tc.setCellRenderer(bn); + } + } + getTableHeader().setReorderingAllowed(false); + } + + public void setPreferredSize(int width, int height) { + if (pane != null) + pane.setPreferredSize(new Dimension(width, height)); + } + + /** + * Removes one specify row from the table. + * + * @param row Row to remove + */ + public void removeRow(int row) { + if (row >= 0 && row < getRowCount()) + ((DefaultTableModel) getModel()).removeRow(row); + } + + /** + * Removes all rows of the table. + */ + public void removeRows() { + while (getRowCount() > 0) + ((DefaultTableModel) getModel()).removeRow(0); + } + + public String[] getRow(int row) { + if (row >= 0) { + int ncol = getColumnCount(); + String items[] = new String[ncol]; + for (int col = 0; col < ncol; col++) + items[col] = (String) getModel().getValueAt(row, col); + return items; + } + return new String[1]; + } + + public String getCell(int row, int col) { + if (row >= 0 && col >= 0) { + return (String) getModel().getValueAt(row, col); + } + return ""; + } + + public void setCell(int row, int col, String value) { + if (row >= 0 && col >= 0) { + getModel().setValueAt(value, row, col); + } + } + + public String getRowCSV(int row, String seperator) { + if (row >= 0) { + int ncol = getColumnCount(); + String items = ""; + for (int col = 0; col < ncol - 1; col++) { + if ((String) getModel().getValueAt(row, col) == null) + items += "" + seperator; + else + items += (String) getModel().getValueAt(row, col) + seperator; + } + if (ncol >= 1) + items += (String) getModel().getValueAt(row, ncol - 1); + return items; + } + return ""; + } + + /** + * Saves the table in a CSV file + * + * @param filename Complete path and filename + */ + public void storeCSV(String filename) { + File file = new File(filename); + try { + BufferedWriter buffer = new BufferedWriter(new FileWriter(file)); + int nrows = getRowCount(); + int ncols = getColumnCount(); + + String row = ""; + for (int c = 0; c < columns.size(); c++) + row += columns.get(c).getHeader() + (c == columns.size() - 1 ? "" : ", "); + buffer.write(row + "\n"); + + for (int r = 0; r < nrows; r++) { + row = ""; + for (int c = 0; c < ncols; c++) + row += this.getCell(r, c) + (c == ncols - 1 ? "" : ", "); + buffer.write(row + "\n"); + } + buffer.close(); + } + catch (IOException ex) { + } + } + + public String getSelectedAtColumn(int col) { + int row = getSelectedRow(); + if (row >= 0) + return (String) getModel().getValueAt(row, col); + else + return ""; + } + + public void setSelectedAtColumn(int col, String selection) { + int nrows = this.getRowCount(); + for (int i = 0; i < nrows; i++) { + String name = (String) getModel().getValueAt(i, col); + if (name.equals(selection)) + this.setRowSelectionInterval(i, i + 1); + } + } + + /** + * Add a row at the end of the table. + * + * @param row + */ + public void append(Object[] row) { + DefaultTableModel model = (DefaultTableModel) getModel(); + int i = 0; + try { + model.addRow(row); + i = getRowCount() - 1; + if (i >= 0) { + getSelectionModel().setSelectionInterval(i, i); + scrollRectToVisible(new Rectangle(getCellRect(i, 0, true))); + } + } + catch (Exception e) { + } + repaint(); + } + + /** + * Add a row at the top of the table. + * + * @param row + */ + public void insert(Object[] row) { + DefaultTableModel model = (DefaultTableModel) getModel(); + int i = 0; + try { + model.insertRow(0, row); + getSelectionModel().setSelectionInterval(i, i); + scrollRectToVisible(new Rectangle(getCellRect(i, 0, true))); + } + catch (Exception e) { + } + repaint(); + } + + @Override + public int getSelectedRow() { + int row = super.getSelectedRow(); + if (row < 0) { + if (getRowCount() > 0) { + setRowSelectionInterval(0, 0); + row = super.getSelectedRow(); + } + return row; + } + return row; + } + + /** + * Replaces all the content of the table by a content private as list of String[]. + * + * @param data + */ + public void update(ArrayList<String[]> data) { + DefaultTableModel model = (DefaultTableModel) getModel(); + model.getDataVector().removeAllElements(); + for (String[] row : data) + model.addRow(row); + repaint(); + } + + public JScrollPane getPane(int width, int height) { + setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); + setPreferredScrollableViewportSize(new Dimension(width, height)); + setFillsViewportHeight(true); + pane = new JScrollPane(this); + return pane; + } + + public JScrollPane getMinimumPane(int width, int height) { + setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); + setMinimumSize(new Dimension(width, height)); + setShowVerticalLines(true); + setPreferredScrollableViewportSize(new Dimension(width, height)); + setFillsViewportHeight(true); + return new JScrollPane(this); + } + + public JFrame show(String title, int w, int h) { + JFrame frame = new JFrame(title); + frame.add(getPane(w, h)); + frame.pack(); + frame.setVisible(true); + return frame; + } + + public class ButtonRenderer extends JButton implements TableCellRenderer { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + setText((String) value); + setMargin(new Insets(1, 1, 1, 1)); + return this; + } + } + +} diff --git a/src/bilib/src/bilib/commons/utils/Chrono.java b/src/bilib/src/bilib/commons/utils/Chrono.java new file mode 100644 index 0000000..516d093 --- /dev/null +++ b/src/bilib/src/bilib/commons/utils/Chrono.java @@ -0,0 +1,91 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.utils; + +import java.text.DecimalFormat; + +/** + * This class provides static methods to measures the elapsed time. It is a + * equivalent to the function tic and toc of Matlab. + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class Chrono { + + static private double chrono = 0; + + /** + * Register the current time. + */ + public static void tic() { + chrono = System.nanoTime(); + } + + /** + * Returns a string that indicates the elapsed time since the last tic() + * call. + */ + public static String toc() { + return toc(""); + } + + /** + * Returns a string that indicates the elapsed time since the last tic() + * call. + * + * @param msg + * message to print + */ + public static String toc(String msg) { + double te = System.nanoTime() - chrono; + String s = msg + " "; + DecimalFormat df = new DecimalFormat("####.##"); + if (te < 1000.0) + return s + df.format(te) + " ns"; + te /= 1000; + if (te < 1000.0) + return s + df.format(te) + " us"; + te /= 1000; + if (te < 3000.0) + return s + df.format(te) + " ms"; + te /= 1000; + if (te < 600.1) + return s + df.format(te) + " s"; + te /= 60; + if (te < 600.1) + return s + df.format(te) + " min."; + te /= 60; + return s + df.format(te) + " h."; + } + +} diff --git a/src/bilib/src/bilib/commons/utils/Files.java b/src/bilib/src/bilib/commons/utils/Files.java new file mode 100644 index 0000000..786228c --- /dev/null +++ b/src/bilib/src/bilib/commons/utils/Files.java @@ -0,0 +1,84 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.utils; + +import java.io.File; + +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileSystemView; + +public class Files { + + public static String getWorkingDirectory() { + return System.getProperty("user.dir"); + } + + public static String getHomeDirectory() { + return FileSystemView.getFileSystemView().getHomeDirectory().getAbsolutePath() + File.separator; + } + + public static String getDesktopDirectory() { + return getHomeDirectory() + "Desktop" + File.separator; + } + + public static File browseFile(String path) { + JFileChooser fc = new JFileChooser(); + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + File dir = new File(path); + if (dir.exists()) + fc.setCurrentDirectory(dir); + + int ret = fc.showOpenDialog(null); + if (ret == JFileChooser.APPROVE_OPTION) { + File file = new File(fc.getSelectedFile().getAbsolutePath()); + if (file.exists()) + return file; + } + return null; + } + + public static File browseDirectory(String path) { + JFileChooser fc = new JFileChooser(); + fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + File dir = new File(path); + if (dir.exists()) + fc.setCurrentDirectory(dir); + + int ret = fc.showOpenDialog(null); + if (ret == JFileChooser.APPROVE_OPTION) { + File file = new File(fc.getSelectedFile().getAbsolutePath()); + if (file.exists()) + return file; + } + return null; + } +} diff --git a/src/bilib/src/bilib/commons/utils/Log.java b/src/bilib/src/bilib/commons/utils/Log.java new file mode 100644 index 0000000..379049f --- /dev/null +++ b/src/bilib/src/bilib/commons/utils/Log.java @@ -0,0 +1,59 @@ +package bilib.commons.utils; + +public class Log { + + static public void dash() { + System.out.println("------------------------------------------"); + } + + static public void print() { + System.out.println(""); + } + + static public void print(String a) { + System.out.println(">" + a); + } + + static public void print(double a) { + System.out.println(">" + a); + } + + static public void print(float a) { + System.out.println(">" + a); + } + + static public void print(long a) { + System.out.println(">" + a); + } + + static public void print(int a) { + System.out.println(">" + a); + } + + static public void print(short a) { + System.out.println(">" + a); + } + + static public void print(byte a) { + System.out.println(">" + a); + } + + static public void print(double a[]) { + if (a == null) { + System.out.println("> null double 1D array"); + return; + } + + if (a.length < 10) { + for (int i = 0; i < a.length; i++) + System.out.println("> [" + i + "] = " + a[i]); + return; + } + for (int i = 0; i < 4; i++) + System.out.println("> [" + i + "] = " + a[i]); + System.out.println("> ......"); + for (int i = a.length - 4; i < a.length; i++) + System.out.println("> [" + i + "] = " + a[i]); + } + +} diff --git a/src/bilib/src/bilib/commons/utils/NumFormat.java b/src/bilib/src/bilib/commons/utils/NumFormat.java new file mode 100644 index 0000000..1773cb2 --- /dev/null +++ b/src/bilib/src/bilib/commons/utils/NumFormat.java @@ -0,0 +1,157 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.utils; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class NumFormat { + + public static double parseNumber(String line, double def) { + double[] numbers = parseNumbers(line); + if (numbers.length >= 1) + return numbers[0]; + else + return def; + } + + public static double[] parseNumbersAfter(String keyword, String line) { + String parts[] = line.trim().toLowerCase().split(keyword.toLowerCase()); + if (parts.length == 2) + return parseNumbers(parts[1]); + else + return new double[0]; + } + + public static double[] parseNumbers(String line) { + ArrayList<String> num = new ArrayList<String>(); + Pattern p = Pattern.compile("[-+]?[0-9]+[.]?[0-9]*([eE][-+]?[0-9]+)?"); + Matcher m = p.matcher(line); + while (m.find()) { + num.add(m.group()); + } + double number[] = new double[num.size()]; + for (int i = 0; i < num.size(); i++) + number[i] = Double.parseDouble(num.get(i)); + return number; + } + + public static String sci(double a) { + double b = Math.abs(a); + String n = a < 0 ? "-" : ""; + if (a == 0) + return "0.0"; + if (b > 3000.0) + return String.format(n + "%6.3E", b); + if (b > 300.0) + return String.format(n + "%4.1f", b); + if (b > 30.0) + return String.format(n + "%3.2f", b); + if (b > 3.0) + return String.format(n + "%2.4f", b); + if (b > 0.003) + return String.format(n + "%1.4f", b); + + return String.format(n + "%6.3E", b).trim(); + } + + public static String unit(int n, String unit) { + double dn = n; + if (dn < 0.000000001) + return new DecimalFormat("0.00 p").format(dn*1000000000.) + unit; + if (dn < 0.000001) + return new DecimalFormat("0.00 n").format(dn*1000000.) + unit; + if (dn < 0.001) + return new DecimalFormat("0.00 m").format(dn*1000.) + unit; + if (dn < 1) + return new DecimalFormat("0.00 ").format(dn) + unit; + if (dn < 1000.) + return new DecimalFormat("0.00 K").format(dn*0.001) + unit; + if (dn < 1000000.) + return new DecimalFormat("0.00 M").format(dn*0.000001) + unit; + if (dn < 1000000000.) + return new DecimalFormat("0.00 G").format(dn*0.000000001) + unit; + return new DecimalFormat("0.00 T").format(dn*0.000000000001) + unit; + } + + public static String seconds(double ns) { + return String.format("%5.1f s", ns * 1e-9); + } + + public static String time(double ns) { + if (ns < 3000.0) + return String.format("%3.2f ns", ns); + ns *= 0.001; + if (ns < 3000.0) + return String.format("%3.2f us", ns); + ns *= 0.001; + if (ns < 3000.0) + return String.format("%3.2f ms", ns); + ns *= 0.001; + if (ns < 3600.0 * 3) + return String.format("%3.2f s", ns); + ns /= 3600; + return String.format("%3.2f h", ns); + } + + public static String bytes(double bytes) { + if (bytes < 3000) + return String.format("%3.0f", bytes); + bytes /= 1024.0; + if (bytes < 3000) + return String.format("%3.1f Kb", bytes); + bytes /= 1024.0; + if (bytes < 3000) + return String.format("%3.1f Mb", bytes); + bytes /= 1024.0; + if (bytes < 3000) + return String.format("%3.1f Gb", bytes); + bytes /= 1024.0; + return String.format("%3.1f Tb", bytes); + } + + public static String toPercent(String value) { + try { + return toPercent(Double.parseDouble(value)); + } + catch (Exception ex) { + } + return value; + } + + public static String toPercent(double value) { + return String.format("%5.3f", value * 100) + "%"; + } + +} diff --git a/src/bilib/src/bilib/commons/utils/WebBrowser.java b/src/bilib/src/bilib/commons/utils/WebBrowser.java new file mode 100644 index 0000000..70fef85 --- /dev/null +++ b/src/bilib/src/bilib/commons/utils/WebBrowser.java @@ -0,0 +1,74 @@ +/* + * bilib --- Java Bioimaging Library --- + * + * Author: Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: D. Sage, M. Unser, "Teaching Image-Processing Programming in Java" + * IEEE Signal Processing Magazine, vol. 20, pp. 43-52, November 2003. + * http://bigwww.epfl.ch/publications/sage0303.html + */ + +/* + * Copyright 2007-2017 Biomedical Imaging Group at the EPFL. + * + * bilib is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * bilib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * bilib. If not, see <http://www.gnu.org/licenses/>. + */ + +package bilib.commons.utils; + +import java.awt.Desktop; +import java.net.URL; + +import javax.swing.JFrame; +import javax.swing.JLabel; + +public class WebBrowser { + + public static boolean open(String url) { + Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; + if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { + try { + desktop.browse(new URL(url).toURI()); + return true; + } + catch (Exception e) { + e.printStackTrace(); + } + } + return false; + } + + public static boolean openInFrame(String url) { + Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; + if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { + try { + desktop.browse(new URL(url).toURI()); + return true; + } + catch (Exception e) { + e.printStackTrace(); + } + } + JFrame frame = new JFrame("Help"); + JLabel lbl = new JLabel(url); + frame.add(lbl); + frame.pack(); + frame.setVisible(true); + return false; + } +} diff --git a/src/bilib/src/bilib/fft/AcademicFourierTransform.java b/src/bilib/src/bilib/fft/AcademicFourierTransform.java new file mode 100644 index 0000000..e37a53e --- /dev/null +++ b/src/bilib/src/bilib/fft/AcademicFourierTransform.java @@ -0,0 +1,21796 @@ +package bilib.fft; + +/*==================================================================== +| Version: May 31, 2014 +\===================================================================*/ + +/*==================================================================== +| Philippe Thevenaz +| EPFL/STI/IMT-LS/LIB/BM.4.137 +| Station 17 +| CH-1015 Lausanne VD +| Switzerland +| +| phone (CET): +41(21)693.51.61 +| fax: +41(21)693.37.01 +| RFC-822: philippe.thevenaz@epfl.ch +| X-400: /C=ch/A=400net/P=switch/O=epfl/S=thevenaz/G=philippe/ +| URL: http://bigwww.epfl.ch/ +\===================================================================*/ + +// import ij.IJ; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.TreeMap; +import java.util.Vector; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static java.lang.Math.PI; +import static java.lang.Math.atan2; +import static java.lang.Math.cos; +import static java.lang.Math.floor; +import static java.lang.Math.sin; +import static java.lang.Math.sqrt; + +/********************************************************************* + <p> + The purpose of this class is to encapsulate operations related to the + discrete Fourier transform of periodic sequences, images, and + volumes. If <i>x</i> is a discrete sequence assumed to be periodic + over <i>K</i> samples, then its discrete Fourier transform is <i>X</i> + while the inverse discrete Fourier transform of <i>X</i> is <i>x</i>. + The transforms are defined as the pair + </p> + <ul> + <li>∀<i>n</i> ∈ [0…<i>K</i> − 1]: + <i>X</i>[<i>n</i>] = ℱ{<i>x</i>}[<i>n</i>] + = ∑<sub><i>k</i> ∈ [0…<i>K</i> − 1]</sub> + <i>x</i>[<i>k</i>] + e<sup>−j <i>n</i> (2 π ∕ <i>K</i>) <i>k</i></sup></li> + <li>∀<i>k</i> ∈ [0…<i>K</i> − 1]: + <i>x</i>[<i>k</i>] = ℱ<sup>−1</sup>{<i>X</i>}[<i>k</i>] + = (1 ∕ <i>K</i>) + ∑<sub><i>n</i> ∈ [0…<i>K</i> − 1]</sub> + <i>X</i>[<i>n</i>] + e<sup>j <i>n</i> (2 π ∕ <i>K</i>) <i>k</i></sup></li> + </ul> + <p> + A few relevant relations are + </p> + <ul> + <li>j<sup>2</sup> = −1</li> + <li>∀<i>k</i>, <i>n</i> ∈ [0…<i>K</i> − 1], + <i>m</i> ∈ ℤ: + <i>x</i>[<i>k</i>] = <i>x</i>[<i>k</i> + <i>m</i> <i>K</i>], + <i>X</i>[<i>n</i>] = <i>X</i>[<i>n</i> + <i>m</i> <i>K</i>]</li> + <li>∀<i>λ</i> ∈ ℂ: ℱ{<i>λ</i> <i>x</i> + <i>y</i>} + = <i>λ</i> <i>X</i> + <i>Y</i></li> + <li>ℱ{<i>X</i>}[0] = <i>x</i>[0]; + <i>m</i> ∈ [1…<i>K</i> − 1]: + ℱ{<i>X</i>}[<i>m</i>] = <i>x</i>[<i>K</i> − <i>m</i>]</li> + <li><i>k</i><sub>0</sub> ∈ [1…<i>K</i> − 1]: + ((<i>k</i> ∈ [0…<i>k</i><sub>0</sub> − 1]: + <i>y</i>[<i>k</i>] = <i>x</i>[<i>K</i> + <i>k</i> + − <i>k</i><sub>0</sub>]), + (<i>k</i> ∈ [<i>k</i><sub>0</sub>…<i>K</i> − 1]: + <i>y</i>[<i>k</i>] = <i>x</i>[<i>k</i> − <i>k</i><sub>0</sub>])); + <i>Y</i>[<i>n</i>] = e<sup>−j <i>k</i><sub>0</sub> + (2 π ∕ <i>K</i>) <i>n</i></sup> <i>X</i>[<i>n</i>]</li> + <li><i>n</i> ∈ [0…<i>K</i> − 1]: + (<i>k</i><sub>0</sub> ∈ [0…<i>K</i> − 2]: + (<i>x</i> ∗ <i>y</i>)[<i>k</i><sub>0</sub>] + = ∑<sub><i>k</i> ∈ [0…<i>k</i><sub>0</sub>]</sub> + <i>x</i>[<i>k</i>] <i>y</i>[<i>k</i><sub>0</sub> − <i>k</i>] + + ∑<sub><i>k</i> ∈ [<i>k</i><sub>0</sub> + 1…<i>K</i> − 1]</sub> + <i>x</i>[<i>k</i>] <i>y</i>[<i>K</i> + <i>k</i><sub>0</sub> − <i>k</i>]), + ((<i>x</i> ∗ <i>y</i>)[<i>K</i> − 1] + = ∑<sub><i>k</i> ∈ [0…<i>K</i> − 1]</sub> + <i>x</i>[<i>k</i>] <i>y</i>[<i>K</i> − 1 − <i>k</i>]); + ℱ{<i>x</i> ∗ <i>y</i>}[<i>n</i>] + = <i>X</i>[<i>n</i>] <i>Y</i>[<i>n</i>]</li> + <li>〈<i>x</i>, <i>y</i>〉 + = ∑<sub><i>k</i> ∈ [0…<i>K</i> − 1]</sub> + <i>x</i>[<i>k</i>] (<i>y</i>[<i>k</i>])<sup>*</sup> + = (1 ∕ <i>K</i>) 〈<i>X</i>, <i>Y</i>〉 + = (1 ∕ <i>K</i>) ∑<sub><i>n</i> ∈ [0…<i>K</i> − 1]</sub> + <i>X</i>[<i>n</i>] (<i>Y</i>[<i>n</i>])<sup>*</sup></li> + <li><i>x</i>[0] = 1, <i>k</i> ∈ [1…<i>K</i> − 1]: + <i>x</i>[<i>k</i>] = 0; <i>n</i> ∈ [0…<i>K</i> − 1]: + <i>X</i>[<i>n</i>] = 1</li> + <li><i>k</i> ∈ [0…<i>K</i> − 1]: + <i>x</i>[<i>k</i>] = 1; <i>X</i>[0] = <i>K</i>, + <i>n</i> ∈ [1…<i>K</i> − 1]: <i>X</i>[<i>n</i>] = 0</li> + <li><i>x</i> = ℜ(<i>x</i>); ℑ(<i>X</i>[0]) = 0, + (2 ∣ <i>K</i>: ℑ(<i>X</i>[<i>K</i> ∕ 2]) = 0), + <i>n</i> ∈ [1…<i>K</i> − 1]: <i>X</i>[<i>n</i>] + = (<i>X</i>[<i>K</i> − <i>n</i>])<sup>*</sup></li> + <li><i>x</i> = ℜ(<i>x</i>); <i>y</i> = ℜ(<i>y</i>); + F = ℱ{<i>x</i> + j <i>y</i>}; <i>X</i>[0] = ℜ(<i>F</i>[0]); + <i>Y</i>[0] = ℑ(<i>F</i>[0]); + <i>n</i> ∈ [1…<i>K</i> − 1]: <i>X</i>[<i>n</i>] + = (<i>F</i>[<i>n</i>] + (<i>F</i>[<i>K</i> − <i>n</i>])<sup>*</sup>) + ∕ 2, <i>Y</i>[<i>n</i>] = −j (<i>F</i>[<i>n</i>] + − (<i>F</i>[<i>K</i> − <i>n</i>])<sup>*</sup>) ∕ 2</li> + </ul> + <p> + In two dimensions + </p> + <ul> + <li>ℱ{<i>x</i>}[<i>n</i><sub>1</sub>, <i>n</i><sub>2</sub>] + = ℱ<sub><i>k</i><sub>2</sub></sub>{ℱ<sub><i>k</i><sub>1</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>, + <i>k</i><sub>2</sub>]}[<i>n</i><sub>1</sub>, <i>k</i><sub>2</sub>]}[<i>n</i><sub>1</sub>, + <i>n</i><sub>2</sub>] + = ℱ<sub><i>k</i><sub>1</sub></sub>{ℱ<sub><i>k</i><sub>2</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>, + <i>k</i><sub>2</sub>]}[<i>k</i><sub>1</sub>, + <i>n</i><sub>2</sub>]}[<i>n</i><sub>1</sub>, <i>n</i><sub>2</sub>]</li> + </ul> + <p> + In three dimensions + </p> + <ul> + <li>ℱ{<i>x</i>}[<i>n</i><sub>1</sub>, <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>] + = ℱ<sub><i>k</i><sub>3</sub></sub>{ℱ<sub><i>k</i><sub>2</sub></sub>{ℱ<sub><i>k</i><sub>1</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>, + <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>, + <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>, + <i>n</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>, + <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>] + = ℱ<sub><i>k</i><sub>3</sub></sub>{ℱ<sub><i>k</i><sub>1</sub></sub>{ℱ<sub><i>k</i><sub>2</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>, + <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>k</i><sub>1</sub>, + <i>n</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>, + <i>n</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>, + <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>] + = ℱ<sub><i>k</i><sub>2</sub></sub>{ℱ<sub><i>k</i><sub>3</sub></sub>{ℱ<sub><i>k</i><sub>1</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>, + <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>, + <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>n</i><sub>1</sub>, + <i>k</i><sub>2</sub>, <i>n</i><sub>3</sub>]}[<i>n</i><sub>1</sub>, + <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>] + = ℱ<sub><i>k</i><sub>2</sub></sub>{ℱ<sub><i>k</i><sub>1</sub></sub>{ℱ<sub><i>k</i><sub>3</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>, + <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>k</i><sub>1</sub>, + <i>k</i><sub>2</sub>, <i>n</i><sub>3</sub>]}[<i>n</i><sub>1</sub>, + <i>k</i><sub>2</sub>, <i>n</i><sub>3</sub>]}[<i>n</i><sub>1</sub>, + <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>] + = ℱ<sub><i>k</i><sub>1</sub></sub>{ℱ<sub><i>k</i><sub>3</sub></sub>{ℱ<sub><i>k</i><sub>2</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>, + <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>k</i><sub>1</sub>, + <i>n</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>k</i><sub>1</sub>, + <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>]}[<i>n</i><sub>1</sub>, + <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>] + = ℱ<sub><i>k</i><sub>1</sub></sub>{ℱ<sub><i>k</i><sub>2</sub></sub>{ℱ<sub><i>k</i><sub>3</sub></sub>{<i>x</i>[<i>k</i><sub>1</sub>, + <i>k</i><sub>2</sub>, <i>k</i><sub>3</sub>]}[<i>k</i><sub>1</sub>, + <i>k</i><sub>2</sub>, <i>n</i><sub>3</sub>]}[<i>k</i><sub>1</sub>, + <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>]}[<i>n</i><sub>1</sub>, + <i>n</i><sub>2</sub>, <i>n</i><sub>3</sub>]</li> + </ul> + <p> + Data are provided as the pair (real part, imaginary part). The real + part is assumed to be provided as an array that contains raster data, + with the convention that the indexing for the horizontal dimension is + faster than the indexing for the vertical dimension, itself faster + than the indexing for the depth dimension. The imaginary part follows + the same organization. + </p> + <p> + The two buffers that are provided to some methods must have the same + length as the data. They are used internally as temporary storage for + the computations. They are there as a convenience to avoid repeated + allocations. + </p> + ********************************************************************/ +public class AcademicFourierTransform + +{ /* begin class SlowFourierTransform */ + +/*.................................................................... + SlowFourierTransform public enum constants +....................................................................*/ +/********************************************************************* + <p> + This enumeration provides the constants that describe the type of + input (complex or real) of a Fourier transform. + </p> + @see #makeHermitian(double[],double[],double[],double[]) + @see #makeHermitian(float[],float[],float[],float[]) + ********************************************************************/ +public enum InputDataType { +/********************************************************************* + <p> + The input of the Fourier transform is complex, made of a real and an + imaginary part. The two parts must be of equal length; moreover, this + length must match the length inferred at creation time of this + object. + </p> + ********************************************************************/ + COMPLEXINPUT, +/********************************************************************* + <p> + The input of the Fourier transform is real, although the storage for + the imaginary part must still be provided to receive the output of + the Fourier transform. The two parts must be of equal length; + moreover, this length must match the length inferred at creation time + of this object. + </p> + ********************************************************************/ + REALINPUT +} + +/*.................................................................... + SlowFourierTransform protected variables +....................................................................*/ +protected double[] imBufferDouble; +protected double[] imDataDouble; +protected double[] reBufferDouble; +protected double[] reDataDouble; +protected float[] imBufferFloat; +protected float[] imDataFloat; +protected float[] reBufferFloat; +protected float[] reDataFloat; + +/*.................................................................... + SlowFourierTransform private variables +....................................................................*/ +private Integer depth; +private Integer height; +private Integer width; +private int dataLength; +private int dimensions; +private int firstDimension; +private int fourierOrigin1; +private int fourierOrigin2; +private int fourierOrigin3; + +/*.................................................................... + SlowFourierTransform private enum constants +....................................................................*/ +private enum Algorithm { + BRUTEFORCE, + COPRIMEFACTOR, + DUOREAL, + EVENREAL, + LENGTH1, + LENGTH2, + LENGTH3, + LENGTH4, + LENGTH5, + LENGTH6, + LENGTH8, + MIXEDRADIX, + PADDEDRADER, + RADER, + RADIX2, + SPLITRADIX +} + +private final boolean PARALLELPROCESSING = true; + +/*.................................................................... + SlowFourierTransform inner classes +....................................................................*/ +/*==================================================================== +| DFTDouble +\===================================================================*/ +static class DFTDouble + +{ /* begin class DFTDouble */ + +/*.................................................................... + DFTDouble variables +....................................................................*/ +protected double[] imData; +protected double[] reData; +protected int startIndex; +protected int stride; + +/*.................................................................... + DFTDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTDouble ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + this.reData = reData; + this.imData = imData; + this.startIndex = startIndex; + this.stride = stride; +} /* end DFTDouble */ + +} /* end class DFTDouble */ + +/*==================================================================== +| DFTFloat +\===================================================================*/ +static class DFTFloat + +{ /* begin class DFTFloat */ + +/*.................................................................... + DFTFloat variables +....................................................................*/ +protected float[] imData; +protected float[] reData; +protected int startIndex; +protected int stride; + +/*.................................................................... + DFTFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTFloat ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + this.reData = reData; + this.imData = imData; + this.startIndex = startIndex; + this.stride = stride; +} /* end DFTFloat */ + +} /* end class DFTFloat */ + +/*==================================================================== +| DFTBruteForce +\===================================================================*/ +static class DFTBruteForce + +{ /* begin class DFTBruteForce */ + +/*.................................................................... + DFTBruteForce static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int transformLength +) { + if (FFTSetup.taboos.contains(new Integer(transformLength))) { + return(-1L); + } + final long K = (long)transformLength; + return(FFTSetup.FLALLOC * 2L + + FFTSetup.FLOP * (K * 2L + (K - 1L) * (K - 1L) * 8L) + + FFTSetup.FLASSIGN * (2L + K * 2L + 2L + (K- 1L) * (2L + + (K - 1L) * 2L + 2L) + K * 2L) + + FFTSetup.INTALLOC * 9L + + FFTSetup.INTOP * (K * 3L + 1L + (K - 1L) * (3L + (K - 1L) * 7L + 1L) + + K * 3L) + + FFTSetup.INTASSIGN * (5L + K * 2L + 2L + (K - 1L) * (4L + + (K - 1L) * 4L + 1L) + 2L + K * 2L) + + FFTSetup.IDX * (K * 2L + 2L + (K - 1L) * (2L + (K - 1L) * 8L + 2L) + + K * 4L) + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTBruteForce */ + +/*==================================================================== +| DFTBruteForceDouble +\===================================================================*/ +static class DFTBruteForceDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTBruteForceDouble */ + +/*.................................................................... + DFTBruteForceDouble private variables +....................................................................*/ +private double[] imBuffer; +private double[] imUnitRoot; +private double[] reBuffer; +private double[] reUnitRoot; + +/*.................................................................... + DFTBruteForceDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTBruteForceDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final double[] reUnitRoot, + final double[] imUnitRoot +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; +} /* end DFTBruteForceDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int transformLength = reUnitRoot.length; +//;IJ.log("DFTBruteForceDouble " + transformLength); + final int d1 = stride; + int n0 = startIndex; + double re = 0.0; + double im = 0.0; + int k1 = startIndex; + for (int n = 0; (n < transformLength); n++) { + re += reData[k1]; + im += imData[k1]; + k1 += d1; + } + reBuffer[n0] = re; + imBuffer[n0] = im; + n0 += d1; + for (int m = 1; (m < transformLength); m++) { + re = reData[startIndex]; + im = imData[startIndex]; + int m1 = m; + k1 = startIndex + d1; + for (int n = 1; (n < transformLength); n++) { + re += reData[k1] * reUnitRoot[m1] - imData[k1] * imUnitRoot[m1]; + im += reData[k1] * imUnitRoot[m1] + imData[k1] * reUnitRoot[m1]; + m1 += m; + m1 -= transformLength * (m1 / transformLength); + k1 += d1; + } + reBuffer[n0] = re; + imBuffer[n0] = im; + n0 += d1; + } + n0 = startIndex; + for (int m = 0; (m < transformLength); m++) { + reData[n0] = reBuffer[n0]; + imData[n0] = imBuffer[n0]; + n0 += d1; + } +} /* end run */ + +/*.................................................................... + DFTBruteForceDouble static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static double[] getImUnitRoot ( + final int transformLength +) { + final double[] imUnitRoot = new double[transformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < transformLength); k++) { + imUnitRoot[k] = sin((double)k * angularStep); + } + return(imUnitRoot); +} /* end getImUnitRoot */ + +/*------------------------------------------------------------------*/ +static double[] getReUnitRoot ( + final int transformLength +) { + final double[] reUnitRoot = new double[transformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < transformLength); k++) { + reUnitRoot[k] = cos((double)k * angularStep); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +} /* end class DFTBruteForceDouble */ + +/*==================================================================== +| DFTBruteForceFloat +\===================================================================*/ +static class DFTBruteForceFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTBruteForceFloat */ + +/*.................................................................... + DFTBruteForceFloat private variables +....................................................................*/ +private float[] imBuffer; +private float[] imUnitRoot; +private float[] reBuffer; +private float[] reUnitRoot; + +/*.................................................................... + DFTBruteForceFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTBruteForceFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final float[] reUnitRoot, + final float[] imUnitRoot +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; +} /* end DFTBruteForceFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int transformLength = reUnitRoot.length; +//;IJ.log("DFTBruteForceFloat " + transformLength); + final int d1 = stride; + int n0 = startIndex; + float re = 0.0F; + float im = 0.0F; + int k1 = startIndex; + for (int n = 0; (n < transformLength); n++) { + re += reData[k1]; + im += imData[k1]; + k1 += d1; + } + reBuffer[n0] = re; + imBuffer[n0] = im; + n0 += d1; + for (int m = 1; (m < transformLength); m++) { + re = reData[startIndex]; + im = imData[startIndex]; + int m1 = m; + k1 = startIndex + d1; + for (int n = 1; (n < transformLength); n++) { + re += reData[k1] * reUnitRoot[m1] - imData[k1] * imUnitRoot[m1]; + im += reData[k1] * imUnitRoot[m1] + imData[k1] * reUnitRoot[m1]; + m1 += m; + m1 -= transformLength * (m1 / transformLength); + k1 += d1; + } + reBuffer[n0] = re; + imBuffer[n0] = im; + n0 += d1; + } + n0 = startIndex; + for (int m = 0; (m < transformLength); m++) { + reData[n0] = reBuffer[n0]; + imData[n0] = imBuffer[n0]; + n0 += d1; + } +} /* end run */ + +/*.................................................................... + DFTBruteForceFloat static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static float[] getImUnitRoot ( + final int transformLength +) { + final float[] imUnitRoot = new float[transformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < transformLength); k++) { + imUnitRoot[k] = (float)sin((double)((float)k * angularStep)); + } + return(imUnitRoot); +} /* end getImUnitRoot */ + +/*------------------------------------------------------------------*/ +static float[] getReUnitRoot ( + final int transformLength +) { + final float[] reUnitRoot = new float[transformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < transformLength); k++) { + reUnitRoot[k] = (float)cos((double)((float)k * angularStep)); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +} /* end class DFTBruteForceFloat */ + +/*==================================================================== +| DFTBruteForceReal +\===================================================================*/ +static class DFTBruteForceReal + +{ /* begin class DFTBruteForceReal */ + +/*.................................................................... + DFTBruteForceReal static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int transformLength +) { + if (FFTSetup.taboos.contains(new Integer(transformLength))) { + return(-1L); + } + final long K = (long)transformLength; + final long k = (long)(transformLength >> 1); + return(FFTSetup.FLALLOC * 2L + + FFTSetup.FLOP * (K * 1L + k * (K * 4L)) + + FFTSetup.FLASSIGN * (2L + K * 1L + 1L + k * (2L + K * 2L + 2L) + + k * 2L + 2L) + + FFTSetup.INTALLOC * 11L + + FFTSetup.INTOP * (4L + K * 3L + k * (2L + K * 7L + 1L) + 1L + k * 3L) + + FFTSetup.INTASSIGN * (6L + K * 2L + 1L + k * (4L + K * 4L + 1L) + 2L + + k * 2L) + + FFTSetup.IDX * (K * 1L + 1L + k * (K * 4L + 2L) + k * 4L + 3L) + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTBruteForceReal */ + +/*==================================================================== +| DFTBruteForceRealDouble +\===================================================================*/ +static class DFTBruteForceRealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTBruteForceRealDouble */ + +/*.................................................................... + DFTBruteForceRealDouble private variables +....................................................................*/ +private double[] imBuffer; +private double[] imUnitRoot; +private double[] reBuffer; +private double[] reUnitRoot; + +/*.................................................................... + DFTBruteForceRealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTBruteForceRealDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final double[] reUnitRoot, + final double[] imUnitRoot +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; +} /* end DFTBruteForceRealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + double re = 0.0; + double im = 0.0; + final int transformLength = reUnitRoot.length; +//;IJ.log("DFTBruteForceRealDouble " + transformLength); + final int halfTransformLength = transformLength >> 1; + final int d1 = stride; + int k1 = startIndex; + int n0 = startIndex + d1; + if (0 == (transformLength & 1)) { + for (int n = 0; (n < halfTransformLength); n++) { + re += reData[k1]; + im += reData[k1]; + k1 += d1; + re += reData[k1]; + im -= reData[k1]; + k1 += d1; + } + reBuffer[startIndex] = re; + int n1 = startIndex + halfTransformLength * d1; + reBuffer[n1] = im; + for (int m = 1; (m < halfTransformLength); m++) { + re = 0.0; + im = 0.0; + int m1 = 0; + k1 = startIndex; + for (int n = 0; (n < transformLength); n++) { + re += reData[k1] * reUnitRoot[m1]; + im += reData[k1] * imUnitRoot[m1]; + m1 += m; + m1 -= transformLength * (m1 / transformLength); + k1 += d1; + } + reBuffer[n0] = re; + imBuffer[n0] = im; + n0 += d1; + } + reData[n0] = reBuffer[n0]; + imData[n0] = 0.0; + n0 -= d1; + for (int m = 1; (m < halfTransformLength); m++) { + reData[n0] = reBuffer[n0]; + imData[n0] = imBuffer[n0]; + n0 -= d1; + } + reData[n0] = reBuffer[n0]; + imData[n0] = 0.0; + } + else { + for (int n = 0; (n < transformLength); n++) { + re += reData[k1]; + k1 += d1; + } + reBuffer[startIndex] = re; + for (int m = 1; (m <= halfTransformLength); m++) { + re = 0.0; + im = 0.0; + int m1 = 0; + k1 = startIndex; + for (int n = 0; (n < transformLength); n++) { + re += reData[k1] * reUnitRoot[m1]; + im += reData[k1] * imUnitRoot[m1]; + m1 += m; + m1 -= transformLength * (m1 / transformLength); + k1 += d1; + } + reBuffer[n0] = re; + imBuffer[n0] = im; + n0 += d1; + } + int n1 = n0 - d1; + for (int m = 1; (m <= halfTransformLength); m++) { + reData[n1] = reBuffer[n1]; + imData[n1] = imBuffer[n1]; + n1 -= d1; + } + reData[n1] = reBuffer[n1]; + imData[n1] = 0.0; + } +} /* end run */ + +} /* end class DFTBruteForceRealDouble */ + +/*==================================================================== +| DFTBruteForceRealFloat +\===================================================================*/ +static class DFTBruteForceRealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTBruteForceRealFloat */ + +/*.................................................................... + DFTBruteForceRealFloat private variables +....................................................................*/ +private float[] imBuffer; +private float[] imUnitRoot; +private float[] reBuffer; +private float[] reUnitRoot; + +/*.................................................................... + DFTBruteForceRealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTBruteForceRealFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final float[] reUnitRoot, + final float[] imUnitRoot +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; +} /* end DFTBruteForceRealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + float re = 0.0F; + float im = 0.0F; + final int transformLength = reUnitRoot.length; +//;IJ.log("DFTBruteForceRealFloat " + transformLength); + final int halfTransformLength = transformLength >> 1; + final int d1 = stride; + int k1 = startIndex; + int n0 = startIndex + d1; + if (0 == (transformLength & 1)) { + for (int n = 0; (n < halfTransformLength); n++) { + re += reData[k1]; + im += reData[k1]; + k1 += d1; + re += reData[k1]; + im -= reData[k1]; + k1 += d1; + } + reBuffer[startIndex] = re; + int n1 = startIndex + halfTransformLength * d1; + reBuffer[n1] = im; + for (int m = 1; (m < halfTransformLength); m++) { + re = 0.0F; + im = 0.0F; + int m1 = 0; + k1 = startIndex; + for (int n = 0; (n < transformLength); n++) { + re += reData[k1] * reUnitRoot[m1]; + im += reData[k1] * imUnitRoot[m1]; + m1 += m; + m1 -= transformLength * (m1 / transformLength); + k1 += d1; + } + reBuffer[n0] = re; + imBuffer[n0] = im; + n0 += d1; + } + reData[n0] = reBuffer[n0]; + imData[n0] = 0.0F; + n0 -= d1; + for (int m = 1; (m < halfTransformLength); m++) { + reData[n0] = reBuffer[n0]; + imData[n0] = imBuffer[n0]; + n0 -= d1; + } + reData[n0] = reBuffer[n0]; + imData[n0] = 0.0F; + } + else { + for (int n = 0; (n < transformLength); n++) { + re += reData[k1]; + k1 += d1; + } + reBuffer[startIndex] = re; + for (int m = 1; (m <= halfTransformLength); m++) { + re = 0.0F; + im = 0.0F; + int m1 = 0; + k1 = startIndex; + for (int n = 0; (n < transformLength); n++) { + re += reData[k1] * reUnitRoot[m1]; + im += reData[k1] * imUnitRoot[m1]; + m1 += m; + m1 -= transformLength * (m1 / transformLength); + k1 += d1; + } + reBuffer[n0] = re; + imBuffer[n0] = im; + n0 += d1; + } + int n1 = n0 - d1; + for (int m = 1; (m <= halfTransformLength); m++) { + reData[n1] = reBuffer[n1]; + imData[n1] = imBuffer[n1]; + n1 -= d1; + } + reData[n1] = reBuffer[n1]; + imData[n1] = 0.0F; + } +} /* end run */ + +} /* end class DFTBruteForceRealFloat */ + +/*==================================================================== +| DFTCoprimeFactor +\===================================================================*/ +static class DFTCoprimeFactor + +{ /* begin class DFTCoprimeFactor */ + +/*.................................................................... + DFTCoprimeFactor static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int K1, + final int K2 +) { + if (FFTSetup.taboos.contains(new Integer(K1)) + || FFTSetup.taboos.contains(new Integer(K2))) { + return(-1L); + } + final long K = (long)K1 * (long)K2; + return(FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * 0L + + FFTSetup.FLASSIGN * (K * 2L + K * 2L) + + FFTSetup.INTALLOC * 11L + + FFTSetup.INTOP * (1L + K * 5L + 2L + K2 * 3L + 1L + K1 * 3L + K * 5L) + + FFTSetup.INTASSIGN * (4L + K * 3L + 4L + K2 * 2L + 2L + K1 * 2L + 2L + + K * 3L) + + FFTSetup.IDX * (K * 5L + K * 5L) + + FFTSetup.NEWOBJ * (K2 * 1L + K1 * 1L) + + (long)K2 * FFTSetup.cost(K1) + (long)K1 * FFTSetup.cost(K2) + ); +} /* end cost */ + +/*------------------------------------------------------------------*/ +static int[] getChineseRemainderShuffling ( + final int K1, + final int K2 +) { + final int K1K2 = K1 * K2; + int[] chineseRemainderShuffling = new int[K1K2]; + final int p1 = modularMultiplicativeInverse(K1, K2); + final int p2 = modularMultiplicativeInverse(K2, K1); + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + int q = p * p1; + for (int k1 = 0; (k1 < K1); k1++) { + chineseRemainderShuffling[q - K1K2 * (q / K1K2)] = p++; + q += p2 * K2; + } + } + return(chineseRemainderShuffling); +} /* end getChineseRemainderShuffling */ + +/*------------------------------------------------------------------*/ +static int[] getRuritanianShuffling ( + final int K1, + final int K2 +) { + final int K1K2 = K1 * K2; + int[] ruritanianShuffling = new int[K1K2]; + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + int q = p; + for (int k1 = 0; (k1 < K1); k1++) { + ruritanianShuffling[p++] = q - K1K2 * (q / K1K2); + q += K2; + } + } + return(ruritanianShuffling); +} /* end getRuritanianShuffling */ + +/*.................................................................... + DFTCoprimeFactor private methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static private int[] extendedGreatestCommonDivisor ( + int m, + int n +) { + if ((m < 1) || (n < 1)) { + return(null); + } + int a = 1; + int b = 0; + int p = 0; + int q = 1; + while (0 != n) { + final int i = m; + final int j = a; + final int k = b; + m = n; + a = p; + b = q; + final int f = i / n; + n = i - f * n; + p = j - f * p; + q = k - f * q; + } + return(new int[] { // a * m + b * n = gcd(m, n) + m, // gcd + a, + b + }); +} /* end extendedGreatestCommonDivisor */ + +/*------------------------------------------------------------------*/ +static private int modularMultiplicativeInverse ( + int n, + final int modulo +) { + if ((n < 1) || (modulo < 1)) { + return(0); + } + n = extendedGreatestCommonDivisor(n, modulo)[1]; + return((n < 0) ? (n + modulo) : (n)); +} /* end modularMultiplicativeInverse */ + +} /* end class DFTCoprimeFactor */ + +/*==================================================================== +| DFTCoprimeFactorDouble +\===================================================================*/ +static class DFTCoprimeFactorDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTCoprimeFactorDouble */ + +/*.................................................................... + DFTCoprimeFactorDouble private variables +....................................................................*/ +private double[] imBuffer; +private double[] reBuffer; +private int[] ruritanian; +private int[] chinese; +private int K1; + +/*.................................................................... + DFTCoprimeFactorDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTCoprimeFactorDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final int[] ruritanian, + final int[] chinese, + final int K1 +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.ruritanian = ruritanian; + this.chinese = chinese; + this.K1 = K1; +} /* end DFTCoprimeFactorDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int transformLength = ruritanian.length; +//;IJ.log("DFTCoprimeFactorDouble " + transformLength + " / " + K1); + final int K2 = transformLength / K1; + int p = startIndex; + for (int k = 0; (k < transformLength); k++) { + final int q = startIndex + ruritanian[k] * stride; + reBuffer[p] = reData[q]; + imBuffer[p] = imData[q]; + p += stride; + } + final int d1 = stride; + final int d2 = K1 * d1; + p = startIndex; + final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1)); + switch (fft1.algorithm) { + case BRUTEFORCE: { + for (int n = 0; (n < K2); n++) { + new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData, + p, d1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble).run(); + p += d2; + } + break; + } + case COPRIMEFACTOR: { + for (int n = 0; (n < K2); n++) { + new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData, + p, d1, + fft1.ruritanian, fft1.chinese, fft1.K1).run(); + p += d2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + for (int n = 0; (n < K2); n++) { + new DFTLength2Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH3: { + for (int n = 0; (n < K2); n++) { + new DFTLength3Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH4: { + for (int n = 0; (n < K2); n++) { + new DFTLength4Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH5: { + for (int n = 0; (n < K2); n++) { + new DFTLength5Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH6: { + for (int n = 0; (n < K2); n++) { + new DFTLength6Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH8: { + for (int n = 0; (n < K2); n++) { + new DFTLength8Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case MIXEDRADIX: { + for (int n = 0; (n < K2); n++) { + new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble, + fft1.K1).run(); + p += d2; + } + break; + } + case PADDEDRADER: { + for (int n = 0; (n < K2); n++) { + new DFTPaddedRaderDouble(reBuffer, imBuffer, + p, d1, fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular).run(); + p += d2; + } + break; + } + case RADER: { + for (int n = 0; (n < K2); n++) { + new DFTRaderDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular).run(); + p += d2; + } + break; + } + case RADIX2: { + for (int n = 0; (n < K2); n++) { + new DFTRadix2Double(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run(); + p += d2; + } + break; + } + case SPLITRADIX: { + for (int n = 0; (n < K2); n++) { + new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run(); + p += d2; + } + break; + } + } + p = startIndex; + final FFTSetup fft2 = FFTSetup.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + for (int n = 0; (n < K1); n++) { + new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + p += d1; + } + break; + } + case COPRIMEFACTOR: { + for (int n = 0; (n < K1); n++) { + new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData, + p, d2, fft2.ruritanian, fft2.chinese, fft2.K1).run(); + p += d1; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + for (int n = 0; (n < K1); n++) { + new DFTLength2Double(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH3: { + for (int n = 0; (n < K1); n++) { + new DFTLength3Double(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH4: { + for (int n = 0; (n < K1); n++) { + new DFTLength4Double(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH5: { + for (int n = 0; (n < K1); n++) { + new DFTLength5Double(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH6: { + for (int n = 0; (n < K1); n++) { + new DFTLength6Double(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH8: { + for (int n = 0; (n < K1); n++) { + new DFTLength8Double(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case MIXEDRADIX: { + for (int n = 0; (n < K1); n++) { + new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reUnitRootDouble, fft2.imUnitRootDouble, + fft2.K1).run(); + p += d1; + } + break; + } + case PADDEDRADER: { + for (int n = 0; (n < K1); n++) { + new DFTPaddedRaderDouble(reBuffer, imBuffer, + p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + p += d1; + } + break; + } + case RADER: { + for (int n = 0; (n < K1); n++) { + new DFTRaderDouble(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + p += d1; + } + break; + } + case RADIX2: { + for (int n = 0; (n < K1); n++) { + new DFTRadix2Double(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + p += d1; + } + break; + } + case SPLITRADIX: { + for (int n = 0; (n < K1); n++) { + new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + p += d1; + } + break; + } + } + p = startIndex; + for (int k = 0; (k < transformLength); k++) { + final int q = startIndex + chinese[k] * stride; + reData[p] = reBuffer[q]; + imData[p] = imBuffer[q]; + p += stride; + } +} /* end run */ + +} /* end class DFTCoprimeFactorDouble */ + +/*==================================================================== +| DFTCoprimeFactorFloat +\===================================================================*/ +static class DFTCoprimeFactorFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTCoprimeFactorFloat */ + +/*.................................................................... + DFTCoprimeFactorFloat private variables +....................................................................*/ +private float[] imBuffer; +private float[] reBuffer; +private int[] ruritanian; +private int[] chinese; +private int K1; + +/*.................................................................... + DFTCoprimeFactorFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTCoprimeFactorFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final int[] ruritanian, + final int[] chinese, + final int K1 +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.ruritanian = ruritanian; + this.chinese = chinese; + this.K1 = K1; +} /* end DFTCoprimeFactorFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int transformLength = ruritanian.length; +//;IJ.log("DFTCoprimeFactorFloat " + transformLength + " / " + K1); + final int K2 = transformLength / K1; + int p = startIndex; + for (int k = 0; (k < transformLength); k++) { + final int q = startIndex + ruritanian[k] * stride; + reBuffer[p] = reData[q]; + imBuffer[p] = imData[q]; + p += stride; + } + final int d1 = stride; + final int d2 = K1 * d1; + p = startIndex; + final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1)); + switch (fft1.algorithm) { + case BRUTEFORCE: { + for (int n = 0; (n < K2); n++) { + new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run(); + p += d2; + } + break; + } + case COPRIMEFACTOR: { + for (int n = 0; (n < K2); n++) { + new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.ruritanian, fft1.chinese, fft1.K1).run(); + p += d2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + for (int n = 0; (n < K2); n++) { + new DFTLength2Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH3: { + for (int n = 0; (n < K2); n++) { + new DFTLength3Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH4: { + for (int n = 0; (n < K2); n++) { + new DFTLength4Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH5: { + for (int n = 0; (n < K2); n++) { + new DFTLength5Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH6: { + for (int n = 0; (n < K2); n++) { + new DFTLength6Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH8: { + for (int n = 0; (n < K2); n++) { + new DFTLength8Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case MIXEDRADIX: { + for (int n = 0; (n < K2); n++) { + new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat, + fft1.K1).run(); + p += d2; + } + break; + } + case PADDEDRADER: { + for (int n = 0; (n < K2); n++) { + new DFTPaddedRaderFloat(reBuffer, imBuffer, + p, d1, fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular).run(); + p += d2; + } + break; + } + case RADER: { + for (int n = 0; (n < K2); n++) { + new DFTRaderFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular).run(); + p += d2; + } + break; + } + case RADIX2: { + for (int n = 0; (n < K2); n++) { + new DFTRadix2Float(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run(); + p += d2; + } + break; + } + case SPLITRADIX: { + for (int n = 0; (n < K2); n++) { + new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run(); + p += d2; + } + break; + } + } + p = startIndex; + final FFTSetup fft2 = FFTSetup.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + for (int n = 0; (n < K1); n++) { + new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + p += d1; + } + break; + } + case COPRIMEFACTOR: { + for (int n = 0; (n < K1); n++) { + new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData, + p, d2, fft2.ruritanian, fft2.chinese, fft2.K1).run(); + p += d1; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + for (int n = 0; (n < K1); n++) { + new DFTLength2Float(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH3: { + for (int n = 0; (n < K1); n++) { + new DFTLength3Float(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH4: { + for (int n = 0; (n < K1); n++) { + new DFTLength4Float(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH5: { + for (int n = 0; (n < K1); n++) { + new DFTLength5Float(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH6: { + for (int n = 0; (n < K1); n++) { + new DFTLength6Float(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH8: { + for (int n = 0; (n < K1); n++) { + new DFTLength8Float(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case MIXEDRADIX: { + for (int n = 0; (n < K1); n++) { + new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reUnitRootFloat, fft2.imUnitRootFloat, + fft2.K1).run(); + p += d1; + } + break; + } + case PADDEDRADER: { + for (int n = 0; (n < K1); n++) { + new DFTPaddedRaderFloat(reBuffer, imBuffer, + p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + p += d1; + } + break; + } + case RADER: { + for (int n = 0; (n < K1); n++) { + new DFTRaderFloat(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + p += d1; + } + break; + } + case RADIX2: { + for (int n = 0; (n < K1); n++) { + new DFTRadix2Float(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + p += d1; + } + break; + } + case SPLITRADIX: { + for (int n = 0; (n < K1); n++) { + new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + p += d1; + } + break; + } + } + p = startIndex; + for (int k = 0; (k < transformLength); k++) { + final int q = startIndex + chinese[k] * stride; + reData[p] = reBuffer[q]; + imData[p] = imBuffer[q]; + p += stride; + } +} /* end run */ + +} /* end class DFTCoprimeFactorFloat */ + +/*==================================================================== +| DFTCoprimeFactorReal +\===================================================================*/ +static class DFTCoprimeFactorReal + +{ /* begin class DFTCoprimeFactorReal */ + +/*.................................................................... + DFTCoprimeFactorReal static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int K1, + final int K2 +) { + if (FFTSetup.taboos.contains(new Integer(K1)) + || FFTSetup.taboos.contains(new Integer(K2))) { + return(-1L); + } + final long K = (long)K1 * (long)K2; + final long k = K >> 1L; + final long k1 = (long)(K1 >> 1); + final long k2 = (long)(K2 >> 1); + return(FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * ((K >> 2L) * 1L) + + FFTSetup.FLASSIGN * (K * 2L + (k + 1L) * 2L) + + FFTSetup.INTALLOC * 12L + + FFTSetup.INTOP * (3L + K * 5L + 4L + (long)K1 * 3L + 1L + + (k2 + 1L) * 3L + 2L + (k + 1L) * 6L) + + FFTSetup.INTASSIGN * (5L + K * 3L + 4L + (long)K1 * 2L + 2L + + (k2 + 1L) * 2L + 3L + (k + 1L) * 4L) + + FFTSetup.IDX * (K * 5L + (k + 1L) * 5L) + + FFTSetup.NEWOBJ * ((long)K1 * 1L + (long)K2 * 1L) + + k1 * FFTSetupDuoReal.cost(K2) + (k2 + 1L) * FFTSetup.cost(K1) + ); +} /* end cost */ + +/*------------------------------------------------------------------*/ +static int[] getTruncatedChineseRemainderShuffling ( + final int K1, + final int K2 +) { + final int K1K2 = K1 * K2; + final int halfK1K2 = (K1K2 >> 1) + 1; + final int halfK2 = (K2 >> 1) + 1; + int[] chineseRemainderShuffling = new int[halfK1K2]; + final int p1 = DFTCoprimeFactor.modularMultiplicativeInverse(K1, K2); + final int p2 = DFTCoprimeFactor.modularMultiplicativeInverse(K2, K1); + int n = 0; + for (int k2 = 0; (k2 < K2); k2++) { + int q = n * p1; + for (int k1 = 0; (k1 < K1); k1++) { + final int p = q - K1K2 * (q / K1K2); + if (p < halfK1K2) { + final int i0 = n / K1; + final int j0 = n - K1 * i0; + chineseRemainderShuffling[p] = (i0 < halfK2) ? (n) + : ((0 == j0) ? (n - K1K2) : (n - K1K2 - K1)); + } + q += p2 * K2; + n++; + } + } + return(chineseRemainderShuffling); +} /* end getTruncatedChineseRemainderShuffling */ + +} /* end class DFTCoprimeFactorReal */ + +/*==================================================================== +| DFTCoprimeFactorRealDouble +\===================================================================*/ +static class DFTCoprimeFactorRealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTCoprimeFactorRealDouble */ + +/*.................................................................... + DFTCoprimeFactorRealDouble private variables +....................................................................*/ +private double[] imBuffer; +private double[] reBuffer; +private int[] ruritanian; +private int[] chinese; +private int K1; + +/*.................................................................... + DFTCoprimeFactorRealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTCoprimeFactorRealDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final int[] ruritanian, + final int[] chinese, + final int K1 +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.ruritanian = ruritanian; + this.chinese = chinese; + this.K1 = K1; +} /* end DFTCoprimeFactorRealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int transformLength = ruritanian.length; +//;IJ.log("DFTCoprimeFactorRealDouble " + transformLength + " / " + K1); + final int K2 = transformLength / K1; + final int halfK2 = (K2 >> 1) + 1; + int p = startIndex; + for (int k = 0; (k < transformLength); k++) { + final int q = startIndex + ruritanian[k] * stride; + reBuffer[p] = reData[q]; + imBuffer[p] = imData[q]; + p += stride; + } + final int d1 = stride; + final int d2 = K1 * d1; + p = startIndex; + int k1 = 0; + if (1 == (K1 & 1)) { + final FFTSetupReal fft2 = FFTSetupReal.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealDouble(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealDouble(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.ruritanian, fft2.chinese, fft2.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealDouble(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + break; + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2RealDouble(reBuffer, imBuffer, p, d2).run(); + break; + } + case LENGTH3: { + new DFTLength3RealDouble(reBuffer, imBuffer, p, d2).run(); + break; + } + case LENGTH4: { + new DFTLength4RealDouble(reBuffer, imBuffer, p, d2).run(); + break; + } + case LENGTH5: { + new DFTLength5RealDouble(reBuffer, imBuffer, p, d2).run(); + break; + } + case LENGTH6: { + new DFTLength6RealDouble(reBuffer, imBuffer, p, d2).run(); + break; + } + case LENGTH8: { + new DFTLength8RealDouble(reBuffer, imBuffer, p, d2).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealDouble(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, + fft2.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealDouble(reBuffer, imBuffer, + p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealDouble(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealDouble(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootEvenDouble, fft2.imUnitRootEvenDouble, + fft2.reUnitRootOddDouble, fft2.imUnitRootOddDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealDouble(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + break; + } + } + p += d1; + k1++; + } + final FFTSetupDuoReal fft2 = + FFTSetupDuoReal.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + while (k1++ < K1) { + new DFTBruteForceRealDouble(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + p += d1; + } + break; + } + case COPRIMEFACTOR: { + while (k1++ < K1) { + new DFTCoprimeFactorRealDouble(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.ruritanian, fft2.chinese, fft2.K1).run(); + p += d1; + } + break; + } + case DUOREAL: { + while (k1++ < K1) { + new DFTDuoRealDouble(reBuffer, imBuffer, + reData, imData, p, p + d1, d2, K2).run(); + p += 2 * d1; + k1++; + } + break; + } + case EVENREAL: { + while (k1++ < K1) { + new DFTEvenRealDouble(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + p += d1; + } + break; + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + while (k1++ < K1) { + new DFTLength2RealDouble(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH3: { + while (k1++ < K1) { + new DFTLength3RealDouble(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH4: { + while (k1++ < K1) { + new DFTLength4RealDouble(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH5: { + while (k1++ < K1) { + new DFTLength5RealDouble(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH6: { + while (k1++ < K1) { + new DFTLength6RealDouble(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH8: { + while (k1++ < K1) { + new DFTLength8RealDouble(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case MIXEDRADIX: { + while (k1++ < K1) { + new DFTMixedRadixRealDouble(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, + fft2.K1).run(); + p += d1; + } + break; + } + case PADDEDRADER: { + while (k1++ < K1) { + new DFTPaddedRaderRealDouble(reBuffer, imBuffer, + p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + p += d1; + } + break; + } + case RADER: { + while (k1++ < K1) { + new DFTRaderRealDouble(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + p += d1; + } + break; + } + case RADIX2: { + while (k1++ < K1) { + new DFTRadix2RealDouble(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootEvenDouble, fft2.imUnitRootEvenDouble, + fft2.reUnitRootOddDouble, fft2.imUnitRootOddDouble).run(); + p += d1; + } + break; + } + case SPLITRADIX: { + while (k1++ < K1) { + new DFTSplitRadixRealDouble(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + p += d1; + } + break; + } + } + p = startIndex; + final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1)); + switch (fft1.algorithm) { + case BRUTEFORCE: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run(); + p += d2; + } + break; + } + case COPRIMEFACTOR: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.ruritanian, fft1.chinese, fft1.K1).run(); + p += d2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength2Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH3: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength3Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH4: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength4Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH5: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength5Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH6: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength6Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH8: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength8Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case MIXEDRADIX: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble, + fft1.K1).run(); + p += d2; + } + break; + } + case PADDEDRADER: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTPaddedRaderDouble(reBuffer, imBuffer, + p, d1, fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular).run(); + p += d2; + } + break; + } + case RADER: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTRaderDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular).run(); + p += d2; + } + break; + } + case RADIX2: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTRadix2Double(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run(); + p += d2; + } + break; + } + case SPLITRADIX: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run(); + p += d2; + } + break; + } + } + p = startIndex; + for (int k = 0, K = (transformLength >> 1) + 1; (k < K); k++) { + int q = chinese[k]; + if (q < 0) { + q = startIndex - q * stride; + reData[p] = reBuffer[q]; + imData[p] = -imBuffer[q]; + } + else { + q = startIndex + q * stride; + reData[p] = reBuffer[q]; + imData[p] = imBuffer[q]; + } + p += stride; + } +} /* end run */ + +} /* end class DFTCoprimeFactorRealDouble */ + +/*==================================================================== +| DFTCoprimeFactorRealFloat +\===================================================================*/ +static class DFTCoprimeFactorRealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTCoprimeFactorRealFloat */ + +/*.................................................................... + DFTCoprimeFactorRealFloat private variables +....................................................................*/ +private float[] imBuffer; +private float[] reBuffer; +private int[] ruritanian; +private int[] chinese; +private int K1; + +/*.................................................................... + DFTCoprimeFactorRealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTCoprimeFactorRealFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final int[] ruritanian, + final int[] chinese, + final int K1 +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.ruritanian = ruritanian; + this.chinese = chinese; + this.K1 = K1; +} /* end DFTCoprimeFactorRealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int transformLength = ruritanian.length; +//;IJ.log("DFTCoprimeFactorRealFloat " + transformLength + " / " + K1); + final int K2 = transformLength / K1; + final int halfK2 = (K2 >> 1) + 1; + int p = startIndex; + for (int k = 0; (k < transformLength); k++) { + final int q = startIndex + ruritanian[k] * stride; + reBuffer[p] = reData[q]; + imBuffer[p] = imData[q]; + p += stride; + } + final int d1 = stride; + final int d2 = K1 * d1; + p = startIndex; + int k1 = 0; + if (1 == (K1 & 1)) { + final FFTSetupReal fft2 = FFTSetupReal.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealFloat(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealFloat(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.ruritanian, fft2.chinese, fft2.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealFloat(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + break; + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2RealFloat(reBuffer, imBuffer, p, d2).run(); + break; + } + case LENGTH3: { + new DFTLength3RealFloat(reBuffer, imBuffer, p, d2).run(); + break; + } + case LENGTH4: { + new DFTLength4RealFloat(reBuffer, imBuffer, p, d2).run(); + break; + } + case LENGTH5: { + new DFTLength5RealFloat(reBuffer, imBuffer, p, d2).run(); + break; + } + case LENGTH6: { + new DFTLength6RealFloat(reBuffer, imBuffer, p, d2).run(); + break; + } + case LENGTH8: { + new DFTLength8RealFloat(reBuffer, imBuffer, p, d2).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealFloat(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, + fft2.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealFloat(reBuffer, imBuffer, + p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealFloat(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealFloat(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootEvenFloat, fft2.imUnitRootEvenFloat, + fft2.reUnitRootOddFloat, fft2.imUnitRootOddFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealFloat(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + break; + } + } + p += d1; + k1++; + } + final FFTSetupDuoReal fft2 = + FFTSetupDuoReal.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + while (k1++ < K1) { + new DFTBruteForceRealFloat(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + p += d1; + } + break; + } + case COPRIMEFACTOR: { + while (k1++ < K1) { + new DFTCoprimeFactorRealFloat(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.ruritanian, fft2.chinese, fft2.K1).run(); + p += d1; + } + break; + } + case DUOREAL: { + while (k1++ < K1) { + new DFTDuoRealFloat(reBuffer, imBuffer, + reData, imData, p, p + d1, d2, K2).run(); + p += 2 * d1; + k1++; + } + break; + } + case EVENREAL: { + while (k1++ < K1) { + new DFTEvenRealFloat(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + p += d1; + } + break; + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + while (k1++ < K1) { + new DFTLength2RealFloat(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH3: { + while (k1++ < K1) { + new DFTLength3RealFloat(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH4: { + while (k1++ < K1) { + new DFTLength4RealFloat(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH5: { + while (k1++ < K1) { + new DFTLength5RealFloat(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH6: { + while (k1++ < K1) { + new DFTLength6RealFloat(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case LENGTH8: { + while (k1++ < K1) { + new DFTLength8RealFloat(reBuffer, imBuffer, p, d2).run(); + p += d1; + } + break; + } + case MIXEDRADIX: { + while (k1++ < K1) { + new DFTMixedRadixRealFloat(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, + fft2.K1).run(); + p += d1; + } + break; + } + case PADDEDRADER: { + while (k1++ < K1) { + new DFTPaddedRaderRealFloat(reBuffer, imBuffer, + p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + p += d1; + } + break; + } + case RADER: { + while (k1++ < K1) { + new DFTRaderRealFloat(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + p += d1; + } + break; + } + case RADIX2: { + while (k1++ < K1) { + new DFTRadix2RealFloat(reBuffer, imBuffer, + reData, imData, p, d2, + fft2.reUnitRootEvenFloat, fft2.imUnitRootEvenFloat, + fft2.reUnitRootOddFloat, fft2.imUnitRootOddFloat).run(); + p += d1; + } + break; + } + case SPLITRADIX: { + while (k1++ < K1) { + new DFTSplitRadixRealFloat(reBuffer, imBuffer, reData, imData, + p, d2, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + p += d1; + } + break; + } + } + p = startIndex; + final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1)); + switch (fft1.algorithm) { + case BRUTEFORCE: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run(); + p += d2; + } + break; + } + case COPRIMEFACTOR: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.ruritanian, fft1.chinese, fft1.K1).run(); + p += d2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength2Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH3: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength3Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH4: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength4Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH5: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength5Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH6: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength6Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH8: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength8Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case MIXEDRADIX: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat, + fft1.K1).run(); + p += d2; + } + break; + } + case PADDEDRADER: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTPaddedRaderFloat(reBuffer, imBuffer, + p, d1, fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular).run(); + p += d2; + } + break; + } + case RADER: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTRaderFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular).run(); + p += d2; + } + break; + } + case RADIX2: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTRadix2Float(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run(); + p += d2; + } + break; + } + case SPLITRADIX: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run(); + p += d2; + } + break; + } + } + p = startIndex; + for (int k = 0, K = (transformLength >> 1) + 1; (k < K); k++) { + int q = chinese[k]; + if (q < 0) { + q = startIndex - q * stride; + reData[p] = reBuffer[q]; + imData[p] = -imBuffer[q]; + } + else { + q = startIndex + q * stride; + reData[p] = reBuffer[q]; + imData[p] = imBuffer[q]; + } + p += stride; + } +} /* end run */ + +} /* end class DFTCoprimeFactorRealFloat */ + +/*==================================================================== +| DFTDuoReal +\===================================================================*/ +static class DFTDuoReal + +{ /* begin class DFTDuoReal */ + +/*.................................................................... + DFTDuoReal static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int transformLength +) { + if (FFTSetup.taboos.contains(new Integer(transformLength / 2))) { + return(-1L); + } + final long K = (long)transformLength; + final long k = K >> 1L; + return(FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * (k * 8L) + + FFTSetup.FLASSIGN * (K * 2L + 4L + k * 4L) + + FFTSetup.INTALLOC * 6L + + FFTSetup.INTOP * (K * 4L + 9L + k * 5L) + + FFTSetup.INTASSIGN * (3L + K * 3L + 7L + k * 4L) + + FFTSetup.IDX * (K * 4L + 6L + k * 8L) + + FFTSetup.NEWOBJ * 1L + + FFTSetup.cost(transformLength) + ); +} /* end cost */ + +} /* end class DFTDuoReal */ + +/*==================================================================== +| DFTDuoRealDouble +\===================================================================*/ +static class DFTDuoRealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTDuoRealDouble */ + +/*.................................................................... + DFTDuoRealDouble static variables +....................................................................*/ +private double[] imBuffer; +private double[] reBuffer; +private int duoStartIndex; +private int transformLength; + +/*.................................................................... + DFTDuoRealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTDuoRealDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int duoStartIndex, + final int stride, + final int transformLength +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.duoStartIndex = duoStartIndex; + this.transformLength = transformLength; +} /* end DFTDuoRealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTDuoRealDouble " + transformLength); + int i0 = startIndex; + int i1 = duoStartIndex; + for (int m = 0; (m < transformLength); m++) { + reBuffer[i0] = reData[i0]; + imBuffer[i0] = reData[i1]; + i0 += stride; + i1 += stride; + } + final FFTSetup fft = FFTSetup.transforms.get(new Integer(transformLength)); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + new DFTLength2Double(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH3: { + new DFTLength3Double(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH4: { + new DFTLength4Double(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH5: { + new DFTLength5Double(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH6: { + new DFTLength6Double(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH8: { + new DFTLength8Double(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootDouble, fft.imUnitRootDouble, + fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderDouble(reBuffer, imBuffer, + startIndex, stride, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderDouble(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2Double(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + i0 = startIndex; + i1 = duoStartIndex; + reData[i0] = reBuffer[i0]; + imData[i0] = 0.0; + reData[i1] = imBuffer[i0]; + imData[i1] = 0.0; + i0 += stride; + i1 += stride; + int j0 = startIndex + (transformLength - 1) * stride; + if (0 == (transformLength & 1)) { + for (int m = 1, M = transformLength >> 1; (m < M); m++) { + reData[i0] = 0.5 * (reBuffer[i0] + reBuffer[j0]); + imData[i0] = 0.5 * (imBuffer[i0] - imBuffer[j0]); + reData[i1] = 0.5 * (imBuffer[i0] + imBuffer[j0]); + imData[i1] = -0.5 * (reBuffer[i0] - reBuffer[j0]); + i0 += stride; + i1 += stride; + j0 -= stride; + } + reData[i0] = reBuffer[i0]; + imData[i0] = 0.0; + reData[i1] = imBuffer[i0]; + imData[i1] = 0.0; + } + else { + for (int m = 1, M = transformLength >> 1; (m <= M); m++) { + reData[i0] = 0.5 * (reBuffer[i0] + reBuffer[j0]); + imData[i0] = 0.5 * (imBuffer[i0] - imBuffer[j0]); + reData[i1] = 0.5 * (imBuffer[i0] + imBuffer[j0]); + imData[i1] = -0.5 * (reBuffer[i0] - reBuffer[j0]); + i0 += stride; + i1 += stride; + j0 -= stride; + } + } +} /* end run */ + +} /* end class DFTDuoRealDouble */ + +/*==================================================================== +| DFTDuoRealFloat +\===================================================================*/ +static class DFTDuoRealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTDuoRealFloat */ + +/*.................................................................... + DFTDuoRealFloat static variables +....................................................................*/ +private float[] imBuffer; +private float[] reBuffer; +private int duoStartIndex; +private int transformLength; + +/*.................................................................... + DFTDuoRealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTDuoRealFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int duoStartIndex, + final int stride, + final int transformLength +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.duoStartIndex = duoStartIndex; + this.transformLength = transformLength; +} /* end DFTDuoRealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTDuoRealFloat " + transformLength); + int i0 = startIndex; + int i1 = duoStartIndex; + for (int m = 0; (m < transformLength); m++) { + reBuffer[i0] = reData[i0]; + imBuffer[i0] = reData[i1]; + i0 += stride; + i1 += stride; + } + final FFTSetup fft = FFTSetup.transforms.get(new Integer(transformLength)); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + new DFTLength2Float(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH3: { + new DFTLength3Float(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH4: { + new DFTLength4Float(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH5: { + new DFTLength5Float(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH6: { + new DFTLength6Float(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH8: { + new DFTLength8Float(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootFloat, fft.imUnitRootFloat, + fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderFloat(reBuffer, imBuffer, + startIndex, stride, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderFloat(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2Float(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + i0 = startIndex; + i1 = duoStartIndex; + reData[i0] = reBuffer[i0]; + imData[i0] = 0.0F; + reData[i1] = imBuffer[i0]; + imData[i1] = 0.0F; + i0 += stride; + i1 += stride; + int j0 = startIndex + (transformLength - 1) * stride; + if (0 == (transformLength & 1)) { + for (int m = 1, M = transformLength >> 1; (m < M); m++) { + reData[i0] = 0.5F * (reBuffer[i0] + reBuffer[j0]); + imData[i0] = 0.5F * (imBuffer[i0] - imBuffer[j0]); + reData[i1] = 0.5F * (imBuffer[i0] + imBuffer[j0]); + imData[i1] = -0.5F * (reBuffer[i0] - reBuffer[j0]); + i0 += stride; + i1 += stride; + j0 -= stride; + } + reData[i0] = reBuffer[i0]; + imData[i0] = 0.0F; + reData[i1] = imBuffer[i0]; + imData[i1] = 0.0F; + } + else { + for (int m = 1, M = transformLength >> 1; (m <= M); m++) { + reData[i0] = 0.5F * (reBuffer[i0] + reBuffer[j0]); + imData[i0] = 0.5F * (imBuffer[i0] - imBuffer[j0]); + reData[i1] = 0.5F * (imBuffer[i0] + imBuffer[j0]); + imData[i1] = -0.5F * (reBuffer[i0] - reBuffer[j0]); + i0 += stride; + i1 += stride; + j0 -= stride; + } + } +} /* end run */ + +} /* end class DFTDuoRealFloat */ + +/*==================================================================== +| DFTEvenReal +\===================================================================*/ +static class DFTEvenReal + +{ /* begin class DFTEvenReal */ + +/*.................................................................... + DFTEvenReal static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int transformLength +) { + if (FFTSetup.taboos.contains(new Integer(transformLength / 2))) { + return(-1L); + } + final long k = (long)(transformLength >> 1); + return(FFTSetup.FLALLOC * 4L + + FFTSetup.FLOP * (1L + (k - 1L) * 14L + 1L) + + FFTSetup.FLASSIGN * (k * 2L + 2L + (k - 1L) * 6L + 2L) + + FFTSetup.INTALLOC * 7L + + FFTSetup.INTOP * (1L + k * 5L + 5L + (k - 1L) * 4L) + + FFTSetup.INTASSIGN * (6L + k * 4L + 4L + (k - 1L) * 3L) + + FFTSetup.IDX * (k * 4L + 4L + (k - 1L) * 12L + 4L) + + FFTSetup.NEWOBJ * 1L + + FFTSetup.cost(transformLength >> 1) + ); +} /* end cost */ + +} /* end class DFTEvenReal */ + +/*==================================================================== +| DFTEvenRealDouble +\===================================================================*/ +static class DFTEvenRealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTEvenRealDouble */ + +/*.................................................................... + DFTEvenRealDouble static variables +....................................................................*/ +private double[] imBuffer; +private double[] imUnitRoot; +private double[] reBuffer; +private double[] reUnitRoot; + +/*.................................................................... + DFTEvenRealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTEvenRealDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final double[] reUnitRoot, + final double[] imUnitRoot +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; +} /* end DFTEvenRealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int halfTransformLength = reUnitRoot.length; +//;IJ.log("DFTEvenRealDouble " + (2 * halfTransformLength)); + final int doubleStride = stride << 1; + int m0 = startIndex; + int i0 = startIndex; + int i1 = startIndex + stride; + for (int m = 0; (m < halfTransformLength); m++) { + reBuffer[m0] = reData[i0]; + imBuffer[m0] = reData[i1]; + m0 += stride; + i0 += doubleStride; + i1 += doubleStride; + } + final FFTSetup fft = + FFTSetup.transforms.get(new Integer(halfTransformLength)); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + new DFTLength2Double(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH3: { + new DFTLength3Double(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH4: { + new DFTLength4Double(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH5: { + new DFTLength5Double(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH6: { + new DFTLength6Double(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH8: { + new DFTLength8Double(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootDouble, fft.imUnitRootDouble, + fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderDouble(reBuffer, imBuffer, + startIndex, stride, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderDouble(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2Double(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + i0 = startIndex; + reData[i0] = reBuffer[i0] + imBuffer[i0]; + imData[i0] = 0.0; + i0 += stride; + i1 = startIndex + (halfTransformLength - 1) * stride; + for (int m = 1; (m < halfTransformLength); m++) { + final double re = reBuffer[i0] - reBuffer[i1]; + final double im = imBuffer[i0] + imBuffer[i1]; + final double reRoot = reUnitRoot[m]; + final double imRoot = imUnitRoot[m]; + reData[i0] = 0.5 * (reBuffer[i0] + reBuffer[i1] + + re * imRoot + im * reRoot); + imData[i0] = 0.5 * (imBuffer[i0] - imBuffer[i1] + - re * reRoot + im * imRoot); + i0 += stride; + i1 -= stride; + } + reData[i0] = reBuffer[startIndex] - imBuffer[startIndex]; + imData[i0] = 0.0; +} /* end run */ + +/*.................................................................... + DFTEvenRealDouble static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static double[] getImUnitRoot ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final double[] imUnitRoot = new double[halfTransformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + imUnitRoot[k] = sin((double)k * angularStep); + } + return(imUnitRoot); +} /* end getImUnitRoot */ + +/*------------------------------------------------------------------*/ +static double[] getReUnitRoot ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final double[] reUnitRoot = new double[halfTransformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + reUnitRoot[k] = cos((double)k * angularStep); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +} /* end class DFTEvenRealDouble */ + +/*==================================================================== +| DFTEvenRealFloat +\===================================================================*/ +static class DFTEvenRealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTEvenRealFloat */ + +/*.................................................................... + DFTEvenRealFloat static variables +....................................................................*/ +private float[] imBuffer; +private float[] imUnitRoot; +private float[] reBuffer; +private float[] reUnitRoot; + +/*.................................................................... + DFTEvenRealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTEvenRealFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final float[] reUnitRoot, + final float[] imUnitRoot +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; +} /* end DFTEvenRealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int halfTransformLength = reUnitRoot.length; +//;IJ.log("DFTEvenRealFloat " + (2 * halfTransformLength)); + final int doubleStride = stride << 1; + int m0 = startIndex; + int i0 = startIndex; + int i1 = startIndex + stride; + for (int m = 0; (m < halfTransformLength); m++) { + reBuffer[m0] = reData[i0]; + imBuffer[m0] = reData[i1]; + m0 += stride; + i0 += doubleStride; + i1 += doubleStride; + } + final FFTSetup fft = + FFTSetup.transforms.get(new Integer(halfTransformLength)); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + new DFTLength2Float(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH3: { + new DFTLength3Float(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH4: { + new DFTLength4Float(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH5: { + new DFTLength5Float(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH6: { + new DFTLength6Float(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case LENGTH8: { + new DFTLength8Float(reBuffer, imBuffer, + startIndex, stride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootFloat, fft.imUnitRootFloat, + fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderFloat(reBuffer, imBuffer, + startIndex, stride, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderFloat(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2Float(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData, + startIndex, stride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + i0 = startIndex; + reData[i0] = reBuffer[i0] + imBuffer[i0]; + imData[i0] = 0.0F; + i0 += stride; + i1 = startIndex + (halfTransformLength - 1) * stride; + for (int m = 1; (m < halfTransformLength); m++) { + final float re = reBuffer[i0] - reBuffer[i1]; + final float im = imBuffer[i0] + imBuffer[i1]; + final float reRoot = reUnitRoot[m]; + final float imRoot = imUnitRoot[m]; + reData[i0] = 0.5F * (reBuffer[i0] + reBuffer[i1] + + re * imRoot + im * reRoot); + imData[i0] = 0.5F * (imBuffer[i0] - imBuffer[i1] + - re * reRoot + im * imRoot); + i0 += stride; + i1 -= stride; + } + reData[i0] = reBuffer[startIndex] - imBuffer[startIndex]; + imData[i0] = 0.0F; +} /* end run */ + +/*.................................................................... + DFTEvenRealFloat static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static float[] getImUnitRoot ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final float[] imUnitRoot = new float[halfTransformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + imUnitRoot[k] = (float)sin((double)((float)k * angularStep)); + } + return(imUnitRoot); +} /* end getImUnitRoot */ + +/*------------------------------------------------------------------*/ +static float[] getReUnitRoot ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final float[] reUnitRoot = new float[halfTransformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + reUnitRoot[k] = (float)cos((double)((float)k * angularStep)); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +} /* end class DFTEvenRealFloat */ + +/*==================================================================== +| DFTLength2 +\===================================================================*/ +static class DFTLength2 + +{ /* begin class DFTLength2 */ + +/*.................................................................... + DFTLength2 static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( +) { + return(FFTSetup.FLALLOC * 3L + + FFTSetup.FLOP * 4L + + FFTSetup.FLASSIGN * 8L + + FFTSetup.INTALLOC * 1L + + FFTSetup.INTOP * 1L + + FFTSetup.INTASSIGN * 1L + + FFTSetup.IDX * 8L + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTLength2 */ + +/*==================================================================== +| DFTLength2Double +\===================================================================*/ +static class DFTLength2Double + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTLength2Double */ + +/*.................................................................... + DFTLength2Double constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength2Double ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength2Double */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength2Double"); + final int i1 = startIndex + stride; + final double re1 = reData[i1]; + final double im1 = imData[i1]; + double butterfly = reData[startIndex] - re1; + reData[startIndex] += re1; + reData[i1] = butterfly; + butterfly = imData[startIndex] - im1; + imData[startIndex] += im1; + imData[i1] = butterfly; +} /* end run */ + +} /* end class DFTLength2Double */ + +/*==================================================================== +| DFTLength2Float +\===================================================================*/ +static class DFTLength2Float + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTLength2Float */ + +/*.................................................................... + DFTLength2Float constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength2Float ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength2Float */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength2Float"); + final int i1 = startIndex + stride; + final float re1 = reData[i1]; + final float im1 = imData[i1]; + float butterfly = reData[startIndex] - re1; + reData[startIndex] += re1; + reData[i1] = butterfly; + butterfly = imData[startIndex] - im1; + imData[startIndex] += im1; + imData[i1] = butterfly; +} /* end run */ + +} /* end class DFTLength2Float */ + +/*==================================================================== +| DFTLength2Real +\===================================================================*/ +static class DFTLength2Real + +{ /* begin class DFTLength2Real */ + +/*.................................................................... + DFTLength2Real static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( +) { + return(FFTSetup.FLALLOC * 2L + + FFTSetup.FLOP * 2L + + FFTSetup.FLASSIGN * 6L + + FFTSetup.INTALLOC * 1L + + FFTSetup.INTOP * 1L + + FFTSetup.INTASSIGN * 1L + + FFTSetup.IDX * 6L + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTLength2Real */ + +/*==================================================================== +| DFTLength2RealDouble +\===================================================================*/ +static class DFTLength2RealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTLength2RealDouble */ + +/*.................................................................... + DFTLength2RealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength2RealDouble ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength2RealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength2RealDouble"); + final int i1 = startIndex + stride; + final double re1 = reData[i1]; + double butterfly = reData[startIndex] - re1; + reData[startIndex] += re1; + reData[i1] = butterfly; + imData[startIndex] = 0.0; + imData[i1] = 0.0; +} /* end run */ + +} /* end class DFTLength2RealDouble */ + +/*==================================================================== +| DFTLength2RealFloat +\===================================================================*/ +static class DFTLength2RealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTLength2RealFloat */ + +/*.................................................................... + DFTLength2RealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength2RealFloat ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength2RealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength2RealFloat"); + final int i1 = startIndex + stride; + final float re1 = reData[i1]; + float butterfly = reData[startIndex] - re1; + reData[startIndex] += re1; + reData[i1] = butterfly; + imData[startIndex] = 0.0F; + imData[i1] = 0.0F; +} /* end run */ + +} /* end class DFTLength2RealFloat */ + +/*==================================================================== +| DFTLength3 +\===================================================================*/ +static class DFTLength3 + +{ /* begin class DFTLength3 */ + +/*.................................................................... + DFTLength3 static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( +) { + return(FFTSetup.FLALLOC * 6L + + FFTSetup.FLOP * 20L + + FFTSetup.FLASSIGN * 16L + + FFTSetup.INTALLOC * 2L + + FFTSetup.INTOP * 2L + + FFTSetup.INTASSIGN * 2L + + FFTSetup.IDX * 12L + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTLength3 */ + +/*==================================================================== +| DFTLength3Double +\===================================================================*/ +static class DFTLength3Double + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTLength3Double */ + +/*.................................................................... + DFTLength3Double static variables +....................................................................*/ +private static final double SQRT3 = sqrt(3.0); + +/*.................................................................... + DFTLength3Double constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength3Double ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength3Double */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength3Double"); + final int i1 = startIndex + stride; + final int i2 = i1 + stride; + double reButterfly = reData[i1]; + final double imButterfly = imData[i1]; + double reDragonfly = reData[i2]; + final double imDragonfly = imData[i2]; + final double reLadybug = SQRT3 * (imDragonfly - imButterfly); + final double imLadybug = SQRT3 * (reButterfly - reDragonfly); + reButterfly += reDragonfly; + reDragonfly = reData[startIndex]; + reData[i1] = reDragonfly - 0.5 * (reLadybug + reButterfly); + reData[i2] = reDragonfly + 0.5 * (reLadybug - reButterfly); + reData[startIndex] += reButterfly; + reDragonfly = imData[startIndex]; + reButterfly = imButterfly + imDragonfly; + imData[i1] = reDragonfly - 0.5 * (imLadybug + reButterfly); + imData[i2] = reDragonfly + 0.5 * (imLadybug - reButterfly); + imData[startIndex] += reButterfly; +} /* end run */ + +} /* end class DFTLength3Double */ + +/*==================================================================== +| DFTLength3Float +\===================================================================*/ +static class DFTLength3Float + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTLength3Float */ + +/*.................................................................... + DFTLength3Float static variables +....................................................................*/ +private static final float SQRT3 = (float)sqrt(3.0); + +/*.................................................................... + DFTLength3Float constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength3Float ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength3Float */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength3Float"); + final int i1 = startIndex + stride; + final int i2 = i1 + stride; + float reButterfly = reData[i1]; + final float imButterfly = imData[i1]; + float reDragonfly = reData[i2]; + final float imDragonfly = imData[i2]; + final float reLadybug = SQRT3 * (imDragonfly - imButterfly); + final float imLadybug = SQRT3 * (reButterfly - reDragonfly); + reButterfly += reDragonfly; + reDragonfly = reData[startIndex]; + reData[i1] = reDragonfly - 0.5F * (reLadybug + reButterfly); + reData[i2] = reDragonfly + 0.5F * (reLadybug - reButterfly); + reData[startIndex] += reButterfly; + reDragonfly = imData[startIndex]; + reButterfly = imButterfly + imDragonfly; + imData[i1] = reDragonfly - 0.5F * (imLadybug + reButterfly); + imData[i2] = reDragonfly + 0.5F * (imLadybug - reButterfly); + imData[startIndex] += reButterfly; +} /* end run */ + +} /* end class DFTLength3Float */ + +/*==================================================================== +| DFTLength3Real +\===================================================================*/ +static class DFTLength3Real + +{ /* begin class DFTLength3Real */ + +/*.................................................................... + DFTLength3Real static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( +) { + return(FFTSetup.FLALLOC * 2L + + FFTSetup.FLOP * 6L + + FFTSetup.FLASSIGN * 6L + + FFTSetup.INTALLOC * 2L + + FFTSetup.INTOP * 2L + + FFTSetup.INTASSIGN * 2L + + FFTSetup.IDX * 9L + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTLength3Real */ + +/*==================================================================== +| DFTLength3RealDouble +\===================================================================*/ +static class DFTLength3RealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTLength3RealDouble */ + +/*.................................................................... + DFTLength3RealDouble static variables +....................................................................*/ +private static final double HALFSQRT3 = 0.5 * sqrt(3.0); + +/*.................................................................... + DFTLength3RealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength3RealDouble ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength3RealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength3RealDouble"); + final int i1 = startIndex + stride; + final int i2 = i1 + stride; + final double reButterfly = reData[i2] + reData[i1]; + final double imButterfly = HALFSQRT3 * (reData[i2] - reData[i1]); + reData[i1] = reData[startIndex] - 0.5 * reButterfly; + reData[startIndex] += reButterfly; + imData[startIndex] = 0.0; + imData[i1] = imButterfly; +} /* end run */ + +} /* end class DFTLength3RealDouble */ + +/*==================================================================== +| DFTLength3RealFloat +\===================================================================*/ +static class DFTLength3RealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTLength3RealFloat */ + +/*.................................................................... + DFTLength3RealFloat static variables +....................................................................*/ +private static final float HALFSQRT3 = 0.5F * (float)sqrt(3.0); + +/*.................................................................... + DFTLength3RealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength3RealFloat ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength3RealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength3RealFloat"); + final int i1 = startIndex + stride; + final int i2 = i1 + stride; + final float reButterfly = reData[i2] + reData[i1]; + final float imButterfly = HALFSQRT3 * (reData[i2] - reData[i1]); + reData[i1] = reData[startIndex] - 0.5F * reButterfly; + reData[startIndex] += reButterfly; + imData[startIndex] = 0.0F; + imData[i1] = imButterfly; +} /* end run */ + +} /* end class DFTLength3RealFloat */ + +/*==================================================================== +| DFTLength4 +\===================================================================*/ +static class DFTLength4 + +{ /* begin class DFTLength4 */ + +/*.................................................................... + DFTLength4 static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( +) { + return(FFTSetup.FLALLOC * 8L + + FFTSetup.FLOP * 16L + + FFTSetup.FLASSIGN * 24L + + FFTSetup.INTALLOC * 1L + + FFTSetup.INTOP * 11L + + FFTSetup.INTASSIGN * 10L + + FFTSetup.IDX * 16L + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTLength4 */ + +/*==================================================================== +| DFTLength4Double +\===================================================================*/ +static class DFTLength4Double + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTLength4Double */ + +/*.................................................................... + DFTLength4Double constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength4Double ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength4Double */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength4Double"); + double data0 = reData[startIndex]; + int k = startIndex + stride; + double data1 = reData[k]; + k += stride; + double data2 = reData[k]; + k += stride; + double data3 = reData[k]; + double butterfly = data0 + data2; + double dragonfly = data1 + data3; + double ladybug = data0 - data2; + double moth = data1 - data3; + data3 = imData[k]; + k -= stride; + data2 = imData[k]; + k -= stride; + data1 = imData[k]; + k += stride; + data0 = imData[startIndex]; + reData[startIndex] = butterfly + dragonfly; + reData[k] = butterfly - dragonfly; + k -= stride; + butterfly = data0 - data2; + dragonfly = data1 - data3; + reData[k] = ladybug + dragonfly; + k += stride + stride; + reData[k] = ladybug - dragonfly; + dragonfly = data0 + data2; + ladybug = data1 + data3; + imData[k] = butterfly + moth; + k -= stride; + imData[k] = dragonfly - ladybug; + k -= stride; + imData[k] = butterfly - moth; + imData[startIndex] = dragonfly + ladybug; +} /* end run */ + +} /* end class DFTLength4Double */ + +/*==================================================================== +| DFTLength4Float +\===================================================================*/ +static class DFTLength4Float + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTLength4Float */ + +/*.................................................................... + DFTLength4Float constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength4Float ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength4Float */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength4Float"); + float data0 = reData[startIndex]; + int k = startIndex + stride; + float data1 = reData[k]; + k += stride; + float data2 = reData[k]; + k += stride; + float data3 = reData[k]; + float butterfly = data0 + data2; + float dragonfly = data1 + data3; + float ladybug = data0 - data2; + float moth = data1 - data3; + data3 = imData[k]; + k -= stride; + data2 = imData[k]; + k -= stride; + data1 = imData[k]; + k += stride; + data0 = imData[startIndex]; + reData[startIndex] = butterfly + dragonfly; + reData[k] = butterfly - dragonfly; + k -= stride; + butterfly = data0 - data2; + dragonfly = data1 - data3; + reData[k] = ladybug + dragonfly; + k += stride + stride; + reData[k] = ladybug - dragonfly; + dragonfly = data0 + data2; + ladybug = data1 + data3; + imData[k] = butterfly + moth; + k -= stride; + imData[k] = dragonfly - ladybug; + k -= stride; + imData[k] = butterfly - moth; + imData[startIndex] = dragonfly + ladybug; +} /* end run */ + +} /* end class DFTLength4Float */ + +/*==================================================================== +| DFTLength4Real +\===================================================================*/ +static class DFTLength4Real + +{ /* begin class DFTLength4Real */ + +/*.................................................................... + DFTLength4Real static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( +) { + return(FFTSetup.FLALLOC * 2L + + FFTSetup.FLOP * 6L + + FFTSetup.FLASSIGN * 8L + + FFTSetup.INTALLOC * 3L + + FFTSetup.INTOP * 3L + + FFTSetup.INTASSIGN * 3L + + FFTSetup.IDX * 14L + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTLength4Real */ + +/*==================================================================== +| DFTLength4RealDouble +\===================================================================*/ +static class DFTLength4RealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTLength4RealDouble */ + +/*.................................................................... + DFTLength4RealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength4RealDouble ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength4RealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength4RealDouble"); + final int i1 = startIndex + stride; + final int i2 = i1 + stride; + final int i3 = i2 + stride; + final double butterfly = reData[startIndex] + reData[i2]; + final double dragonfly = reData[i1] + reData[i3]; + imData[startIndex] = 0.0; + imData[i1] = reData[i3] - reData[i1]; + imData[i2] = 0.0; + reData[i1] = reData[startIndex] - reData[i2]; + reData[i2] = butterfly - dragonfly; + reData[startIndex] = butterfly + dragonfly; +} /* end run */ + +} /* end class DFTLength4RealDouble */ + +/*==================================================================== +| DFTLength4RealFloat +\===================================================================*/ +static class DFTLength4RealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTLength4RealFloat */ + +/*.................................................................... + DFTLength4RealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength4RealFloat ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength4RealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength4RealFloat"); + final int i1 = startIndex + stride; + final int i2 = i1 + stride; + final int i3 = i2 + stride; + final float butterfly = reData[startIndex] + reData[i2]; + final float dragonfly = reData[i1] + reData[i3]; + imData[startIndex] = 0.0F; + imData[i1] = reData[i3] - reData[i1]; + imData[i2] = 0.0F; + reData[i1] = reData[startIndex] - reData[i2]; + reData[i2] = butterfly - dragonfly; + reData[startIndex] = butterfly + dragonfly; +} /* end run */ + +} /* end class DFTLength4RealFloat */ + +/*==================================================================== +| DFTLength5 +\===================================================================*/ +static class DFTLength5 + +{ /* begin class DFTLength5 */ + +/*.................................................................... + DFTLength5 static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( +) { + return(FFTSetup.FLALLOC * 18L + + FFTSetup.FLOP * 52L + + FFTSetup.FLASSIGN * 50L + + FFTSetup.INTALLOC * 1L + + FFTSetup.INTOP * 7L + + FFTSetup.INTASSIGN * 7L + + FFTSetup.IDX * 20L + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTLength5 */ + +/*==================================================================== +| DFTLength5Double +\===================================================================*/ +static class DFTLength5Double + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTLength5Double */ + +/*.................................................................... + DFTLength5Double static variables +....................................................................*/ +private static final double IM5 = sqrt((5.0 - sqrt(5.0)) / 32.0); +private static final double RE5 = sqrt((5.0 + sqrt(5.0)) / 32.0); +private static final double S5 = sqrt(5.0 / 16.0); + +/*.................................................................... + DFTLength5Double constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength5Double ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength5Double */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength5Double"); + int k = startIndex + stride; + double re1 = reData[k]; + double im1 = imData[k]; + k += stride; + double re2 = reData[k]; + double im2 = imData[k]; + k += stride; + double re3 = reData[k]; + double im3 = imData[k]; + k += stride; + double re4 = reData[k]; + double im4 = imData[k]; + double reDragonfly = re1 + re4; + double imDragonfly = im1 + im4; + double reLadybug = re1 - re4; + double imLadybug = im1 - im4; + double reMoth = re2 + re3; + double imMoth = im2 + im3; + double reBeetle = re2 - re3; + double imBeetle = im2 - im3; + final double reButterfly = reDragonfly + reMoth; + final double imButterfly = imDragonfly + imMoth; + re1 = imLadybug + reBeetle; + im1 = reLadybug - imBeetle; + re2 = IM5 * im1 - RE5 * re1; + im2 = IM5 * re1 + RE5 * im1; + re1 = -0.25 * reButterfly; + im1 = -0.25 * imButterfly; + re3 = reBeetle - imLadybug; + im3 = imBeetle + reLadybug; + re4 = RE5 * re3 - IM5 * im3; + im4 = RE5 * im3 + IM5 * re3; + re3 = S5 * (reDragonfly - reMoth); + im3 = S5 * (imDragonfly - imMoth); + reDragonfly = reData[startIndex] + re1; + imDragonfly = imData[startIndex] + im1; + reLadybug = reDragonfly - re3; + imLadybug = imDragonfly - im3; + reDragonfly += re3; + imDragonfly += im3; + reMoth = re2 + re4; + imMoth = im2 + im4; + reBeetle = re2 - re4; + imBeetle = im2 - im4; + reData[k] = reDragonfly + reMoth; + imData[k] = imDragonfly + imMoth; + k -= stride; + reData[k] = reLadybug - imBeetle; + imData[k] = imLadybug + reBeetle; + k -= stride; + reData[k] = reLadybug + imBeetle; + imData[k] = imLadybug - reBeetle; + k -= stride; + reData[k] = reDragonfly - reMoth; + imData[k] = imDragonfly - imMoth; + reData[startIndex] += reButterfly; + imData[startIndex] += imButterfly; +} /* end run */ + +} /* end class DFTLength5Double */ + +/*==================================================================== +| DFTLength5Float +\===================================================================*/ +static class DFTLength5Float + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTLength5Float */ + +/*.................................................................... + DFTLength5Float static variables +....................................................................*/ +private static final float IM5 = (float)sqrt((5.0 - sqrt(5.0)) / 32.0); +private static final float RE5 = (float)sqrt((5.0 + sqrt(5.0)) / 32.0); +private static final float S5 = (float)sqrt(5.0 / 16.0); + +/*.................................................................... + DFTLength5Float constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength5Float ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength5Float */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength5Float"); + int k = startIndex + stride; + float re1 = reData[k]; + float im1 = imData[k]; + k += stride; + float re2 = reData[k]; + float im2 = imData[k]; + k += stride; + float re3 = reData[k]; + float im3 = imData[k]; + k += stride; + float re4 = reData[k]; + float im4 = imData[k]; + float reDragonfly = re1 + re4; + float imDragonfly = im1 + im4; + float reLadybug = re1 - re4; + float imLadybug = im1 - im4; + float reMoth = re2 + re3; + float imMoth = im2 + im3; + float reBeetle = re2 - re3; + float imBeetle = im2 - im3; + final float reButterfly = reDragonfly + reMoth; + final float imButterfly = imDragonfly + imMoth; + re1 = imLadybug + reBeetle; + im1 = reLadybug - imBeetle; + re2 = IM5 * im1 - RE5 * re1; + im2 = IM5 * re1 + RE5 * im1; + re1 = -0.25F * reButterfly; + im1 = -0.25F * imButterfly; + re3 = reBeetle - imLadybug; + im3 = imBeetle + reLadybug; + re4 = RE5 * re3 - IM5 * im3; + im4 = RE5 * im3 + IM5 * re3; + re3 = S5 * (reDragonfly - reMoth); + im3 = S5 * (imDragonfly - imMoth); + reDragonfly = reData[startIndex] + re1; + imDragonfly = imData[startIndex] + im1; + reLadybug = reDragonfly - re3; + imLadybug = imDragonfly - im3; + reDragonfly += re3; + imDragonfly += im3; + reMoth = re2 + re4; + imMoth = im2 + im4; + reBeetle = re2 - re4; + imBeetle = im2 - im4; + reData[k] = reDragonfly + reMoth; + imData[k] = imDragonfly + imMoth; + k -= stride; + reData[k] = reLadybug - imBeetle; + imData[k] = imLadybug + reBeetle; + k -= stride; + reData[k] = reLadybug + imBeetle; + imData[k] = imLadybug - reBeetle; + k -= stride; + reData[k] = reDragonfly - reMoth; + imData[k] = imDragonfly - imMoth; + reData[startIndex] += reButterfly; + imData[startIndex] += imButterfly; +} /* end run */ + +} /* end class DFTLength5Float */ + +/*==================================================================== +| DFTLength5Real +\===================================================================*/ +static class DFTLength5Real + +{ /* begin class DFTLength5Real */ + +/*.................................................................... + DFTLength5Real static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( +) { + return(FFTSetup.FLALLOC * 4L + + FFTSetup.FLOP * 18L + + FFTSetup.FLASSIGN * 13L + + FFTSetup.INTALLOC * 4L + + FFTSetup.INTOP * 4L + + FFTSetup.INTASSIGN * 4L + + FFTSetup.IDX * 15L + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTLength5Real */ + +/*==================================================================== +| DFTLength5RealDouble +\===================================================================*/ +static class DFTLength5RealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTLength5RealDouble */ + +/*.................................................................... + DFTLength5RealDouble static variables +....................................................................*/ +private static final double IM5 = -sqrt((5.0 - sqrt(5.0)) / 8.0); +private static final double RE5 = -sqrt((5.0 + sqrt(5.0)) / 8.0); +private static final double S5 = sqrt(5.0 / 16.0); + +/*.................................................................... + DFTLength5RealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength5RealDouble ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength5RealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength5RealDouble"); + final int i1 = startIndex + stride; + final int i2 = i1 + stride; + final int i3 = i2 + stride; + final int i4 = i3 + stride; + double butterfly = reData[i1] - reData[i4]; + double dragonfly = reData[i2] - reData[i3]; + imData[startIndex] = 0.0; + imData[i1] = RE5 * butterfly + IM5 * dragonfly; + imData[i2] = IM5 * butterfly - RE5 * dragonfly; + butterfly = reData[i1] + reData[i4]; + dragonfly = reData[i2] + reData[i3]; + final double ladybug = S5 * (butterfly - dragonfly); + final double moth = butterfly + dragonfly; + butterfly = reData[startIndex] - 0.25 * moth; + reData[startIndex] += moth; + reData[i1] = butterfly + ladybug; + reData[i2] = butterfly - ladybug; +} /* end run */ + +} /* end class DFTLength5RealDouble */ + +/*==================================================================== +| DFTLength5RealFloat +\===================================================================*/ +static class DFTLength5RealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTLength5RealFloat */ + +/*.................................................................... + DFTLength5RealFloat static variables +....................................................................*/ +private static final float IM5 = -(float)sqrt((5.0 - sqrt(5.0)) / 8.0); +private static final float RE5 = -(float)sqrt((5.0 + sqrt(5.0)) / 8.0); +private static final float S5 = (float)sqrt(5.0 / 16.0); + +/*.................................................................... + DFTLength5RealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength5RealFloat ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength5RealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength5RealFloat"); + final int i1 = startIndex + stride; + final int i2 = i1 + stride; + final int i3 = i2 + stride; + final int i4 = i3 + stride; + float butterfly = reData[i1] - reData[i4]; + float dragonfly = reData[i2] - reData[i3]; + imData[startIndex] = 0.0F; + imData[i1] = RE5 * butterfly + IM5 * dragonfly; + imData[i2] = IM5 * butterfly - RE5 * dragonfly; + butterfly = reData[i1] + reData[i4]; + dragonfly = reData[i2] + reData[i3]; + final float ladybug = S5 * (butterfly - dragonfly); + final float moth = butterfly + dragonfly; + butterfly = reData[startIndex] - 0.25F * moth; + reData[startIndex] += moth; + reData[i1] = butterfly + ladybug; + reData[i2] = butterfly - ladybug; +} /* end run */ + +} /* end class DFTLength5RealFloat */ + +/*==================================================================== +| DFTLength6 +\===================================================================*/ +static class DFTLength6 + +{ /* begin class DFTLength6 */ + +/*.................................................................... + DFTLength6 static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( +) { + return(FFTSetup.FLALLOC * 24L + + FFTSetup.FLOP * 52L + + FFTSetup.FLASSIGN * 44L + + FFTSetup.INTALLOC * 1L + + FFTSetup.INTOP * 12L + + FFTSetup.INTASSIGN * 9L + + FFTSetup.IDX * 24L + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTLength6 */ + +/*==================================================================== +| DFTLength6Double +\===================================================================*/ +static class DFTLength6Double + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTLength6Double */ + +/*.................................................................... + DFTLength6Double static variables +....................................................................*/ +private static final double SQRT3 = sqrt(3.0); + +/*.................................................................... + DFTLength6Double constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength6Double ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength6Double */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength6Double"); + double reLadybug = reData[startIndex]; + int k = startIndex + 3 * stride; + double reMoth = reData[k]; + final double reButterfly = reLadybug + reMoth; + final double reDragonfly = reLadybug - reMoth; + double imLadybug = imData[startIndex]; + double imMoth = imData[k]; + k -= stride << 1; + final double imButterfly = imLadybug + imMoth; + final double imDragonfly = imLadybug - imMoth; + double re1 = reData[k]; + double im1 = imData[k]; + k += stride; + double re2 = reData[k]; + double im2 = imData[k]; + k += stride << 1; + double re4 = reData[k]; + double im4 = imData[k]; + k += stride; + double re5 = reData[k]; + double im5 = imData[k]; + reLadybug = re1 + re2; + imLadybug = im1 + im2; + reMoth = re4 + re5; + imMoth = im4 + im5; + final double reAnt = reLadybug + reMoth; + final double imAnt = imLadybug + imMoth; + final double reBee = SQRT3 * (imMoth - imLadybug); + final double imBee = SQRT3 * (reLadybug - reMoth); + reLadybug = re1 - re2; + imLadybug = im1 - im2; + reMoth = re4 - re5; + imMoth = im4 - im5; + final double reGrasshopper = SQRT3 * (imMoth + imLadybug); + final double imGrasshopper = SQRT3 * (reMoth + reLadybug); + final double reBeetle = reMoth - reLadybug; + final double imBeetle = imMoth - imLadybug; + reData[k] = reDragonfly + 0.5 * (reBee - reBeetle); + imData[k] = imDragonfly + 0.5 * (imBee - imBeetle); + k -= stride; + reData[k] = reButterfly - 0.5 * (reAnt + reGrasshopper); + imData[k] = imButterfly - 0.5 * (imAnt - imGrasshopper); + k -= stride; + reData[k] = reDragonfly + reBeetle; + imData[k] = imDragonfly + imBeetle; + k -= stride; + reData[k] = reButterfly + 0.5 * (reGrasshopper - reAnt); + imData[k] = imButterfly - 0.5 * (imGrasshopper + imAnt); + k -= stride; + reData[k] = reDragonfly - 0.5 * (reBee + reBeetle); + imData[k] = imDragonfly - 0.5 * (imBee + imBeetle); + reData[startIndex] = reButterfly + reAnt; + imData[startIndex] = imButterfly + imAnt; +} /* end run */ + +} /* end class DFTLength6Double */ + +/*==================================================================== +| DFTLength6Float +\===================================================================*/ +static class DFTLength6Float + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTLength6Float */ + +/*.................................................................... + DFTLength6Float static variables +....................................................................*/ +private static final float SQRT3 = (float)sqrt(3.0); + +/*.................................................................... + DFTLength6Float constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength6Float ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength6Float */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength6Float"); + float reLadybug = reData[startIndex]; + int k = startIndex + 3 * stride; + float reMoth = reData[k]; + final float reButterfly = reLadybug + reMoth; + final float reDragonfly = reLadybug - reMoth; + float imLadybug = imData[startIndex]; + float imMoth = imData[k]; + k -= stride << 1; + final float imButterfly = imLadybug + imMoth; + final float imDragonfly = imLadybug - imMoth; + float re1 = reData[k]; + float im1 = imData[k]; + k += stride; + float re2 = reData[k]; + float im2 = imData[k]; + k += stride << 1; + float re4 = reData[k]; + float im4 = imData[k]; + k += stride; + float re5 = reData[k]; + float im5 = imData[k]; + reLadybug = re1 + re2; + imLadybug = im1 + im2; + reMoth = re4 + re5; + imMoth = im4 + im5; + final float reAnt = reLadybug + reMoth; + final float imAnt = imLadybug + imMoth; + final float reBee = SQRT3 * (imMoth - imLadybug); + final float imBee = SQRT3 * (reLadybug - reMoth); + reLadybug = re1 - re2; + imLadybug = im1 - im2; + reMoth = re4 - re5; + imMoth = im4 - im5; + final float reGrasshopper = SQRT3 * (imMoth + imLadybug); + final float imGrasshopper = SQRT3 * (reMoth + reLadybug); + final float reBeetle = reMoth - reLadybug; + final float imBeetle = imMoth - imLadybug; + reData[k] = reDragonfly + 0.5F * (reBee - reBeetle); + imData[k] = imDragonfly + 0.5F * (imBee - imBeetle); + k -= stride; + reData[k] = reButterfly - 0.5F * (reAnt + reGrasshopper); + imData[k] = imButterfly - 0.5F * (imAnt - imGrasshopper); + k -= stride; + reData[k] = reDragonfly + reBeetle; + imData[k] = imDragonfly + imBeetle; + k -= stride; + reData[k] = reButterfly + 0.5F * (reGrasshopper - reAnt); + imData[k] = imButterfly - 0.5F * (imGrasshopper + imAnt); + k -= stride; + reData[k] = reDragonfly - 0.5F * (reBee + reBeetle); + imData[k] = imDragonfly - 0.5F * (imBee + imBeetle); + reData[startIndex] = reButterfly + reAnt; + imData[startIndex] = imButterfly + imAnt; +} /* end run */ + +} /* end class DFTLength6Float */ + +/*==================================================================== +| DFTLength6Real +\===================================================================*/ +static class DFTLength6Real + +{ /* begin class DFTLength6Real */ + +/*.................................................................... + DFTLength6Real static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( +) { + return(FFTSetup.FLALLOC * 6L + + FFTSetup.FLOP * 18L + + FFTSetup.FLASSIGN * 16L + + FFTSetup.INTALLOC * 5L + + FFTSetup.INTOP * 5L + + FFTSetup.INTASSIGN * 5L + + FFTSetup.IDX * 20L + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTLength6Real */ + +/*==================================================================== +| DFTLength6RealDouble +\===================================================================*/ +static class DFTLength6RealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTLength6RealDouble */ + +/*.................................................................... + DFTLength6RealDouble static variables +....................................................................*/ +private static final double NEGHALFSQRT3 = -0.5 * sqrt(3.0); + +/*.................................................................... + DFTLength6RealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength6RealDouble ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength6RealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength6RealDouble"); + final int i1 = startIndex + stride; + final int i2 = i1 + stride; + final int i3 = i2 + stride; + final int i4 = i3 + stride; + final int i5 = i4 + stride; + double butterfly = reData[i1] + reData[i2]; + double dragonfly = reData[i1] - reData[i2]; + final double ladybug = reData[i4] + reData[i5]; + final double grasshopper = reData[i4] - reData[i5]; + imData[startIndex] = 0.0; + imData[i1] = NEGHALFSQRT3 * (butterfly - ladybug); + imData[i2] = NEGHALFSQRT3 * (dragonfly + grasshopper); + imData[i3] = 0.0; + final double beetle = butterfly + ladybug; + final double ant = dragonfly - grasshopper; + butterfly = reData[startIndex] + reData[i3]; + dragonfly = reData[startIndex] - reData[i3]; + reData[startIndex] = butterfly + beetle; + reData[i1] = dragonfly + 0.5 * ant; + reData[i2] = butterfly - 0.5 * beetle; + reData[i3] = dragonfly - ant; +} /* end run */ + +} /* end class DFTLength6RealDouble */ + +/*==================================================================== +| DFTLength6RealFloat +\===================================================================*/ +static class DFTLength6RealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTLength6RealFloat */ + +/*.................................................................... + DFTLength6RealFloat static variables +....................................................................*/ +private static final float NEGHALFSQRT3 = -0.5F * (float)sqrt(3.0); + +/*.................................................................... + DFTLength6RealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength6RealFloat ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength6RealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength6RealFloat"); + final int i1 = startIndex + stride; + final int i2 = i1 + stride; + final int i3 = i2 + stride; + final int i4 = i3 + stride; + final int i5 = i4 + stride; + float butterfly = reData[i1] + reData[i2]; + float dragonfly = reData[i1] - reData[i2]; + final float ladybug = reData[i4] + reData[i5]; + final float grasshopper = reData[i4] - reData[i5]; + imData[startIndex] = 0.0F; + imData[i1] = NEGHALFSQRT3 * (butterfly - ladybug); + imData[i2] = NEGHALFSQRT3 * (dragonfly + grasshopper); + imData[i3] = 0.0F; + final float beetle = butterfly + ladybug; + final float ant = dragonfly - grasshopper; + butterfly = reData[startIndex] + reData[i3]; + dragonfly = reData[startIndex] - reData[i3]; + reData[startIndex] = butterfly + beetle; + reData[i1] = dragonfly + 0.5F * ant; + reData[i2] = butterfly - 0.5F * beetle; + reData[i3] = dragonfly - ant; +} /* end run */ + +} /* end class DFTLength6RealFloat */ + +/*==================================================================== +| DFTLength8 +\===================================================================*/ +static class DFTLength8 + +{ /* begin class DFTLength8 */ + +/*.................................................................... + DFTLength8 static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( +) { + return(FFTSetup.FLALLOC * 24L + + FFTSetup.FLOP * 56L + + FFTSetup.FLASSIGN * 68L + + FFTSetup.INTALLOC * 1L + + FFTSetup.INTOP * 15L + + FFTSetup.INTASSIGN * 14L + + FFTSetup.IDX * 32L + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTLength8 */ + +/*==================================================================== +| DFTLength8Double +\===================================================================*/ +static class DFTLength8Double + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTLength8Double */ + +/*.................................................................... + DFTLength8Double static variables +....................................................................*/ +private static final double SQRTHALF = 1.0 / sqrt(2.0); + +/*.................................................................... + DFTLength8Double constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength8Double ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength8Double */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength8Double"); + int k = startIndex; + double reData0 = reData[k]; + double imData0 = imData[k]; + k += stride; + double reData1 = reData[k]; + double imData1 = imData[k]; + k += stride; + double reData2 = reData[k]; + double imData2 = imData[k]; + k += stride; + double reData3 = reData[k]; + double imData3 = imData[k]; + k += stride; + double reData4 = reData[k]; + double imData4 = imData[k]; + k += stride; + double reData5 = reData[k]; + double imData5 = imData[k]; + k += stride; + double reData6 = reData[k]; + double imData6 = imData[k]; + k += stride; + double reData7 = reData[k]; + double imData7 = imData[k]; + double reButterfly = reData0 + reData4; + double imButterfly = imData0 + imData4; + double reDragonfly = reData2 + reData6; + double imDragonfly = imData2 + imData6; + final double re0 = reButterfly + reDragonfly; + final double im0 = imButterfly + imDragonfly; + final double re1 = reButterfly - reDragonfly; + final double im1 = imButterfly - imDragonfly; + reButterfly = reData0 - reData4; + imButterfly = imData0 - imData4; + reDragonfly = imData6 - imData2; + imDragonfly = reData2 - reData6; + reData0 = reButterfly + reDragonfly; + imData0 = imButterfly + imDragonfly; + reData2 = reButterfly - reDragonfly; + imData2 = imButterfly - imDragonfly; + reButterfly = reData1 + reData5; + imButterfly = imData1 + imData5; + reDragonfly = reData3 + reData7; + imDragonfly = imData3 + imData7; + reData4 = reButterfly + reDragonfly; + imData4 = imButterfly + imDragonfly; + reData6 = imDragonfly - imButterfly; + imData6 = reButterfly - reDragonfly; + reData5 -= reData1; + imData5 -= imData1; + reButterfly = SQRTHALF * (reData5 + imData5); + imButterfly = SQRTHALF * (reData5 - imData5); + reData7 -= reData3; + imData7 -= imData3; + reDragonfly = SQRTHALF * (imData7 - reData7); + imDragonfly = SQRTHALF * (imData7 + reData7); + reData1 = imButterfly - imDragonfly; + imData1 = reDragonfly - reButterfly; + reData3 = reDragonfly + reButterfly; + imData3 = imButterfly + imDragonfly; + reData[k] = reData0 - reData1; + imData[k] = imData0 + imData1; + k -= stride; + reData[k] = re1 + reData6; + imData[k] = im1 + imData6; + k -= stride; + reData[k] = reData2 + reData3; + imData[k] = imData2 - imData3; + k -= stride; + reData[k] = re0 - reData4; + imData[k] = im0 - imData4; + k -= stride; + reData[k] = reData0 + reData1; + imData[k] = imData0 - imData1; + k -= stride; + reData[k] = re1 - reData6; + imData[k] = im1 - imData6; + k -= stride; + reData[k] = reData2 - reData3; + imData[k] = imData2 + imData3; + k -= stride; + reData[k] = re0 + reData4; + imData[k] = im0 + imData4; +} /* end run */ + +} /* end class DFTLength8Double */ + +/*==================================================================== +| DFTLength8Float +\===================================================================*/ +static class DFTLength8Float + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTLength8Float */ + +/*.................................................................... + DFTLength8Float static variables +....................................................................*/ +private static final float SQRTHALF = 1.0F / (float)sqrt(2.0); + +/*.................................................................... + DFTLength8Float constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength8Float ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength8Float */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength8Float"); + int k = startIndex; + float reData0 = reData[k]; + float imData0 = imData[k]; + k += stride; + float reData1 = reData[k]; + float imData1 = imData[k]; + k += stride; + float reData2 = reData[k]; + float imData2 = imData[k]; + k += stride; + float reData3 = reData[k]; + float imData3 = imData[k]; + k += stride; + float reData4 = reData[k]; + float imData4 = imData[k]; + k += stride; + float reData5 = reData[k]; + float imData5 = imData[k]; + k += stride; + float reData6 = reData[k]; + float imData6 = imData[k]; + k += stride; + float reData7 = reData[k]; + float imData7 = imData[k]; + float reButterfly = reData0 + reData4; + float imButterfly = imData0 + imData4; + float reDragonfly = reData2 + reData6; + float imDragonfly = imData2 + imData6; + final float re0 = reButterfly + reDragonfly; + final float im0 = imButterfly + imDragonfly; + final float re1 = reButterfly - reDragonfly; + final float im1 = imButterfly - imDragonfly; + reButterfly = reData0 - reData4; + imButterfly = imData0 - imData4; + reDragonfly = imData6 - imData2; + imDragonfly = reData2 - reData6; + reData0 = reButterfly + reDragonfly; + imData0 = imButterfly + imDragonfly; + reData2 = reButterfly - reDragonfly; + imData2 = imButterfly - imDragonfly; + reButterfly = reData1 + reData5; + imButterfly = imData1 + imData5; + reDragonfly = reData3 + reData7; + imDragonfly = imData3 + imData7; + reData4 = reButterfly + reDragonfly; + imData4 = imButterfly + imDragonfly; + reData6 = imDragonfly - imButterfly; + imData6 = reButterfly - reDragonfly; + reData5 -= reData1; + imData5 -= imData1; + reButterfly = SQRTHALF * (reData5 + imData5); + imButterfly = SQRTHALF * (reData5 - imData5); + reData7 -= reData3; + imData7 -= imData3; + reDragonfly = SQRTHALF * (imData7 - reData7); + imDragonfly = SQRTHALF * (imData7 + reData7); + reData1 = imButterfly - imDragonfly; + imData1 = reDragonfly - reButterfly; + reData3 = reDragonfly + reButterfly; + imData3 = imButterfly + imDragonfly; + reData[k] = reData0 - reData1; + imData[k] = imData0 + imData1; + k -= stride; + reData[k] = re1 + reData6; + imData[k] = im1 + imData6; + k -= stride; + reData[k] = reData2 + reData3; + imData[k] = imData2 - imData3; + k -= stride; + reData[k] = re0 - reData4; + imData[k] = im0 - imData4; + k -= stride; + reData[k] = reData0 + reData1; + imData[k] = imData0 - imData1; + k -= stride; + reData[k] = re1 - reData6; + imData[k] = im1 - imData6; + k -= stride; + reData[k] = reData2 - reData3; + imData[k] = imData2 + imData3; + k -= stride; + reData[k] = re0 + reData4; + imData[k] = im0 + imData4; +} /* end run */ + +} /* end class DFTLength8Float */ + +/*==================================================================== +| DFTLength8Real +\===================================================================*/ +static class DFTLength8Real + +{ /* begin class DFTLength8Real */ + +/*.................................................................... + DFTLength8Real static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( +) { + return(FFTSetup.FLALLOC * 8L + + FFTSetup.FLOP * 22L + + FFTSetup.FLASSIGN * 26L + + FFTSetup.INTALLOC * 7L + + FFTSetup.INTOP * 7L + + FFTSetup.INTASSIGN * 7L + + FFTSetup.IDX * 22L + + FFTSetup.NEWOBJ * 0L + ); +} /* end cost */ + +} /* end class DFTLength8Real */ + +/*==================================================================== +| DFTLength8RealDouble +\===================================================================*/ +static class DFTLength8RealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTLength8RealDouble */ + +/*.................................................................... + DFTLength8RealDouble static variables +....................................................................*/ +private static final double NEGSQRTHALF = -1.0 / sqrt(2.0); + +/*.................................................................... + DFTLength8RealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength8RealDouble ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength8RealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength8RealDouble"); + final int i1 = startIndex + stride; + final int i2 = i1 + stride; + final int i3 = i2 + stride; + final int i4 = i3 + stride; + final int i5 = i4 + stride; + final int i6 = i5 + stride; + final int i7 = i6 + stride; + double re0 = reData[startIndex]; + double re2 = reData[i2]; + final double re4 = reData[i4]; + final double re6 = reData[i6]; + double butterfly = re0 + re4; + double dragonfly = re2 + re6; + double ladybug = butterfly + dragonfly; + reData[i2] = butterfly - dragonfly; + butterfly = reData[i1] + reData[i5]; + dragonfly = reData[i3] + reData[i7]; + double moth = dragonfly + butterfly; + imData[i2] = dragonfly - butterfly; + reData[startIndex] = ladybug + moth; + imData[startIndex] = 0.0; + reData[i4] = ladybug - moth; + imData[i4] = 0.0; + butterfly = re0 - re4; + dragonfly = re2 - re6; + re0 = reData[i1] - reData[i5]; + re2 = reData[i3] - reData[i7]; + ladybug = NEGSQRTHALF * (re0 - re2); + moth = NEGSQRTHALF * (re0 + re2); + reData[i1] = butterfly - ladybug; + imData[i1] = moth - dragonfly; + reData[i3] = butterfly + ladybug; + imData[i3] = moth + dragonfly; +} /* end run */ + +} /* end class DFTLength8RealDouble */ + +/*==================================================================== +| DFTLength8RealFloat +\===================================================================*/ +static class DFTLength8RealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTLength8RealFloat */ + +/*.................................................................... + DFTLength8RealFloat static variables +....................................................................*/ +private static final float NEGSQRTHALF = -1.0F / (float)sqrt(2.0); + +/*.................................................................... + DFTLength8RealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTLength8RealFloat ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride +) { + super(reData, imData, startIndex, stride); +} /* end DFTLength8RealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { +//;IJ.log("DFTLength8RealFloat"); + final int i1 = startIndex + stride; + final int i2 = i1 + stride; + final int i3 = i2 + stride; + final int i4 = i3 + stride; + final int i5 = i4 + stride; + final int i6 = i5 + stride; + final int i7 = i6 + stride; + float re0 = reData[startIndex]; + float re2 = reData[i2]; + final float re4 = reData[i4]; + final float re6 = reData[i6]; + float butterfly = re0 + re4; + float dragonfly = re2 + re6; + float ladybug = butterfly + dragonfly; + reData[i2] = butterfly - dragonfly; + butterfly = reData[i1] + reData[i5]; + dragonfly = reData[i3] + reData[i7]; + float moth = dragonfly + butterfly; + imData[i2] = dragonfly - butterfly; + reData[startIndex] = ladybug + moth; + imData[startIndex] = 0.0F; + reData[i4] = ladybug - moth; + imData[i4] = 0.0F; + butterfly = re0 - re4; + dragonfly = re2 - re6; + re0 = reData[i1] - reData[i5]; + re2 = reData[i3] - reData[i7]; + ladybug = NEGSQRTHALF * (re0 - re2); + moth = NEGSQRTHALF * (re0 + re2); + reData[i1] = butterfly - ladybug; + imData[i1] = moth - dragonfly; + reData[i3] = butterfly + ladybug; + imData[i3] = moth + dragonfly; +} /* end run */ + +} /* end class DFTLength8RealFloat */ + +/*==================================================================== +| DFTMixedRadix +\===================================================================*/ +static class DFTMixedRadix + +{ /* begin class DFTMixedRadix */ + +/*.................................................................... + DFTMixedRadix static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int K1, + final int K2 +) { + if (FFTSetup.taboos.contains(new Integer(K1)) + || FFTSetup.taboos.contains(new Integer(K2))) { + return(-1L); + } + final long K = (long)K1 * (long)K2; + return(FFTSetup.FLALLOC * 4L + + FFTSetup.FLOP * ((long)K2 * ((long)K1 * 6L)) + + FFTSetup.FLASSIGN * ((long)K2 * ((long)K1 * 6L) + + (long)K2 * ((long)K1 * 2L) + K * 2L) + + FFTSetup.INTALLOC * 16L + + FFTSetup.INTOP * (3L + (long)K2 * 3L + (long)K2 * (3L + + (long)K1 * 7L) + 1L + (long)K1 * 3L + (long)K2 * (3L + + (long)K1 * 4L) + K * 3L) + + FFTSetup.INTASSIGN * (6L + (long)K2 * 2L + 2L + (long)K2 * (3L + + (long)K1 * 4L) + 2L + (long)K1 * 2L + 3L + (long)K2 * (4L + + (long)K1 * 3L) + 2L + K * 2L) + + FFTSetup.IDX * ((long)K2 * ((long)K1 * 6L) + + (long)K2 * ((long)K1 * 4L) + K * 4L) + + FFTSetup.NEWOBJ * ((long)K2 * 1L + (long)K1 * 1L) + + (long)K2 * FFTSetup.cost(K1) + (long)K1 * FFTSetup.cost(K2) + ); +} /* end cost */ + +} /* end class DFTMixedRadix */ + +/*==================================================================== +| DFTMixedRadixDouble +\===================================================================*/ +static class DFTMixedRadixDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTMixedRadixDouble */ + +/*.................................................................... + DFTMixedRadixDouble private variables +....................................................................*/ +private double[] imBuffer; +private double[] imUnitRoot; +private double[] reBuffer; +private double[] reUnitRoot; +private int K1; + +/*.................................................................... + DFTMixedRadixDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTMixedRadixDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final double[] reUnitRoot, + final double[] imUnitRoot, + final int K1 +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; + this.K1 = K1; +} /* end DFTMixedRadixDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int transformLength = reUnitRoot.length; +//;IJ.log("DFTMixedRadixDouble " + transformLength + " / " + K1); + final int K2 = transformLength / K1; + final int d1 = stride; + final int d2 = K2 * d1; + int k2 = startIndex; + final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1)); + switch (fft1.algorithm) { + case BRUTEFORCE: { + for (int n = 0; (n < K2); n++) { + new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer, + k2, d2, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run(); + k2 += d1; + } + break; + } + case COPRIMEFACTOR: { + for (int n = 0; (n < K2); n++) { + new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer, + k2, d2, fft1.ruritanian, fft1.chinese, fft1.K1).run(); + k2 += d1; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + for (int n = 0; (n < K2); n++) { + new DFTLength2Double(reData, imData, k2, d2).run(); + k2 += d1; + } + break; + } + case LENGTH3: { + for (int n = 0; (n < K2); n++) { + new DFTLength3Double(reData, imData, k2, d2).run(); + k2 += d1; + } + break; + } + case LENGTH4: { + for (int n = 0; (n < K2); n++) { + new DFTLength4Double(reData, imData, k2, d2).run(); + k2 += d1; + } + break; + } + case LENGTH5: { + for (int n = 0; (n < K2); n++) { + new DFTLength5Double(reData, imData, k2, d2).run(); + k2 += d1; + } + break; + } + case LENGTH6: { + for (int n = 0; (n < K2); n++) { + new DFTLength6Double(reData, imData, k2, d2).run(); + k2 += d1; + } + break; + } + case LENGTH8: { + for (int n = 0; (n < K2); n++) { + new DFTLength8Double(reData, imData, k2, d2).run(); + k2 += d1; + } + break; + } + case MIXEDRADIX: { + for (int n = 0; (n < K2); n++) { + new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer, + k2, d2, fft1.reUnitRootDouble, fft1.imUnitRootDouble, + fft1.K1).run(); + k2 += d1; + } + break; + } + case PADDEDRADER: { + for (int n = 0; (n < K2); n++) { + new DFTPaddedRaderDouble(reData, imData, + k2, d2, fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular).run(); + k2 += d1; + } + break; + } + case RADER: { + for (int n = 0; (n < K2); n++) { + new DFTRaderDouble(reData, imData, reBuffer, imBuffer, + k2, d2, fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular).run(); + k2 += d1; + } + break; + } + case RADIX2: { + for (int n = 0; (n < K2); n++) { + new DFTRadix2Double(reData, imData, reBuffer, imBuffer, + k2, d2, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run(); + k2 += d1; + } + break; + } + case SPLITRADIX: { + for (int n = 0; (n < K2); n++) { + new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer, + k2, d2, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run(); + k2 += d1; + } + break; + } + } + k2 = startIndex; + for (int n = 0; (n < K2); n++) { + int m2 = 0; + int m1 = k2; + k2 += d1; + for (int m = 0; (m < K1); m++) { + final double re = reData[m1]; + final double im = imData[m1]; + final double reRoot = reUnitRoot[m2]; + final double imRoot = imUnitRoot[m2]; + reData[m1] = re * reRoot - im * imRoot; + imData[m1] = re * imRoot + im * reRoot; + m1 += d2; + m2 += n; + m2 -= transformLength * (m2 / transformLength); + } + } + int k1 = startIndex; + final FFTSetup fft2 = FFTSetup.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + for (int n = 0; (n < K1); n++) { + new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer, + k1, d1, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + k1 += d2; + } + break; + } + case COPRIMEFACTOR: { + for (int n = 0; (n < K1); n++) { + new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer, + k1, d1, fft2.ruritanian, fft2.chinese, fft2.K1).run(); + k1 += d2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + for (int n = 0; (n < K1); n++) { + new DFTLength2Double(reData, imData, k1, d1).run(); + k1 += d2; + } + break; + } + case LENGTH3: { + for (int n = 0; (n < K1); n++) { + new DFTLength3Double(reData, imData, k1, d1).run(); + k1 += d2; + } + break; + } + case LENGTH4: { + for (int n = 0; (n < K1); n++) { + new DFTLength4Double(reData, imData, k1, d1).run(); + k1 += d2; + } + break; + } + case LENGTH5: { + for (int n = 0; (n < K1); n++) { + new DFTLength5Double(reData, imData, k1, d1).run(); + k1 += d2; + } + break; + } + case LENGTH6: { + for (int n = 0; (n < K1); n++) { + new DFTLength6Double(reData, imData, k1, d1).run(); + k1 += d2; + } + break; + } + case LENGTH8: { + for (int n = 0; (n < K1); n++) { + new DFTLength8Double(reData, imData, k1, d1).run(); + k1 += d2; + } + break; + } + case MIXEDRADIX: { + for (int n = 0; (n < K1); n++) { + new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer, + k1, d1, fft2.reUnitRootDouble, fft2.imUnitRootDouble, + fft2.K1).run(); + k1 += d2; + } + break; + } + case PADDEDRADER: { + for (int n = 0; (n < K1); n++) { + new DFTPaddedRaderDouble(reData, imData, + k1, d1, fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + k1 += d2; + } + break; + } + case RADER: { + for (int n = 0; (n < K1); n++) { + new DFTRaderDouble(reData, imData, reBuffer, imBuffer, + k1, d1, fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + k1 += d2; + } + break; + } + case RADIX2: { + for (int n = 0; (n < K1); n++) { + new DFTRadix2Double(reData, imData, reBuffer, imBuffer, + k1, d1, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + k1 += d2; + } + break; + } + case SPLITRADIX: { + for (int n = 0; (n < K1); n++) { + new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer, + k1, d1, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + k1 += d2; + } + break; + } + } + k1 = startIndex; + k2 = startIndex; + for (int m2 = 0; (m2 < K2); m2++) { + int n2 = k1; + for (int m1 = 0; (m1 < K1); m1++) { + reBuffer[k2] = reData[n2]; + imBuffer[k2] = imData[n2]; + k2 += d1; + n2 += d2; + } + k1 += d1; + } + k1 = startIndex; + for (int m = 0; (m < transformLength); m++) { + reData[k1] = reBuffer[k1]; + imData[k1] = imBuffer[k1]; + k1 += d1; + } +} /* end run */ + +/*.................................................................... + DFTMixedRadixDouble static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static double[] getImUnitRoot ( + final int transformLength +) { + final double[] imUnitRoot = new double[transformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < transformLength); k++) { + imUnitRoot[k] = sin((double)k * angularStep); + } + return(imUnitRoot); +} /* end getImUnitRoot */ + +/*------------------------------------------------------------------*/ +static double[] getReUnitRoot ( + final int transformLength +) { + final double[] reUnitRoot = new double[transformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < transformLength); k++) { + reUnitRoot[k] = cos((double)k * angularStep); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +} /* end class DFTMixedRadixDouble */ + +/*==================================================================== +| DFTMixedRadixFloat +\===================================================================*/ +static class DFTMixedRadixFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTMixedRadixFloat */ + +/*.................................................................... + DFTMixedRadixFloat private variables +....................................................................*/ +private float[] imBuffer; +private float[] imUnitRoot; +private float[] reBuffer; +private float[] reUnitRoot; +private int K1; + +/*.................................................................... + DFTMixedRadixFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTMixedRadixFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final float[] reUnitRoot, + final float[] imUnitRoot, + final int K1 +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; + this.K1 = K1; +} /* end DFTMixedRadixFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int transformLength = reUnitRoot.length; +//;IJ.log("DFTMixedRadixFloat " + transformLength + " / " + K1); + final int K2 = transformLength / K1; + final int d1 = stride; + final int d2 = K2 * d1; + int k2 = startIndex; + final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1)); + switch (fft1.algorithm) { + case BRUTEFORCE: { + for (int n = 0; (n < K2); n++) { + new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer, + k2, d2, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run(); + k2 += d1; + } + break; + } + case COPRIMEFACTOR: { + for (int n = 0; (n < K2); n++) { + new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer, + k2, d2, fft1.ruritanian, fft1.chinese, fft1.K1).run(); + k2 += d1; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + for (int n = 0; (n < K2); n++) { + new DFTLength2Float(reData, imData, k2, d2).run(); + k2 += d1; + } + break; + } + case LENGTH3: { + for (int n = 0; (n < K2); n++) { + new DFTLength3Float(reData, imData, k2, d2).run(); + k2 += d1; + } + break; + } + case LENGTH4: { + for (int n = 0; (n < K2); n++) { + new DFTLength4Float(reData, imData, k2, d2).run(); + k2 += d1; + } + break; + } + case LENGTH5: { + for (int n = 0; (n < K2); n++) { + new DFTLength5Float(reData, imData, k2, d2).run(); + k2 += d1; + } + break; + } + case LENGTH6: { + for (int n = 0; (n < K2); n++) { + new DFTLength6Float(reData, imData, k2, d2).run(); + k2 += d1; + } + break; + } + case LENGTH8: { + for (int n = 0; (n < K2); n++) { + new DFTLength8Float(reData, imData, k2, d2).run(); + k2 += d1; + } + break; + } + case MIXEDRADIX: { + for (int n = 0; (n < K2); n++) { + new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer, + k2, d2, fft1.reUnitRootFloat, fft1.imUnitRootFloat, + fft1.K1).run(); + k2 += d1; + } + break; + } + case PADDEDRADER: { + for (int n = 0; (n < K2); n++) { + new DFTPaddedRaderFloat(reData, imData, + k2, d2, fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular).run(); + k2 += d1; + } + break; + } + case RADER: { + for (int n = 0; (n < K2); n++) { + new DFTRaderFloat(reData, imData, reBuffer, imBuffer, + k2, d2, fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular).run(); + k2 += d1; + } + break; + } + case RADIX2: { + for (int n = 0; (n < K2); n++) { + new DFTRadix2Float(reData, imData, reBuffer, imBuffer, + k2, d2, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run(); + k2 += d1; + } + break; + } + case SPLITRADIX: { + for (int n = 0; (n < K2); n++) { + new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer, + k2, d2, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run(); + k2 += d1; + } + break; + } + } + k2 = startIndex; + for (int n = 0; (n < K2); n++) { + int m2 = 0; + int m1 = k2; + k2 += d1; + for (int m = 0; (m < K1); m++) { + final float re = reData[m1]; + final float im = imData[m1]; + final float reRoot = reUnitRoot[m2]; + final float imRoot = imUnitRoot[m2]; + reData[m1] = re * reRoot - im * imRoot; + imData[m1] = re * imRoot + im * reRoot; + m1 += d2; + m2 += n; + m2 -= transformLength * (m2 / transformLength); + } + } + int k1 = startIndex; + final FFTSetup fft2 = FFTSetup.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + for (int n = 0; (n < K1); n++) { + new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer, + k1, d1, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + k1 += d2; + } + break; + } + case COPRIMEFACTOR: { + for (int n = 0; (n < K1); n++) { + new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer, + k1, d1, fft2.ruritanian, fft2.chinese, fft2.K1).run(); + k1 += d2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + for (int n = 0; (n < K1); n++) { + new DFTLength2Float(reData, imData, k1, d1).run(); + k1 += d2; + } + break; + } + case LENGTH3: { + for (int n = 0; (n < K1); n++) { + new DFTLength3Float(reData, imData, k1, d1).run(); + k1 += d2; + } + break; + } + case LENGTH4: { + for (int n = 0; (n < K1); n++) { + new DFTLength4Float(reData, imData, k1, d1).run(); + k1 += d2; + } + break; + } + case LENGTH5: { + for (int n = 0; (n < K1); n++) { + new DFTLength5Float(reData, imData, k1, d1).run(); + k1 += d2; + } + break; + } + case LENGTH6: { + for (int n = 0; (n < K1); n++) { + new DFTLength6Float(reData, imData, k1, d1).run(); + k1 += d2; + } + break; + } + case LENGTH8: { + for (int n = 0; (n < K1); n++) { + new DFTLength8Float(reData, imData, k1, d1).run(); + k1 += d2; + } + break; + } + case MIXEDRADIX: { + for (int n = 0; (n < K1); n++) { + new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer, + k1, d1, fft2.reUnitRootFloat, fft2.imUnitRootFloat, + fft2.K1).run(); + k1 += d2; + } + break; + } + case PADDEDRADER: { + for (int n = 0; (n < K1); n++) { + new DFTPaddedRaderFloat(reData, imData, + k1, d1, fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + k1 += d2; + } + break; + } + case RADER: { + for (int n = 0; (n < K1); n++) { + new DFTRaderFloat(reData, imData, reBuffer, imBuffer, + k1, d1, fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + k1 += d2; + } + break; + } + case RADIX2: { + for (int n = 0; (n < K1); n++) { + new DFTRadix2Float(reData, imData, reBuffer, imBuffer, + k1, d1, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + k1 += d2; + } + break; + } + case SPLITRADIX: { + for (int n = 0; (n < K1); n++) { + new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer, + k1, d1, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + k1 += d2; + } + break; + } + } + k1 = startIndex; + k2 = startIndex; + for (int m2 = 0; (m2 < K2); m2++) { + int n2 = k1; + for (int m1 = 0; (m1 < K1); m1++) { + reBuffer[k2] = reData[n2]; + imBuffer[k2] = imData[n2]; + k2 += d1; + n2 += d2; + } + k1 += d1; + } + k1 = startIndex; + for (int m = 0; (m < transformLength); m++) { + reData[k1] = reBuffer[k1]; + imData[k1] = imBuffer[k1]; + k1 += d1; + } +} /* end run */ + +/*.................................................................... + DFTMixedRadixFloat static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static float[] getImUnitRoot ( + final int transformLength +) { + final float[] imUnitRoot = new float[transformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < transformLength); k++) { + imUnitRoot[k] = (float)sin((double)((float)k * angularStep)); + } + return(imUnitRoot); +} /* end getImUnitRoot */ + +/*------------------------------------------------------------------*/ +static float[] getReUnitRoot ( + final int transformLength +) { + final float[] reUnitRoot = new float[transformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < transformLength); k++) { + reUnitRoot[k] = (float)cos((double)((float)k * angularStep)); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +} /* end class DFTMixedRadixFloat */ + +/*==================================================================== +| DFTMixedRadixReal +\===================================================================*/ +static class DFTMixedRadixReal + +{ /* begin class DFTMixedRadixReal */ + +/*.................................................................... + DFTMixedRadixReal static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int K1, + final int K2 +) { + if (FFTSetup.taboos.contains(new Integer(K1)) + || FFTSetup.taboos.contains(new Integer(K2))) { + return(-1L); + } + final long K = (long)K1 * (long)K2; + final long k = K >> 1L; + final long k1 = (long)(K1 >> 1); + final long k2 = (long)(K2 >> 1); + return(FFTSetup.FLALLOC * 4L + + FFTSetup.FLOP * ((k2 + 1L) * ((long)K1 * 6L) + k1 * k2 * 1L) + + FFTSetup.FLASSIGN * ((k2 + 1L) * ((long)K1 * 6L) + 2L + K * 1L) + + FFTSetup.INTALLOC * 14L + + FFTSetup.INTOP * (7L + (long)K1 * 3L + (k2 + 1L) * (2L + + (long)K1 * 4L) + 1L + (k2 + 1L) * 3L + 4L + (K * 2L + K1 * 5L) + + 2L) + + FFTSetup.INTASSIGN * (7L + (long)K1 * 2L + 1L + (k2 + 1L) * (3L + + (long)K1 * 3L) + 2L + (k2 + 1L) * 2L + 5L + (k * 3L + K1 * 3L)) + + FFTSetup.IDX * ((k2 + 1L) * ((long)K1 * 6L) + 3L + (K * 2L)) + + FFTSetup.NEWOBJ * ((long)K1 * 1L + (k2 + 1L) * 1L) + + k1 * FFTSetupDuoReal.cost(K2) + (k2 + 1L) * FFTSetup.cost(K1) + ); +} /* end cost */ + +} /* end class DFTMixedRadixReal */ + +/*==================================================================== +| DFTMixedRadixRealDouble +\===================================================================*/ +static class DFTMixedRadixRealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTMixedRadixRealDouble */ + +/*.................................................................... + DFTMixedRadixRealDouble private variables +....................................................................*/ +private double[] imBuffer; +private double[] imUnitRoot; +private double[] reBuffer; +private double[] reUnitRoot; +private int K1; + +/*.................................................................... + DFTMixedRadixRealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTMixedRadixRealDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final double[] reUnitRoot, + final double[] imUnitRoot, + final int K1 +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; + this.K1 = K1; +} /* end DFTMixedRadixDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int transformLength = reUnitRoot.length; +//;IJ.log("DFTMixedRadixRealDouble " + transformLength + " / " + K1); + final int K2 = transformLength / K1; + final int halfK2 = (K2 >> 1) + 1; + final int d1 = stride; + final int d2 = K1 * d1; + int p = startIndex; + int k1 = 0; + if (1 == (K1 & 1)) { + final FFTSetupReal fft2 = FFTSetupReal.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealDouble(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealDouble(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.ruritanian, fft2.chinese, fft2.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealDouble(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + break; + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2RealDouble(reData, imData, p, d2).run(); + break; + } + case LENGTH3: { + new DFTLength3RealDouble(reData, imData, p, d2).run(); + break; + } + case LENGTH4: { + new DFTLength4RealDouble(reData, imData, p, d2).run(); + break; + } + case LENGTH5: { + new DFTLength5RealDouble(reData, imData, p, d2).run(); + break; + } + case LENGTH6: { + new DFTLength6RealDouble(reData, imData, p, d2).run(); + break; + } + case LENGTH8: { + new DFTLength8RealDouble(reData, imData, p, d2).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealDouble(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, + fft2.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealDouble(reData, imData, + p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealDouble(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealDouble(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootEvenDouble, fft2.imUnitRootEvenDouble, + fft2.reUnitRootOddDouble, fft2.imUnitRootOddDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealDouble(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + break; + } + } + p += d1; + k1++; + } + final FFTSetupDuoReal fft2 = + FFTSetupDuoReal.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + while (k1++ < K1) { + new DFTBruteForceRealDouble(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + p += d1; + } + break; + } + case COPRIMEFACTOR: { + while (k1++ < K1) { + new DFTCoprimeFactorRealDouble(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.ruritanian, fft2.chinese, fft2.K1).run(); + p += d1; + } + break; + } + case DUOREAL: { + while (k1++ < K1) { + new DFTDuoRealDouble(reData, imData, + reBuffer, imBuffer, p, p + d1, d2, K2).run(); + p += 2 * d1; + k1++; + } + break; + } + case EVENREAL: { + while (k1++ < K1) { + new DFTEvenRealDouble(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + p += d1; + } + break; + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + while (k1++ < K1) { + new DFTLength2RealDouble(reData, imData, p, d2).run(); + p += d1; + } + break; + } + case LENGTH3: { + while (k1++ < K1) { + new DFTLength3RealDouble(reData, imData, p, d2).run(); + p += d1; + } + break; + } + case LENGTH4: { + while (k1++ < K1) { + new DFTLength4RealDouble(reData, imData, p, d2).run(); + p += d1; + } + break; + } + case LENGTH5: { + while (k1++ < K1) { + new DFTLength5RealDouble(reData, imData, p, d2).run(); + p += d1; + } + break; + } + case LENGTH6: { + while (k1++ < K1) { + new DFTLength6RealDouble(reData, imData, p, d2).run(); + p += d1; + } + break; + } + case LENGTH8: { + while (k1++ < K1) { + new DFTLength8RealDouble(reData, imData, p, d2).run(); + p += d1; + } + break; + } + case MIXEDRADIX: { + while (k1++ < K1) { + new DFTMixedRadixRealDouble(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, + fft2.K1).run(); + p += d1; + } + break; + } + case PADDEDRADER: { + while (k1++ < K1) { + new DFTPaddedRaderRealDouble(reData, imData, + p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + p += d1; + } + break; + } + case RADER: { + while (k1++ < K1) { + new DFTRaderRealDouble(reData, imData, reBuffer, imBuffer, + p, d2, fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + p += d1; + } + break; + } + case RADIX2: { + while (k1++ < K1) { + new DFTRadix2RealDouble(reData, imData, reBuffer, imBuffer, + p, d2, fft2.reUnitRootEvenDouble, fft2.imUnitRootEvenDouble, + fft2.reUnitRootOddDouble, fft2.imUnitRootOddDouble).run(); + p += d1; + } + break; + } + case SPLITRADIX: { + while (k1++ < K1) { + new DFTSplitRadixRealDouble(reData, imData, reBuffer, imBuffer, + p, d2, fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + p += d1; + } + break; + } + } + p = startIndex; + for (int n2 = 0; (n2 < halfK2); n2++) { + int n = 0; + for (k1 = 0; (k1 < K1); k1++) { + final double re = reData[p]; + final double im = imData[p]; + final double reRoot = reUnitRoot[n]; + final double imRoot = imUnitRoot[n]; + reBuffer[p] = re * reRoot - im * imRoot; + imBuffer[p] = re * imRoot + im * reRoot; + p += d1; + n += n2; + } + } + p = startIndex; + final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1)); + switch (fft1.algorithm) { + case BRUTEFORCE: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run(); + p += d2; + } + break; + } + case COPRIMEFACTOR: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.ruritanian, fft1.chinese, fft1.K1).run(); + p += d2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength2Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH3: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength3Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH4: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength4Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH5: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength5Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH6: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength6Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH8: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength8Double(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case MIXEDRADIX: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble, + fft1.K1).run(); + p += d2; + } + break; + } + case PADDEDRADER: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTPaddedRaderDouble(reBuffer, imBuffer, + p, d1, fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular).run(); + p += d2; + } + break; + } + case RADER: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTRaderDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular).run(); + p += d2; + } + break; + } + case RADIX2: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTRadix2Double(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run(); + p += d2; + } + break; + } + case SPLITRADIX: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootDouble, fft1.imUnitRootDouble).run(); + p += d2; + } + break; + } + } + p = startIndex; + reData[p] = reBuffer[p]; + imData[p] = 0.0; + p += d1; + int progressive = startIndex; + int regressive = startIndex + (K1 - 1) * d1; + int k2 = 1; + while (progressive < regressive) { + int q = progressive + d2; + while (k2 < halfK2) { + reData[p] = reBuffer[q]; + imData[p] = imBuffer[q]; + k2++; + p += d1; + q += d2; + } + k2 -= 2 - (K2 & 1); + progressive += d1; + q = regressive + k2 * d2; + while (0 <= k2) { + reData[p] = reBuffer[q]; + imData[p] = -imBuffer[q]; + k2--; + p += d1; + q -= d2; + } + k2 += 2; + regressive -= d1; + } + if (1 == (K1 & 1)) { + int q = progressive + k2 * d2; + while (k2 < halfK2) { + reData[p] = reBuffer[q]; + imData[p] = imBuffer[q]; + k2++; + p += d1; + q += d2; + } + } +} /* end run */ + +} /* end class DFTMixedRadixRealDouble */ + +/*==================================================================== +| DFTMixedRadixRealFloat +\===================================================================*/ +static class DFTMixedRadixRealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTMixedRadixRealFloat */ + +/*.................................................................... + DFTMixedRadixRealFloat private variables +....................................................................*/ +private float[] imBuffer; +private float[] imUnitRoot; +private float[] reBuffer; +private float[] reUnitRoot; +private int K1; + +/*.................................................................... + DFTMixedRadixRealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTMixedRadixRealFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final float[] reUnitRoot, + final float[] imUnitRoot, + final int K1 +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; + this.K1 = K1; +} /* end DFTMixedRadixRealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int transformLength = reUnitRoot.length; +//;IJ.log("DFTMixedRadixRealFloat " + transformLength + " / " + K1); + final int K2 = transformLength / K1; + final int halfK2 = (K2 >> 1) + 1; + final int d1 = stride; + final int d2 = K1 * d1; + int p = startIndex; + int k1 = 0; + if (1 == (K1 & 1)) { + final FFTSetupReal fft2 = FFTSetupReal.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealFloat(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealFloat(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.ruritanian, fft2.chinese, fft2.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealFloat(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + break; + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2RealFloat(reData, imData, p, d2).run(); + break; + } + case LENGTH3: { + new DFTLength3RealFloat(reData, imData, p, d2).run(); + break; + } + case LENGTH4: { + new DFTLength4RealFloat(reData, imData, p, d2).run(); + break; + } + case LENGTH5: { + new DFTLength5RealFloat(reData, imData, p, d2).run(); + break; + } + case LENGTH6: { + new DFTLength6RealFloat(reData, imData, p, d2).run(); + break; + } + case LENGTH8: { + new DFTLength8RealFloat(reData, imData, p, d2).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealFloat(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, + fft2.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealFloat(reData, imData, + p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealFloat(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealFloat(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootEvenFloat, fft2.imUnitRootEvenFloat, + fft2.reUnitRootOddFloat, fft2.imUnitRootOddFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealFloat(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + break; + } + } + p += d1; + k1++; + } + final FFTSetupDuoReal fft2 = + FFTSetupDuoReal.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + while (k1++ < K1) { + new DFTBruteForceRealFloat(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + p += d1; + } + break; + } + case COPRIMEFACTOR: { + while (k1++ < K1) { + new DFTCoprimeFactorRealFloat(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.ruritanian, fft2.chinese, fft2.K1).run(); + p += d1; + } + break; + } + case DUOREAL: { + while (k1++ < K1) { + new DFTDuoRealFloat(reData, imData, + reBuffer, imBuffer, p, p + d1, d2, K2).run(); + p += 2 * d1; + k1++; + } + break; + } + case EVENREAL: { + while (k1++ < K1) { + new DFTEvenRealFloat(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + p += d1; + } + break; + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + while (k1++ < K1) { + new DFTLength2RealFloat(reData, imData, p, d2).run(); + p += d1; + } + break; + } + case LENGTH3: { + while (k1++ < K1) { + new DFTLength3RealFloat(reData, imData, p, d2).run(); + p += d1; + } + break; + } + case LENGTH4: { + while (k1++ < K1) { + new DFTLength4RealFloat(reData, imData, p, d2).run(); + p += d1; + } + break; + } + case LENGTH5: { + while (k1++ < K1) { + new DFTLength5RealFloat(reData, imData, p, d2).run(); + p += d1; + } + break; + } + case LENGTH6: { + while (k1++ < K1) { + new DFTLength6RealFloat(reData, imData, p, d2).run(); + p += d1; + } + break; + } + case LENGTH8: { + while (k1++ < K1) { + new DFTLength8RealFloat(reData, imData, p, d2).run(); + p += d1; + } + break; + } + case MIXEDRADIX: { + while (k1++ < K1) { + new DFTMixedRadixRealFloat(reData, imData, + reBuffer, imBuffer, p, d2, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, + fft2.K1).run(); + p += d1; + } + break; + } + case PADDEDRADER: { + while (k1++ < K1) { + new DFTPaddedRaderRealFloat(reData, imData, + p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + p += d1; + } + break; + } + case RADER: { + while (k1++ < K1) { + new DFTRaderRealFloat(reData, imData, reBuffer, imBuffer, + p, d2, fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + p += d1; + } + break; + } + case RADIX2: { + while (k1++ < K1) { + new DFTRadix2RealFloat(reData, imData, reBuffer, imBuffer, + p, d2, fft2.reUnitRootEvenFloat, fft2.imUnitRootEvenFloat, + fft2.reUnitRootOddFloat, fft2.imUnitRootOddFloat).run(); + p += d1; + } + break; + } + case SPLITRADIX: { + while (k1++ < K1) { + new DFTSplitRadixRealFloat(reData, imData, reBuffer, imBuffer, + p, d2, fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + p += d1; + } + break; + } + } + p = startIndex; + for (int n2 = 0; (n2 < halfK2); n2++) { + int n = 0; + for (k1 = 0; (k1 < K1); k1++) { + final float re = reData[p]; + final float im = imData[p]; + final float reRoot = reUnitRoot[n]; + final float imRoot = imUnitRoot[n]; + reBuffer[p] = re * reRoot - im * imRoot; + imBuffer[p] = re * imRoot + im * reRoot; + p += d1; + n += n2; + } + } + p = startIndex; + final FFTSetup fft1 = FFTSetup.transforms.get(new Integer(K1)); + switch (fft1.algorithm) { + case BRUTEFORCE: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run(); + p += d2; + } + break; + } + case COPRIMEFACTOR: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.ruritanian, fft1.chinese, fft1.K1).run(); + p += d2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength2Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH3: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength3Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH4: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength4Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH5: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength5Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH6: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength6Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case LENGTH8: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTLength8Float(reBuffer, imBuffer, p, d1).run(); + p += d2; + } + break; + } + case MIXEDRADIX: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat, + fft1.K1).run(); + p += d2; + } + break; + } + case PADDEDRADER: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTPaddedRaderFloat(reBuffer, imBuffer, + p, d1, fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular).run(); + p += d2; + } + break; + } + case RADER: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTRaderFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular).run(); + p += d2; + } + break; + } + case RADIX2: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTRadix2Float(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run(); + p += d2; + } + break; + } + case SPLITRADIX: { + for (int k2 = 0; (k2 < halfK2); k2++) { + new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData, + p, d1, fft1.reUnitRootFloat, fft1.imUnitRootFloat).run(); + p += d2; + } + break; + } + } + p = startIndex; + reData[p] = reBuffer[p]; + imData[p] = 0.0F; + p += d1; + int progressive = startIndex; + int regressive = startIndex + (K1 - 1) * d1; + int k2 = 1; + while (progressive < regressive) { + int q = progressive + d2; + while (k2 < halfK2) { + reData[p] = reBuffer[q]; + imData[p] = imBuffer[q]; + k2++; + p += d1; + q += d2; + } + k2 -= 2 - (K2 & 1); + progressive += d1; + q = regressive + k2 * d2; + while (0 <= k2) { + reData[p] = reBuffer[q]; + imData[p] = -imBuffer[q]; + k2--; + p += d1; + q -= d2; + } + k2 += 2; + regressive -= d1; + } + if (1 == (K1 & 1)) { + int q = progressive + k2 * d2; + while (k2 < halfK2) { + reData[p] = reBuffer[q]; + imData[p] = imBuffer[q]; + k2++; + p += d1; + q += d2; + } + } +} /* end run */ + +} /* end class DFTMixedRadixRealFloat */ + +/*==================================================================== +| DFTPaddedRader +\===================================================================*/ +static class DFTPaddedRader + +{ /* begin class DFTPaddedRader */ + +/*.................................................................... + DFTPaddedRader static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int prime, + final int paddedLength +) { + if (FFTSetup.taboos.contains(new Integer(paddedLength))) { + return(-1L); + } + final long K = (long)prime; + final long P = (long)paddedLength; + return(FFTSetup.FLALLOC * (P * 4L + 8L) + + FFTSetup.FLOP * (P * 6L + (K - 1L) * 2L + 2L) + + FFTSetup.FLASSIGN * (P * 4L + 2L + (K - 1L) * 2L + 2L + P * 6L + + (K - 1L) * 2L + 2L) + + FFTSetup.INTALLOC * 9L + + FFTSetup.INTOP * (2L + (K - 1L) * 5L + P * 2L + 1L + (K - 1L) * 3L) + + FFTSetup.INTASSIGN * (4L + (K - 1L) * 3L + 1L + P * 1L + 2L + + (K - 1L) * 3L) + + FFTSetup.IDX * (2L + (K - 1L) * 5L + 2L + P * 6L + (K - 1L) * 5L + 2L) + + 1L * (FFTSetup.NEWOBJ + + FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * 0L + + FFTSetup.FLASSIGN * 0L + + FFTSetup.INTALLOC * 0L + + FFTSetup.INTOP * 1L + + FFTSetup.INTASSIGN * 6L + + FFTSetup.IDX * 0L + + FFTSetup.NEWOBJ * 6L) + + 2L * (FFTSetup.cost(paddedLength) + + FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * 0L + + FFTSetup.FLASSIGN * 0L + + FFTSetup.INTALLOC * 0L + + FFTSetup.INTOP * (10L + 2L) + + FFTSetup.INTASSIGN * 0L + + FFTSetup.IDX * 0L + + FFTSetup.NEWOBJ * 1L) + ); +} /* end cost */ + +/*------------------------------------------------------------------*/ +static int[] getInverseModularPowerShuffling ( + final int[] modularShuffling, + final int paddedLength +) { + final int prime = modularShuffling.length; + int[] inverseShuffling = new int[prime]; + inverseShuffling[modularShuffling[prime - 3]] = 0; + inverseShuffling[modularShuffling[prime - 2]] = 1; + inverseShuffling[modularShuffling[prime - 1]] = paddedLength - prime + 3; + for (int k = 4; (k < prime); k++) { + inverseShuffling[modularShuffling[k - 3]] = paddedLength - prime + k; + } + return(inverseShuffling); +} /* end getInverseModularPowerShuffling */ + +} /* end class DFTPaddedRader */ + +/*==================================================================== +| DFTPaddedRaderDouble +\===================================================================*/ +static class DFTPaddedRaderDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTPaddedRaderDouble */ + +/*.................................................................... + DFTPaddedRaderDouble private variables +....................................................................*/ +private double[] imConvolver; +private double[] reConvolver; +private int[] inverseModular; +private int[] modular; + +/*.................................................................... + DFTPaddedRaderDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTPaddedRaderDouble ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride, + final double[] reConvolver, + final double[] imConvolver, + final int[] modular, + final int[] inverseModular +) { + super(reData, imData, startIndex, stride); + this.reConvolver = reConvolver; + this.imConvolver = imConvolver; + this.modular = modular; + this.inverseModular = inverseModular; +} /* end DFTPaddedRaderDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int prime = modular.length; + final int paddedLength = reConvolver.length; +//;IJ.log("DFTPaddedRaderDouble " + prime + " (" + paddedLength +")"); + final double[] rePadded = new double[paddedLength]; + final double[] imPadded = new double[paddedLength]; + final double[] reBuffer = new double[paddedLength]; + final double[] imBuffer = new double[paddedLength]; + final double reBaseline = reData[startIndex]; + final double imBaseline = imData[startIndex]; + for (int k = paddedLength - prime + 1, m = 1; (k < paddedLength); k++) { + final int q = startIndex + modular[m++] * stride; + rePadded[k] = reData[q]; + imPadded[k] = imData[q]; + } + final AcademicFourierTransform fft = new AcademicFourierTransform(paddedLength, 0); + fft.directTransform(rePadded, imPadded, reBuffer, imBuffer, + InputDataType.COMPLEXINPUT); + final double reSum = rePadded[0]; + final double imSum = imPadded[0]; + for (int k = 0; (k < paddedLength); k++) { + final double re = rePadded[k]; + final double im = imPadded[k]; + final double reWeight = reConvolver[k]; + final double imWeight = imConvolver[k]; + rePadded[k] = re * reWeight - im * imWeight; + imPadded[k] = re * imWeight + im * reWeight; + } + fft.directTransform(rePadded, imPadded, reBuffer, imBuffer, + InputDataType.COMPLEXINPUT); + int p = startIndex + stride; + for (int k = 1; (k < prime); k++) { + final int q = inverseModular[k]; + reData[p] = rePadded[q] + reBaseline; + imData[p] = imPadded[q] + imBaseline; + p += stride; + } + reData[startIndex] += reSum; + imData[startIndex] += imSum; +} /* end run */ + +/*.................................................................... + DFTPaddedRaderDouble static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static double[][] getConvolverReAndIm ( + final int[] modular, + final int paddedLength +) { + final int prime = modular.length; + final double[] reConvolver = new double[paddedLength]; + final double[] imConvolver = new double[paddedLength]; + final double angularStep = -2.0 * PI / (double)prime; + final double norm = 1.0 / (double)paddedLength; + int n = 0; + for (int k = prime - 2; (1 <= k); k--) { + final double shuffled = (double)modular[k]; + reConvolver[n] = norm * cos(shuffled * angularStep); + imConvolver[n] = norm * sin(shuffled * angularStep); + n++; + } + reConvolver[n] = norm * cos(angularStep); + imConvolver[n] = norm * sin(angularStep); + while (++n < paddedLength) { + reConvolver[n] = reConvolver[n - prime + 1]; + imConvolver[n] = imConvolver[n - prime + 1]; + } + final AcademicFourierTransform fft = new AcademicFourierTransform(paddedLength, 0); + fft.directTransform(reConvolver, imConvolver, null, null, + InputDataType.COMPLEXINPUT); + return(new double[][] { + reConvolver, + imConvolver + }); +} /* end getConvolverReAndIm */ + +} /* end class DFTPaddedRaderDouble */ + +/*==================================================================== +| DFTPaddedRaderFloat +\===================================================================*/ +static class DFTPaddedRaderFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTPaddedRaderFloat */ + +/*.................................................................... + DFTPaddedRaderFloat private variables +....................................................................*/ +private float[] imConvolver; +private float[] reConvolver; +private int[] inverseModular; +private int[] modular; + +/*.................................................................... + DFTPaddedRaderFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTPaddedRaderFloat ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride, + final float[] reConvolver, + final float[] imConvolver, + final int[] modular, + final int[] inverseModular +) { + super(reData, imData, startIndex, stride); + this.reConvolver = reConvolver; + this.imConvolver = imConvolver; + this.modular = modular; + this.inverseModular = inverseModular; +} /* end DFTPaddedRaderFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int prime = modular.length; + final int paddedLength = reConvolver.length; +//;IJ.log("DFTPaddedRaderFloat " + prime + " (" + paddedLength +")"); + final float[] rePadded = new float[paddedLength]; + final float[] imPadded = new float[paddedLength]; + final float[] reBuffer = new float[paddedLength]; + final float[] imBuffer = new float[paddedLength]; + final float reBaseline = reData[startIndex]; + final float imBaseline = imData[startIndex]; + for (int k = paddedLength - prime + 1, m = 1; (k < paddedLength); k++) { + final int q = startIndex + modular[m++] * stride; + rePadded[k] = reData[q]; + imPadded[k] = imData[q]; + } + final AcademicFourierTransform fft = new AcademicFourierTransform(paddedLength, 0); + fft.directTransform(rePadded, imPadded, reBuffer, imBuffer, + InputDataType.COMPLEXINPUT); + final float reSum = rePadded[0]; + final float imSum = imPadded[0]; + for (int k = 0; (k < paddedLength); k++) { + final float re = rePadded[k]; + final float im = imPadded[k]; + final float reWeight = reConvolver[k]; + final float imWeight = imConvolver[k]; + rePadded[k] = re * reWeight - im * imWeight; + imPadded[k] = re * imWeight + im * reWeight; + } + fft.directTransform(rePadded, imPadded, reBuffer, imBuffer, + InputDataType.COMPLEXINPUT); + int p = startIndex + stride; + for (int k = 1; (k < prime); k++) { + final int q = inverseModular[k]; + reData[p] = rePadded[q] + reBaseline; + imData[p] = imPadded[q] + imBaseline; + p += stride; + } + reData[startIndex] += reSum; + imData[startIndex] += imSum; +} /* end run */ + +/*.................................................................... + DFTPaddedRaderFloat static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static float[][] getConvolverReAndIm ( + final int[] modular, + final int paddedLength +) { + final int prime = modular.length; + final float[] reConvolver = new float[paddedLength]; + final float[] imConvolver = new float[paddedLength]; + final float angularStep = -2.0F * (float)PI / (float)prime; + final float norm = 1.0F / (float)paddedLength; + int n = 0; + for (int k = prime - 2; (1 <= k); k--) { + final float shuffled = (float)modular[k]; + reConvolver[n] = norm * (float)cos((double)(shuffled * angularStep)); + imConvolver[n] = norm * (float)sin((double)(shuffled * angularStep)); + n++; + } + reConvolver[n] = norm * (float)cos((double)angularStep); + imConvolver[n] = norm * (float)sin((double)angularStep); + while (++n < paddedLength) { + reConvolver[n] = reConvolver[n - prime + 1]; + imConvolver[n] = imConvolver[n - prime + 1]; + } + final AcademicFourierTransform fft = new AcademicFourierTransform(paddedLength, 0); + fft.directTransform(reConvolver, imConvolver, null, null, + InputDataType.COMPLEXINPUT); + return(new float[][] { + reConvolver, + imConvolver + }); +} /* end getConvolverReAndIm */ + +} /* end class DFTPaddedRaderFloat */ + +/*==================================================================== +| DFTPaddedRaderReal +\===================================================================*/ +static class DFTPaddedRaderReal + +{ /* begin class DFTPaddedRaderReal */ + +/*.................................................................... + DFTPaddedRaderReal static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int prime, + final int paddedLength +) { + if (FFTSetup.taboos.contains(new Integer(paddedLength))) { + return(-1L); + } + final long K = (long)prime; + final long k = K >> 1L; + final long P = (long)paddedLength; + return(FFTSetup.FLALLOC * (P * 4L + 6L) + + FFTSetup.FLOP * (P * 6L + k * 1L + 1L) + + FFTSetup.FLASSIGN * (P * 4L + 1L + (K - 1L) * 1L + 1L + P * 6L + + k * 2L + 2L) + + FFTSetup.INTALLOC * 8L + + FFTSetup.INTOP * (4L + (K - 1L) * 5L + P * 2L + 1L + k * 3L) + + FFTSetup.INTASSIGN * (5L + (K - 1L) * 2L + 1L + P * 1L + 1L + + k * 3L) + + FFTSetup.IDX * (1L + (K - 1L) * 3L + 1L + P * 6L + k * 5L + 2L) + + 1L * (FFTSetup.NEWOBJ + + FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * 0L + + FFTSetup.FLASSIGN * 0L + + FFTSetup.INTALLOC * 0L + + FFTSetup.INTOP * 1L + + FFTSetup.INTASSIGN * 6L + + FFTSetup.IDX * 0L + + FFTSetup.NEWOBJ * 6L) + + 1L * (FFTSetupReal.cost(paddedLength) + + FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * (k * 1L) + + FFTSetup.FLASSIGN * (k * 2L) + + FFTSetup.INTALLOC * 2L + + FFTSetup.INTOP * (10L + 2L + 1L + k * 3L) + + FFTSetup.INTASSIGN * (2L + k * 2L) + + FFTSetup.IDX * (k * 4L) + + FFTSetup.NEWOBJ * 1L) + + 1L * (FFTSetup.cost(paddedLength) + + FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * 0L + + FFTSetup.FLASSIGN * 0L + + FFTSetup.INTALLOC * 0L + + FFTSetup.INTOP * (10L + 2L) + + FFTSetup.INTASSIGN * 0L + + FFTSetup.IDX * 0L + + FFTSetup.NEWOBJ * 1L) + ); +} /* end cost */ + +} /* end class DFTPaddedRaderReal */ + +/*==================================================================== +| DFTPaddedRaderRealDouble +\===================================================================*/ +static class DFTPaddedRaderRealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTPaddedRaderRealDouble */ + +/*.................................................................... + DFTPaddedRaderRealDouble private variables +....................................................................*/ +private double[] imConvolver; +private double[] reConvolver; +private int[] inverseModular; +private int[] modular; + +/*.................................................................... + DFTPaddedRaderRealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTPaddedRaderRealDouble ( + final double[] reData, + final double[] imData, + final int startIndex, + final int stride, + final double[] reConvolver, + final double[] imConvolver, + final int[] modular, + final int[] inverseModular +) { + super(reData, imData, startIndex, stride); + this.reConvolver = reConvolver; + this.imConvolver = imConvolver; + this.modular = modular; + this.inverseModular = inverseModular; +} /* end DFTPaddedRaderRealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int prime = modular.length; + final int halfPrime = (prime >> 1) + 1; + final int paddedLength = reConvolver.length; +//;IJ.log("DFTPaddedRaderRealDouble " + prime + " (" + paddedLength +")"); + final double[] rePadded = new double[paddedLength]; + final double[] imPadded = new double[paddedLength]; + final double[] reBuffer = new double[paddedLength]; + final double[] imBuffer = new double[paddedLength]; + final double reBaseline = reData[startIndex]; + for (int k = paddedLength - prime + 1, m = 1; (k < paddedLength); k++) { + rePadded[k] = reData[startIndex + modular[m++] * stride]; + } + final AcademicFourierTransform fft = new AcademicFourierTransform(paddedLength, 0); + fft.directTransform(rePadded, imPadded, reBuffer, imBuffer, + InputDataType.REALINPUT); + final double reSum = rePadded[0]; + for (int k = 0; (k < paddedLength); k++) { + final double re = rePadded[k]; + final double im = imPadded[k]; + final double reWeight = reConvolver[k]; + final double imWeight = imConvolver[k]; + rePadded[k] = re * reWeight - im * imWeight; + imPadded[k] = re * imWeight + im * reWeight; + } + fft.directTransform(rePadded, imPadded, reBuffer, imBuffer, + InputDataType.COMPLEXINPUT); + int p = startIndex + stride; + for (int k = 1; (k < halfPrime); k++) { + final int q = inverseModular[k]; + reData[p] = rePadded[q] + reBaseline; + imData[p] = imPadded[q]; + p += stride; + } + reData[startIndex] += reSum; + imData[startIndex] = 0.0; +} /* end run */ + +} /* end class DFTPaddedRaderRealDouble */ + +/*==================================================================== +| DFTPaddedRaderRealFloat +\===================================================================*/ +static class DFTPaddedRaderRealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTPaddedRaderRealFloat */ + +/*.................................................................... + DFTPaddedRaderRealFloat private variables +....................................................................*/ +private float[] imConvolver; +private float[] reConvolver; +private int[] inverseModular; +private int[] modular; + +/*.................................................................... + DFTPaddedRaderRealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTPaddedRaderRealFloat ( + final float[] reData, + final float[] imData, + final int startIndex, + final int stride, + final float[] reConvolver, + final float[] imConvolver, + final int[] modular, + final int[] inverseModular +) { + super(reData, imData, startIndex, stride); + this.reConvolver = reConvolver; + this.imConvolver = imConvolver; + this.modular = modular; + this.inverseModular = inverseModular; +} /* end DFTPaddedRaderRealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int prime = modular.length; + final int halfPrime = (prime >> 1) + 1; + final int paddedLength = reConvolver.length; +//;IJ.log("DFTPaddedRaderRealFloat " + prime + " (" + paddedLength +")"); + final float[] rePadded = new float[paddedLength]; + final float[] imPadded = new float[paddedLength]; + final float[] reBuffer = new float[paddedLength]; + final float[] imBuffer = new float[paddedLength]; + final float reBaseline = reData[startIndex]; + for (int k = paddedLength - prime + 1, m = 1; (k < paddedLength); k++) { + rePadded[k] = reData[startIndex + modular[m++] * stride]; + } + final AcademicFourierTransform fft = new AcademicFourierTransform(paddedLength, 0); + fft.directTransform(rePadded, imPadded, reBuffer, imBuffer, + InputDataType.REALINPUT); + final float reSum = rePadded[0]; + for (int k = 0; (k < paddedLength); k++) { + final float re = rePadded[k]; + final float im = imPadded[k]; + final float reWeight = reConvolver[k]; + final float imWeight = imConvolver[k]; + rePadded[k] = re * reWeight - im * imWeight; + imPadded[k] = re * imWeight + im * reWeight; + } + fft.directTransform(rePadded, imPadded, reBuffer, imBuffer, + InputDataType.COMPLEXINPUT); + int p = startIndex + stride; + for (int k = 1; (k < halfPrime); k++) { + final int q = inverseModular[k]; + reData[p] = rePadded[q] + reBaseline; + imData[p] = imPadded[q]; + p += stride; + } + reData[startIndex] += reSum; + imData[startIndex] = 0.0F; +} /* end run */ + +} /* end class DFTPaddedRaderRealFloat */ + +/*==================================================================== +| DFTRader +\===================================================================*/ +static class DFTRader + +{ /* begin class DFTRader */ + +/*.................................................................... + DFTRader static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int prime +) { + if (FFTSetup.taboos.contains(new Integer(prime)) + || FFTSetup.taboos.contains(new Integer(prime - 1))) { + return(-1L); + } + final long K = (long)prime; + return(FFTSetup.FLALLOC * 6L + + FFTSetup.FLOP * ((K - 1L) * 6L + (K - 1L) * 2L + 2L) + + FFTSetup.FLASSIGN * (2L + (K - 1L) * 2L + 2L + (K - 1L) * 6L + + (K - 1L) * 2L + 2L) + + FFTSetup.INTALLOC * 8L + + FFTSetup.INTOP * (1L + (K - 1L) * 5L + 4L + (K- 1L) * 3L + 2L + + (K - 1L) * 5L) + + FFTSetup.INTASSIGN * (3L + (K - 1L) * 3L + 3L + (K - 1L) * 2L + 2L + + (K - 1L) * 3L) + + FFTSetup.IDX * (2L + (K - 1L) * 5L + 4L + (K - 1L) * 6L + + (K - 1L) * 5L + 4L) + + FFTSetup.NEWOBJ * 2L + + 2L * FFTSetup.cost(prime - 1) + ); +} /* end cost */ + +/*------------------------------------------------------------------*/ +static int[] getInverseModularPowerShuffling ( + final int[] modularShuffling +) { + final int prime = modularShuffling.length; + int[] inverseShuffling = new int[prime]; + inverseShuffling[0] = 0; + inverseShuffling[modularShuffling[prime - 3]] = 1; + inverseShuffling[modularShuffling[prime - 2]] = 2; + inverseShuffling[modularShuffling[prime - 1]] = 3; + for (int k = 4; (k < prime); k++) { + inverseShuffling[modularShuffling[k - 3]] = k; + } + return(inverseShuffling); +} /* end getInverseModularPowerShuffling */ + +/*------------------------------------------------------------------*/ +static int[] getModularPowerShuffling ( + final int prime +) { + final int g = smallestPrimitiveRootOfPrime(prime); + int[] modularShuffling = new int[prime]; + modularShuffling[0] = 0; + for (int k = 1; (k < prime); k++) { + modularShuffling[k] = modularPositivePower(g, k, prime); + } + return(modularShuffling); +} /* end getModularPowerShuffling */ + +/*.................................................................... + DFTRader private methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static private TreeMap<Integer, Integer> factorize ( + int number +) { + final TreeMap<Integer, Integer> factors = + new TreeMap<Integer, Integer>(); + for (int divisor = 2; (divisor <= number); divisor++) { + if ((divisor * (number / divisor)) == number) { + number /= divisor; + int multiplicity = 1; + while ((divisor * (number / divisor)) == number) { + number /= divisor; + multiplicity++; + } + factors.put(new Integer(divisor), new Integer(multiplicity)); + } + } + return(factors); +} /* end factorize */ + +/*------------------------------------------------------------------*/ +static private int modularPositivePower ( + int number, + int exponent, + final int modulo +) { + int result = 1; + number -= modulo * (number / modulo); + while (0 < exponent) { + if (1 == (exponent & 1)) { + result *= number; + result -= modulo * (result / modulo); + } + exponent >>= 1; + number *= number; + number -= modulo * (number / modulo); + } + return(result); +} /* end modularPositivePower */ + +/*------------------------------------------------------------------*/ +static private int smallestPrimitiveRootOfPrime ( + final int prime +) { + if (2 == prime) { + return(1); + } + final TreeMap<Integer, Integer> factors = factorize(prime - 1); + int result = -1; + for (int candidate = 1; (candidate < prime); candidate++) { + result = candidate; + for (Integer primeDivisor: factors.navigableKeySet()) { + if (1 == modularPositivePower(candidate, (prime - 1) + / primeDivisor.intValue(), prime)) { + result = -1; + break; + } + } + if (0 < result) { + break; + } + } + return(result); +} /* end smallestPrimitiveRootOfPrime */ + +} /* end class DFTRader */ + +/*==================================================================== +| DFTRaderDouble +\===================================================================*/ +static class DFTRaderDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTRaderDouble */ + +/*.................................................................... + DFTRaderDouble private variables +....................................................................*/ +private double[] imBuffer; +private double[] imConvolver; +private double[] reBuffer; +private double[] reConvolver; +private int[] inverseModular; +private int[] modular; + +/*.................................................................... + DFTRaderDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTRaderDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final double[] reConvolver, + final double[] imConvolver, + final int[] modular, + final int[] inverseModular +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reConvolver = reConvolver; + this.imConvolver = imConvolver; + this.modular = modular; + this.inverseModular = inverseModular; +} /* end DFTRaderDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int prime = modular.length; +//;IJ.log("DFTRaderDouble " + prime); + final double reBaseline = reData[startIndex]; + final double imBaseline = imData[startIndex]; + int p = startIndex + stride; + for (int k = 1; (k < prime); k++) { + final int q = startIndex + modular[k] * stride; + reBuffer[p] = reData[q]; + imBuffer[p] = imData[q]; + p += stride; + } + final FFTSetup fft = FFTSetup.transforms.get(new Integer(prime - 1)); + p = startIndex + stride; + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData, + p, stride, fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2Double(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH3: { + throw(new IllegalStateException()); + } + case LENGTH4: { + new DFTLength4Double(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH5: { + throw(new IllegalStateException()); + } + case LENGTH6: { + new DFTLength6Double(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH8: { + new DFTLength8Double(reBuffer, imBuffer, p, stride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble, + fft.K1).run(); + break; + } + case PADDEDRADER: { + throw(new IllegalStateException()); + } + case RADER: { + throw(new IllegalStateException()); + } + case RADIX2: { + new DFTRadix2Double(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + reBuffer[startIndex] = reBuffer[p]; + imBuffer[startIndex] = imBuffer[p]; + for (int k = 0, K = prime - 1; (k < K); k++) { + final double re = reBuffer[p]; + final double im = imBuffer[p]; + final double reWeight = reConvolver[k]; + final double imWeight = imConvolver[k]; + reBuffer[p] = re * reWeight - im * imWeight; + imBuffer[p] = re * imWeight + im * reWeight; + p += stride; + } + p = startIndex + stride; + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData, + p, stride, fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2Double(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH3: { + throw(new IllegalStateException()); + } + case LENGTH4: { + new DFTLength4Double(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH5: { + throw(new IllegalStateException()); + } + case LENGTH6: { + new DFTLength6Double(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH8: { + new DFTLength8Double(reBuffer, imBuffer, p, stride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble, + fft.K1).run(); + break; + } + case PADDEDRADER: { + throw(new IllegalStateException()); + } + case RADER: { + throw(new IllegalStateException()); + } + case RADIX2: { + new DFTRadix2Double(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + for (int k = 1; (k < prime); k++) { + final int q = startIndex + inverseModular[k] * stride; + reData[p] = reBuffer[q] + reBaseline; + imData[p] = imBuffer[q] + imBaseline; + p += stride; + } + reData[startIndex] += reBuffer[startIndex]; + imData[startIndex] += imBuffer[startIndex]; +} /* end run */ + +/*.................................................................... + DFTRaderDouble static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static double[][] getConvolverReAndIm ( + final int[] modular +) { + final int prime = modular.length; + final double[] reConvolver = new double[prime - 1]; + final double[] imConvolver = new double[prime - 1]; + final double angularStep = -2.0 * PI / (double)prime; + final double norm = 1.0 / (double)(prime - 1); + int n = 0; + for (int k = prime - 2; (1 <= k); k--) { + final double shuffled = (double)modular[k]; + reConvolver[n] = norm * cos(shuffled * angularStep); + imConvolver[n] = norm * sin(shuffled * angularStep); + n++; + } + reConvolver[n] = norm * cos(angularStep); + imConvolver[n] = norm * sin(angularStep); + final AcademicFourierTransform fft = new AcademicFourierTransform(prime - 1, 0); + fft.directTransform(reConvolver, imConvolver, null, null, + InputDataType.COMPLEXINPUT); + return(new double[][] { + reConvolver, + imConvolver + }); +} /* end getConvolverReAndIm */ + +} /* end class DFTRaderDouble */ + +/*==================================================================== +| DFTRaderFloat +\===================================================================*/ +static class DFTRaderFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTRaderFloat */ + +/*.................................................................... + DFTRaderFloat private variables +....................................................................*/ +private float[] imBuffer; +private float[] imConvolver; +private float[] reBuffer; +private float[] reConvolver; +private int[] inverseModular; +private int[] modular; + +/*.................................................................... + DFTRaderFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTRaderFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final float[] reConvolver, + final float[] imConvolver, + final int[] modular, + final int[] inverseModular +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reConvolver = reConvolver; + this.imConvolver = imConvolver; + this.modular = modular; + this.inverseModular = inverseModular; +} /* end DFTRaderFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int prime = modular.length; +//;IJ.log("DFTRaderFloat " + prime); + final float reBaseline = reData[startIndex]; + final float imBaseline = imData[startIndex]; + int p = startIndex + stride; + for (int k = 1; (k < prime); k++) { + final int q = startIndex + modular[k] * stride; + reBuffer[p] = reData[q]; + imBuffer[p] = imData[q]; + p += stride; + } + final FFTSetup fft = FFTSetup.transforms.get(new Integer(prime - 1)); + p = startIndex + stride; + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData, + p, stride, fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2Float(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH3: { + throw(new IllegalStateException()); + } + case LENGTH4: { + new DFTLength4Float(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH5: { + throw(new IllegalStateException()); + } + case LENGTH6: { + new DFTLength6Float(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH8: { + new DFTLength8Float(reBuffer, imBuffer, p, stride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat, + fft.K1).run(); + break; + } + case PADDEDRADER: { + throw(new IllegalStateException()); + } + case RADER: { + throw(new IllegalStateException()); + } + case RADIX2: { + new DFTRadix2Float(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + reBuffer[startIndex] = reBuffer[p]; + imBuffer[startIndex] = imBuffer[p]; + for (int k = 0, K = prime - 1; (k < K); k++) { + final float re = reBuffer[p]; + final float im = imBuffer[p]; + final float reWeight = reConvolver[k]; + final float imWeight = imConvolver[k]; + reBuffer[p] = re * reWeight - im * imWeight; + imBuffer[p] = re * imWeight + im * reWeight; + p += stride; + } + p = startIndex + stride; + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData, + p, stride, fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2Float(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH3: { + throw(new IllegalStateException()); + } + case LENGTH4: { + new DFTLength4Float(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH5: { + throw(new IllegalStateException()); + } + case LENGTH6: { + new DFTLength6Float(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH8: { + new DFTLength8Float(reBuffer, imBuffer, p, stride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat, + fft.K1).run(); + break; + } + case PADDEDRADER: { + throw(new IllegalStateException()); + } + case RADER: { + throw(new IllegalStateException()); + } + case RADIX2: { + new DFTRadix2Float(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + for (int k = 1; (k < prime); k++) { + final int q = startIndex + inverseModular[k] * stride; + reData[p] = reBuffer[q] + reBaseline; + imData[p] = imBuffer[q] + imBaseline; + p += stride; + } + reData[startIndex] += reBuffer[startIndex]; + imData[startIndex] += imBuffer[startIndex]; +} /* end run */ + +/*.................................................................... + DFTRaderFloat static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static float[][] getConvolverReAndIm ( + final int[] modular +) { + final int prime = modular.length; + final float[] reConvolver = new float[prime - 1]; + final float[] imConvolver = new float[prime - 1]; + final float angularStep = -2.0F * (float)PI / (float)prime; + final float norm = 1.0F / (float)(prime - 1); + int n = 0; + for (int k = prime - 2; (1 <= k); k--) { + final float shuffled = (float)modular[k]; + reConvolver[n] = norm * (float)cos((double)(shuffled * angularStep)); + imConvolver[n] = norm * (float)sin((double)(shuffled * angularStep)); + n++; + } + reConvolver[n] = norm * (float)cos((double)angularStep); + imConvolver[n] = norm * (float)sin((double)angularStep); + final AcademicFourierTransform fft = new AcademicFourierTransform(prime - 1, 0); + fft.directTransform(reConvolver, imConvolver, null, null, + InputDataType.COMPLEXINPUT); + return(new float[][] { + reConvolver, + imConvolver + }); +} /* end getConvolverReAndIm */ + +} /* end class DFTRaderFloat */ + +/*==================================================================== +| DFTRaderReal +\===================================================================*/ +static class DFTRaderReal + +{ /* begin class DFTRaderReal */ + +/*.................................................................... + DFTRaderReal static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int prime +) { + if (FFTSetup.taboos.contains(new Integer(prime)) + || FFTSetup.taboos.contains(new Integer(prime - 1))) { + return(-1L); + } + final long K = (long)prime; + final long k = K >> 1L; + return(FFTSetup.FLALLOC * 5L + + FFTSetup.FLOP * (k * 1L + (K - 1L) * 6L + k * 1L + 1L) + + FFTSetup.FLASSIGN * (1L + (K - 1L) * 1L + k * 2L + 1L + (K - 1L) * 6L + + k * 2L + 2L) + + FFTSetup.INTALLOC * 11L + + FFTSetup.INTOP * (3L + (K - 1L) * 5L + 7L + k * 3L + 1L + + (K- 1L) * 3L + 3L + k * 5L) + + FFTSetup.INTASSIGN * (4L + (K - 1L) * 3L + 3L + k * 2L + 2L + + (K - 1L) * 2L + 2L + k * 3L) + + FFTSetup.IDX * (1L + (K - 1L) * 3L + k * 4L + 2L + (K - 1L) * 6L + + k * 5L + 3L) + + FFTSetup.NEWOBJ * 2L + + FFTSetupReal.cost(prime - 1) + FFTSetup.cost(prime - 1) + ); +} /* end cost */ + +} /* end class DFTRaderReal */ + +/*==================================================================== +| DFTRaderRealDouble +\===================================================================*/ +static class DFTRaderRealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTRaderRealDouble */ + +/*.................................................................... + DFTRaderRealDouble private variables +....................................................................*/ +private double[] imBuffer; +private double[] imConvolver; +private double[] reBuffer; +private double[] reConvolver; +private int[] inverseModular; +private int[] modular; + +/*.................................................................... + DFTRaderRealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTRaderRealDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final double[] reConvolver, + final double[] imConvolver, + final int[] modular, + final int[] inverseModular +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reConvolver = reConvolver; + this.imConvolver = imConvolver; + this.modular = modular; + this.inverseModular = inverseModular; +} /* end DFTRaderRealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int prime = modular.length; + final int halfPrime = (prime >> 1) + 1; +//;IJ.log("DFTRaderRealDouble " + prime); + final double reBaseline = reData[startIndex]; + int p = startIndex + stride; + for (int k = 1; (k < prime); k++) { + final int q = startIndex + modular[k] * stride; + reBuffer[p] = reData[q]; + p += stride; + } + final FFTSetupReal fftReal = + FFTSetupReal.transforms.get(new Integer(prime - 1)); + p = startIndex + stride; + switch (fftReal.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealDouble(reBuffer, imBuffer, reData, imData, + p, stride, + fftReal.reUnitRootDouble, fftReal.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealDouble(reBuffer, imBuffer, reData, imData, + p, stride, + fftReal.ruritanian, fftReal.chinese, fftReal.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealDouble(reBuffer, imBuffer, reData, imData, + p, stride, + fftReal.reUnitRootDouble, fftReal.imUnitRootDouble).run(); + break; + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2RealDouble(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH3: { + throw(new IllegalStateException()); + } + case LENGTH4: { + new DFTLength4RealDouble(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH5: { + throw(new IllegalStateException()); + } + case LENGTH6: { + new DFTLength6RealDouble(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH8: { + new DFTLength8RealDouble(reBuffer, imBuffer, p, stride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealDouble(reBuffer, imBuffer, reData, imData, + p, stride, + fftReal.reUnitRootDouble, fftReal.imUnitRootDouble, + fftReal.K1).run(); + break; + } + case PADDEDRADER: { + throw(new IllegalStateException()); + } + case RADER: { + throw(new IllegalStateException()); + } + case RADIX2: { + new DFTRadix2RealDouble(reBuffer, imBuffer, reData, imData, + p, stride, + fftReal.reUnitRootEvenDouble, fftReal.imUnitRootEvenDouble, + fftReal.reUnitRootOddDouble, fftReal.imUnitRootOddDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealDouble(reBuffer, imBuffer, reData, imData, + p, stride, + fftReal.reUnitRootDouble, fftReal.imUnitRootDouble).run(); + break; + } + } + int progressive = p + stride; + int regressive = p + (prime - 2) * stride; + while (progressive < regressive) { + reBuffer[regressive] = reBuffer[progressive]; + imBuffer[regressive] = -imBuffer[progressive]; + progressive += stride; + regressive -= stride; + } + reBuffer[startIndex] = reBuffer[p]; + for (int k = 0, K = prime - 1; (k < K); k++) { + final double re = reBuffer[p]; + final double im = imBuffer[p]; + final double reWeight = reConvolver[k]; + final double imWeight = imConvolver[k]; + reBuffer[p] = re * reWeight - im * imWeight; + imBuffer[p] = re * imWeight + im * reWeight; + p += stride; + } + p = startIndex + stride; + final FFTSetup fft = FFTSetup.transforms.get(new Integer(prime - 1)); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceDouble(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorDouble(reBuffer, imBuffer, reData, imData, + p, stride, fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2Double(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH3: { + throw(new IllegalStateException()); + } + case LENGTH4: { + new DFTLength4Double(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH5: { + throw(new IllegalStateException()); + } + case LENGTH6: { + new DFTLength6Double(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH8: { + new DFTLength8Double(reBuffer, imBuffer, p, stride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixDouble(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble, + fft.K1).run(); + break; + } + case PADDEDRADER: { + throw(new IllegalStateException()); + } + case RADER: { + throw(new IllegalStateException()); + } + case RADIX2: { + new DFTRadix2Double(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixDouble(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + for (int k = 1; (k < halfPrime); k++) { + final int q = startIndex + inverseModular[k] * stride; + reData[p] = reBuffer[q] + reBaseline; + imData[p] = imBuffer[q]; + p += stride; + } + reData[startIndex] += reBuffer[startIndex]; + imData[startIndex] = 0.0; +} /* end run */ + +} /* end class DFTRaderRealDouble */ + +/*==================================================================== +| DFTRaderRealFloat +\===================================================================*/ +static class DFTRaderRealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTRaderRealFloat */ + +/*.................................................................... + DFTRaderRealFloat private variables +....................................................................*/ +private float[] imBuffer; +private float[] imConvolver; +private float[] reBuffer; +private float[] reConvolver; +private int[] inverseModular; +private int[] modular; + +/*.................................................................... + DFTRaderRealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTRaderRealFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final float[] reConvolver, + final float[] imConvolver, + final int[] modular, + final int[] inverseModular +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reConvolver = reConvolver; + this.imConvolver = imConvolver; + this.modular = modular; + this.inverseModular = inverseModular; +} /* end DFTRaderRealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int prime = modular.length; + final int halfPrime = (prime >> 1) + 1; +//;IJ.log("DFTRaderRealFloat " + prime); + final float reBaseline = reData[startIndex]; + int p = startIndex + stride; + for (int k = 1; (k < prime); k++) { + final int q = startIndex + modular[k] * stride; + reBuffer[p] = reData[q]; + p += stride; + } + final FFTSetupReal fftReal = + FFTSetupReal.transforms.get(new Integer(prime - 1)); + p = startIndex + stride; + switch (fftReal.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealFloat(reBuffer, imBuffer, reData, imData, + p, stride, + fftReal.reUnitRootFloat, fftReal.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealFloat(reBuffer, imBuffer, reData, imData, + p, stride, + fftReal.ruritanian, fftReal.chinese, fftReal.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealFloat(reBuffer, imBuffer, reData, imData, + p, stride, + fftReal.reUnitRootFloat, fftReal.imUnitRootFloat).run(); + break; + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2RealFloat(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH3: { + throw(new IllegalStateException()); + } + case LENGTH4: { + new DFTLength4RealFloat(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH5: { + throw(new IllegalStateException()); + } + case LENGTH6: { + new DFTLength6RealFloat(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH8: { + new DFTLength8RealFloat(reBuffer, imBuffer, p, stride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealFloat(reBuffer, imBuffer, reData, imData, + p, stride, + fftReal.reUnitRootFloat, fftReal.imUnitRootFloat, + fftReal.K1).run(); + break; + } + case PADDEDRADER: { + throw(new IllegalStateException()); + } + case RADER: { + throw(new IllegalStateException()); + } + case RADIX2: { + new DFTRadix2RealFloat(reBuffer, imBuffer, reData, imData, + p, stride, + fftReal.reUnitRootEvenFloat, fftReal.imUnitRootEvenFloat, + fftReal.reUnitRootOddFloat, fftReal.imUnitRootOddFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealFloat(reBuffer, imBuffer, reData, imData, + p, stride, + fftReal.reUnitRootFloat, fftReal.imUnitRootFloat).run(); + break; + } + } + int progressive = p + stride; + int regressive = p + (prime - 2) * stride; + while (progressive < regressive) { + reBuffer[regressive] = reBuffer[progressive]; + imBuffer[regressive] = -imBuffer[progressive]; + progressive += stride; + regressive -= stride; + } + reBuffer[startIndex] = reBuffer[p]; + for (int k = 0, K = prime - 1; (k < K); k++) { + final float re = reBuffer[p]; + final float im = imBuffer[p]; + final float reWeight = reConvolver[k]; + final float imWeight = imConvolver[k]; + reBuffer[p] = re * reWeight - im * imWeight; + imBuffer[p] = re * imWeight + im * reWeight; + p += stride; + } + p = startIndex + stride; + final FFTSetup fft = FFTSetup.transforms.get(new Integer(prime - 1)); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceFloat(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorFloat(reBuffer, imBuffer, reData, imData, + p, stride, fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2Float(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH3: { + throw(new IllegalStateException()); + } + case LENGTH4: { + new DFTLength4Float(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH5: { + throw(new IllegalStateException()); + } + case LENGTH6: { + new DFTLength6Float(reBuffer, imBuffer, p, stride).run(); + break; + } + case LENGTH8: { + new DFTLength8Float(reBuffer, imBuffer, p, stride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixFloat(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat, + fft.K1).run(); + break; + } + case PADDEDRADER: { + throw(new IllegalStateException()); + } + case RADER: { + throw(new IllegalStateException()); + } + case RADIX2: { + new DFTRadix2Float(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixFloat(reBuffer, imBuffer, reData, imData, + p, stride, fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + for (int k = 1; (k < halfPrime); k++) { + final int q = startIndex + inverseModular[k] * stride; + reData[p] = reBuffer[q] + reBaseline; + imData[p] = imBuffer[q]; + p += stride; + } + reData[startIndex] += reBuffer[startIndex]; + imData[startIndex] = 0.0F; +} /* end run */ + +} /* end class DFTRaderRealFloat */ + +/*==================================================================== +| DFTRadix2 +\===================================================================*/ +static class DFTRadix2 + +{ /* begin class DFTRadix2 */ + +/*.................................................................... + DFTRadix2 static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int transformLength +) { + if (FFTSetup.taboos.contains(new Integer(transformLength / 2))) { + return(-1L); + } + final long k = (long)(transformLength >> 1); + return(FFTSetup.FLALLOC * 4L + + FFTSetup.FLOP * (k * 6L + k * 4L) + + FFTSetup.FLASSIGN * (k * 4L + k * 8L + k * 4L) + + FFTSetup.INTALLOC * 8L + + FFTSetup.INTOP * (5L + k * 6L + k * 5L + k * 4L) + + FFTSetup.INTASSIGN * (6L + k * 5L + 2L + k * 4L + 1L + k * 3L) + + FFTSetup.IDX * (k * 8L + k * 10L + k * 4L) + + FFTSetup.NEWOBJ * 2L + + 2L * FFTSetup.cost(transformLength / 2) + ); +} /* end cost */ + +} /* end class DFTRadix2 */ + +/*==================================================================== +| DFTRadix2Double +\===================================================================*/ +static class DFTRadix2Double + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTRadix2Double */ + +/*.................................................................... + DFTRadix2Double static variables +....................................................................*/ +private double[] imBuffer; +private double[] imUnitRoot; +private double[] reBuffer; +private double[] reUnitRoot; + +/*.................................................................... + DFTRadix2Double constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTRadix2Double ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final double[] reUnitRoot, + final double[] imUnitRoot +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; +} /* end DFTRadix2Double */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int K2 = reUnitRoot.length; +//;IJ.log("DFTRadix2Double " + (2 * K2)); + final int doubleStride = stride << 1; + final FFTSetup fft = FFTSetup.transforms.get(new Integer(K2)); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.ruritanian, fft.chinese, fft.K1).run(); + new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + new DFTLength2Double(reData, imData, + startIndex, doubleStride).run(); + new DFTLength2Double(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH3: { + new DFTLength3Double(reData, imData, + startIndex, doubleStride).run(); + new DFTLength3Double(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH4: { + new DFTLength4Double(reData, imData, + startIndex, doubleStride).run(); + new DFTLength4Double(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH5: { + new DFTLength5Double(reData, imData, + startIndex, doubleStride).run(); + new DFTLength5Double(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH6: { + new DFTLength6Double(reData, imData, + startIndex, doubleStride).run(); + new DFTLength6Double(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH8: { + new DFTLength8Double(reData, imData, + startIndex, doubleStride).run(); + new DFTLength8Double(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble, + fft.K1).run(); + new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble, + fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderDouble(reData, imData, + startIndex, doubleStride, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + new DFTPaddedRaderDouble(reData, imData, + startIndex + stride, doubleStride, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + new DFTRaderDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2Double(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + new DFTRadix2Double(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + int m0 = startIndex; + int i0 = startIndex; + int i1 = startIndex + K2 * stride; + for (int m = 0; (m < K2); m++) { + reBuffer[i0] = reData[m0]; + imBuffer[i0] = imData[m0]; + m0 += stride; + reBuffer[i1] = reData[m0]; + imBuffer[i1] = imData[m0]; + m0 += stride; + i0 += stride; + i1 += stride; + } + m0 = K2; + for (int m = 0; (m < K2); m++) { + m0--; + i1 -= stride; + i0 -= stride; + reData[i0] = reBuffer[i0]; + imData[i0] = imBuffer[i0]; + final double re = reBuffer[i1]; + final double im = imBuffer[i1]; + final double reRoot = reUnitRoot[m0]; + final double imRoot = imUnitRoot[m0]; + reData[i1] = re * reRoot - im * imRoot; + imData[i1] = re * imRoot + im * reRoot; + } + for (int m = 0; (m < K2); m++) { + reData[i0] -= reData[i1]; + imData[i0] -= imData[i1]; + reData[i1] += reBuffer[i0]; + imData[i1] += imBuffer[i0]; + i0 += stride; + i1 += stride; + } +} /* end run */ + +/*.................................................................... + DFTRadix2Double static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static double[] getImUnitRoot ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final double[] imUnitRoot = new double[halfTransformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + imUnitRoot[k] = sin((double)(halfTransformLength + k) * angularStep); + } + return(imUnitRoot); +} /* end getImUnitRoot */ + +/*------------------------------------------------------------------*/ +static double[] getReUnitRoot ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final double[] reUnitRoot = new double[halfTransformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + reUnitRoot[k] = cos((double)(halfTransformLength + k) * angularStep); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +} /* end class DFTRadix2Double */ + +/*==================================================================== +| DFTRadix2Float +\===================================================================*/ +static class DFTRadix2Float + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTRadix2Float */ + +/*.................................................................... + DFTRadix2Float static variables +....................................................................*/ +private float[] imBuffer; +private float[] imUnitRoot; +private float[] reBuffer; +private float[] reUnitRoot; + +/*.................................................................... + DFTRadix2Float constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTRadix2Float ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final float[] reUnitRoot, + final float[] imUnitRoot +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; +} /* end DFTRadix2Float */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int K2 = reUnitRoot.length; +//;IJ.log("DFTRadix2Float " + (2 * K2)); + final int doubleStride = stride << 1; + final FFTSetup fft = FFTSetup.transforms.get(new Integer(K2)); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.ruritanian, fft.chinese, + fft.K1).run(); + new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.ruritanian, fft.chinese, + fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + new DFTLength2Float(reData, imData, + startIndex, doubleStride).run(); + new DFTLength2Float(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH3: { + new DFTLength3Float(reData, imData, + startIndex, doubleStride).run(); + new DFTLength3Float(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH4: { + new DFTLength4Float(reData, imData, + startIndex, doubleStride).run(); + new DFTLength4Float(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH5: { + new DFTLength5Float(reData, imData, + startIndex, doubleStride).run(); + new DFTLength5Float(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH6: { + new DFTLength6Float(reData, imData, + startIndex, doubleStride).run(); + new DFTLength6Float(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH8: { + new DFTLength8Float(reData, imData, + startIndex, doubleStride).run(); + new DFTLength8Float(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat, + fft.K1).run(); + new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat, + fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderFloat(reData, imData, + startIndex, doubleStride, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + new DFTPaddedRaderFloat(reData, imData, + startIndex + stride, doubleStride, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + new DFTRaderFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2Float(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + new DFTRadix2Float(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + int m0 = startIndex; + int i0 = startIndex; + int i1 = startIndex + K2 * stride; + for (int m = 0; (m < K2); m++) { + reBuffer[i0] = reData[m0]; + imBuffer[i0] = imData[m0]; + m0 += stride; + reBuffer[i1] = reData[m0]; + imBuffer[i1] = imData[m0]; + m0 += stride; + i0 += stride; + i1 += stride; + } + m0 = K2; + for (int m = 0; (m < K2); m++) { + m0--; + i1 -= stride; + i0 -= stride; + reData[i0] = reBuffer[i0]; + imData[i0] = imBuffer[i0]; + final float re = reBuffer[i1]; + final float im = imBuffer[i1]; + final float reRoot = reUnitRoot[m0]; + final float imRoot = imUnitRoot[m0]; + reData[i1] = re * reRoot - im * imRoot; + imData[i1] = re * imRoot + im * reRoot; + } + for (int m = 0; (m < K2); m++) { + reData[i0] -= reData[i1]; + imData[i0] -= imData[i1]; + reData[i1] += reBuffer[i0]; + imData[i1] += imBuffer[i0]; + i0 += stride; + i1 += stride; + } +} /* end run */ + +/*.................................................................... + DFTRadix2Float static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static float[] getImUnitRoot ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final float[] imUnitRoot = new float[halfTransformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + imUnitRoot[k] = (float)sin((double)((float)(halfTransformLength + k) + * angularStep)); + } + return(imUnitRoot); +} /* end getImUnitRoot */ + +/*------------------------------------------------------------------*/ +static float[] getReUnitRoot ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final float[] reUnitRoot = new float[halfTransformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + reUnitRoot[k] = (float)cos((double)((float)(halfTransformLength + k) + * angularStep)); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +} /* end class DFTRadix2Float */ + +/*==================================================================== +| DFTRadix2Real +\===================================================================*/ +static class DFTRadix2Real + +{ /* begin class DFTRadix2Real */ + +/*.................................................................... + DFTRadix2Real static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int transformLength +) { + if (FFTSetup.taboos.contains(new Integer(transformLength / 2))) { + return(-1L); + } + final long k2 = (long)(transformLength >> 2); + return(FFTSetup.FLALLOC * 8L + + FFTSetup.FLOP * (1L + k2 * 8L + k2 * 9L + 1L) + + FFTSetup.FLASSIGN * ((k2 + 1L) * 4L + 2L + k2 * 6L + k2 * 6L + 2L) + + FFTSetup.INTALLOC * 9L + + FFTSetup.INTOP * (6L + (k2 + 1L) * 6L + 6L + k2 * 4L + 2L + k2 * 5L) + + FFTSetup.INTASSIGN * (7L + (k2 + 1L) * 5L + 4L + k2 * 3L + 4L + + k2 * 4L) + + FFTSetup.IDX * ((k2 + 1L) * 8L + 4L + k2 * 8L + k2 * 8L + 4L) + + FFTSetup.NEWOBJ * 2L + + FFTSetupDuoReal.cost(transformLength / 2) + ); +} /* end cost */ + +} /* end class DFTRadix2Real */ + +/*==================================================================== +| DFTRadix2RealDouble +\===================================================================*/ +static class DFTRadix2RealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTRadix2RealDouble */ + +/*.................................................................... + DFTRadix2RealDouble static variables +....................................................................*/ +private double[] imBuffer; +private double[] imUnitRootEven; +private double[] imUnitRootOdd; +private double[] reBuffer; +private double[] reUnitRootEven; +private double[] reUnitRootOdd; + +/*.................................................................... + DFTRadix2RealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTRadix2RealDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final double[] reUnitRootEven, + final double[] imUnitRootEven, + final double[] reUnitRootOdd, + final double[] imUnitRootOdd +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRootEven = reUnitRootEven; + this.imUnitRootEven = imUnitRootEven; + this.reUnitRootOdd = reUnitRootOdd; + this.imUnitRootOdd = imUnitRootOdd; +} /* end DFTRadix2RealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int K2 = reUnitRootEven.length; +//;IJ.log("DFTRadix2RealDouble " + (2 * K2)); + final int K4 = K2 >> 1; + final int doubleStride = stride << 1; + final FFTSetupDuoReal fft = + FFTSetupDuoReal.transforms.get(new Integer(K2)); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + new DFTBruteForceRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.ruritanian, fft.chinese, fft.K1).run(); + new DFTCoprimeFactorRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + new DFTDuoRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, startIndex + stride, doubleStride, K2).run(); + break; + } + case EVENREAL: { + new DFTEvenRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + new DFTEvenRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case LENGTH1: { + imData[startIndex] = 0.0; + imData[startIndex + stride] = 0.0; + break; + } + case LENGTH2: { + new DFTLength2RealDouble(reData, imData, + startIndex, doubleStride).run(); + new DFTLength2RealDouble(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH3: { + new DFTLength3RealDouble(reData, imData, + startIndex, doubleStride).run(); + new DFTLength3RealDouble(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH4: { + new DFTLength4RealDouble(reData, imData, + startIndex, doubleStride).run(); + new DFTLength4RealDouble(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH5: { + new DFTLength5RealDouble(reData, imData, + startIndex, doubleStride).run(); + new DFTLength5RealDouble(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH6: { + new DFTLength6RealDouble(reData, imData, + startIndex, doubleStride).run(); + new DFTLength6RealDouble(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH8: { + new DFTLength8RealDouble(reData, imData, + startIndex, doubleStride).run(); + new DFTLength8RealDouble(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble, + fft.K1).run(); + new DFTMixedRadixRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble, + fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealDouble(reData, imData, + startIndex, doubleStride, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + new DFTPaddedRaderRealDouble(reData, imData, + startIndex + stride, doubleStride, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + new DFTRaderRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble, + fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run(); + new DFTRadix2RealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble, + fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + new DFTSplitRadixRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + int m0 = startIndex; + int i0 = startIndex; + int i1 = startIndex + K2 * stride; + for (int m = 0; (m <= K4); m++) { + reBuffer[i0] = reData[m0]; + imBuffer[i0] = imData[m0]; + m0 += stride; + reBuffer[i1] = reData[m0]; + imBuffer[i1] = imData[m0]; + m0 += stride; + i0 += stride; + i1 += stride; + } + i1 = startIndex + K2 * stride; + reData[startIndex] = reBuffer[startIndex] + reBuffer[i1]; + imData[startIndex] = 0.0; + i0 = startIndex + stride; + i1 += stride; + if (0 == (K2 & 1)) { + for (int m = 1; (m < K4); m++) { + final double re = reBuffer[i1]; + final double im = imBuffer[i1]; + final double reRoot = reUnitRootEven[m]; + final double imRoot = imUnitRootEven[m]; + reData[i0] = reBuffer[i0] + re * reRoot - im * imRoot; + imData[i0] = imBuffer[i0] + re * imRoot + im * reRoot; + i0 += stride; + i1 += stride; + } + reData[i0] = reBuffer[i0]; + imData[i0] = -reBuffer[i1]; + m0 = i0 + stride; + i0 -= stride; + i1 -= stride; + for (int m = 1; (m < K4); m++) { + final double re = imBuffer[i1]; + final double im = reBuffer[i1]; + final double reRoot = reUnitRootEven[m]; + final double imRoot = imUnitRootEven[m]; + reData[m0] = reBuffer[i0] - re * reRoot + im * imRoot; + imData[m0] = -imBuffer[i0] - re * imRoot - im * reRoot; + m0 += stride; + i0 -= stride; + i1 -= stride; + } + } + else { + for (int m = 1; (m <= K4); m++) { + final double re = reBuffer[i1]; + final double im = imBuffer[i1]; + final double reRoot = reUnitRootEven[m]; + final double imRoot = imUnitRootEven[m]; + reData[i0] = reBuffer[i0] + re * reRoot - im * imRoot; + imData[i0] = imBuffer[i0] + re * imRoot + im * reRoot; + i0 += stride; + i1 += stride; + } + m0 = i0; + i0 -= stride; + i1 -= stride; + for (int m = 0; (m < K4); m++) { + final double re = imBuffer[i1]; + final double im = reBuffer[i1]; + final double reRoot = reUnitRootOdd[m]; + final double imRoot = imUnitRootOdd[m]; + reData[m0] = reBuffer[i0] - re * reRoot + im * imRoot; + imData[m0] = -imBuffer[i0] - re * imRoot - im * reRoot; + i0 -= stride; + i1 -= stride; + m0 += stride; + } + } + reData[m0] = reBuffer[i0] - reBuffer[i1]; + imData[m0] = 0.0; +} /* end run */ + +/*.................................................................... + DFTRadix2RealDouble static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static double[] getImUnitRootEven ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final double[] imUnitRoot = new double[halfTransformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + imUnitRoot[k] = sin((double)k * angularStep); + } + return(imUnitRoot); +} /* end getImUnitRootEven */ + +/*------------------------------------------------------------------*/ +static double[] getReUnitRootEven ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final double[] reUnitRoot = new double[halfTransformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + reUnitRoot[k] = cos((double)k * angularStep); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +/*------------------------------------------------------------------*/ +static double[] getImUnitRootOdd ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final double[] imUnitRoot = new double[halfTransformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + imUnitRoot[k] = sin(((double)k + 0.5) * angularStep); + } + return(imUnitRoot); +} /* end getImUnitRootOdd */ + +/*------------------------------------------------------------------*/ +static double[] getReUnitRootOdd ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final double[] reUnitRoot = new double[halfTransformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + reUnitRoot[k] = cos(((double)k + 0.5) * angularStep); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +} /* end class getReUnitRootOdd */ + +/*==================================================================== +| DFTRadix2RealFloat +\===================================================================*/ +static class DFTRadix2RealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTRadix2RealFloat */ + +/*.................................................................... + DFTRadix2RealFloat static variables +....................................................................*/ +private float[] imBuffer; +private float[] imUnitRootEven; +private float[] imUnitRootOdd; +private float[] reBuffer; +private float[] reUnitRootEven; +private float[] reUnitRootOdd; + +/*.................................................................... + DFTRadix2RealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTRadix2RealFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final float[] reUnitRootEven, + final float[] imUnitRootEven, + final float[] reUnitRootOdd, + final float[] imUnitRootOdd +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRootEven = reUnitRootEven; + this.imUnitRootEven = imUnitRootEven; + this.reUnitRootOdd = reUnitRootOdd; + this.imUnitRootOdd = imUnitRootOdd; +} /* end DFTRadix2RealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int K2 = reUnitRootEven.length; +//;IJ.log("DFTRadix2RealFloat " + (2 * K2)); + final int K4 = K2 >> 1; + final int doubleStride = stride << 1; + final FFTSetupDuoReal fft = + FFTSetupDuoReal.transforms.get(new Integer(K2)); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + new DFTBruteForceRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.ruritanian, fft.chinese, fft.K1).run(); + new DFTCoprimeFactorRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + new DFTDuoRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, startIndex + stride, doubleStride, K2).run(); + break; + } + case EVENREAL: { + new DFTEvenRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + new DFTEvenRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case LENGTH1: { + imData[startIndex] = 0.0F; + imData[startIndex + stride] = 0.0F; + break; + } + case LENGTH2: { + new DFTLength2RealFloat(reData, imData, + startIndex, doubleStride).run(); + new DFTLength2RealFloat(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH3: { + new DFTLength3RealFloat(reData, imData, + startIndex, doubleStride).run(); + new DFTLength3RealFloat(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH4: { + new DFTLength4RealFloat(reData, imData, + startIndex, doubleStride).run(); + new DFTLength4RealFloat(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH5: { + new DFTLength5RealFloat(reData, imData, + startIndex, doubleStride).run(); + new DFTLength5RealFloat(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH6: { + new DFTLength6RealFloat(reData, imData, + startIndex, doubleStride).run(); + new DFTLength6RealFloat(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case LENGTH8: { + new DFTLength8RealFloat(reData, imData, + startIndex, doubleStride).run(); + new DFTLength8RealFloat(reData, imData, + startIndex + stride, doubleStride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat, + fft.K1).run(); + new DFTMixedRadixRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat, + fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealFloat(reData, imData, + startIndex, doubleStride, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + new DFTPaddedRaderRealFloat(reData, imData, + startIndex + stride, doubleStride, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + new DFTRaderRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat, + fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run(); + new DFTRadix2RealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat, + fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + new DFTSplitRadixRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, doubleStride, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + int m0 = startIndex; + int i0 = startIndex; + int i1 = startIndex + K2 * stride; + for (int m = 0; (m <= K4); m++) { + reBuffer[i0] = reData[m0]; + imBuffer[i0] = imData[m0]; + m0 += stride; + reBuffer[i1] = reData[m0]; + imBuffer[i1] = imData[m0]; + m0 += stride; + i0 += stride; + i1 += stride; + } + i1 = startIndex + K2 * stride; + reData[startIndex] = reBuffer[startIndex] + reBuffer[i1]; + imData[startIndex] = 0.0F; + i0 = startIndex + stride; + i1 += stride; + if (0 == (K2 & 1)) { + for (int m = 1; (m < K4); m++) { + final float re = reBuffer[i1]; + final float im = imBuffer[i1]; + final float reRoot = reUnitRootEven[m]; + final float imRoot = imUnitRootEven[m]; + reData[i0] = reBuffer[i0] + re * reRoot - im * imRoot; + imData[i0] = imBuffer[i0] + re * imRoot + im * reRoot; + i0 += stride; + i1 += stride; + } + reData[i0] = reBuffer[i0]; + imData[i0] = -reBuffer[i1]; + m0 = i0 + stride; + i0 -= stride; + i1 -= stride; + for (int m = 1; (m < K4); m++) { + final float re = imBuffer[i1]; + final float im = reBuffer[i1]; + final float reRoot = reUnitRootEven[m]; + final float imRoot = imUnitRootEven[m]; + reData[m0] = reBuffer[i0] - re * reRoot + im * imRoot; + imData[m0] = -imBuffer[i0] - re * imRoot - im * reRoot; + m0 += stride; + i0 -= stride; + i1 -= stride; + } + } + else { + for (int m = 1; (m <= K4); m++) { + final float re = reBuffer[i1]; + final float im = imBuffer[i1]; + final float reRoot = reUnitRootEven[m]; + final float imRoot = imUnitRootEven[m]; + reData[i0] = reBuffer[i0] + re * reRoot - im * imRoot; + imData[i0] = imBuffer[i0] + re * imRoot + im * reRoot; + i0 += stride; + i1 += stride; + } + m0 = i0; + i0 -= stride; + i1 -= stride; + for (int m = 0; (m < K4); m++) { + final float re = imBuffer[i1]; + final float im = reBuffer[i1]; + final float reRoot = reUnitRootOdd[m]; + final float imRoot = imUnitRootOdd[m]; + reData[m0] = reBuffer[i0] - re * reRoot + im * imRoot; + imData[m0] = -imBuffer[i0] - re * imRoot - im * reRoot; + i0 -= stride; + i1 -= stride; + m0 += stride; + } + } + reData[m0] = reBuffer[i0] - reBuffer[i1]; + imData[m0] = 0.0F; +} /* end run */ + +/*.................................................................... + DFTRadix2RealFloat static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static float[] getImUnitRootEven ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final float[] imUnitRoot = new float[halfTransformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + imUnitRoot[k] = (float)sin((double)((float)k * angularStep)); + } + return(imUnitRoot); +} /* end getImUnitRootEven */ + +/*------------------------------------------------------------------*/ +static float[] getReUnitRootEven ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final float[] reUnitRoot = new float[halfTransformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + reUnitRoot[k] = (float)cos((double)((float)k * angularStep)); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +/*------------------------------------------------------------------*/ +static float[] getImUnitRootOdd ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final float[] imUnitRoot = new float[halfTransformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + imUnitRoot[k] = (float)sin((double)(((float)k + 0.5F) + * angularStep)); + } + return(imUnitRoot); +} /* end getImUnitRootOdd */ + +/*------------------------------------------------------------------*/ +static float[] getReUnitRootOdd ( + final int transformLength +) { + final int halfTransformLength = transformLength >> 1; + final float[] reUnitRoot = new float[halfTransformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < halfTransformLength); k++) { + reUnitRoot[k] = (float)cos((double)(((float)k + 0.5F) + * angularStep)); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +} /* end class getReUnitRootOdd */ + +/*==================================================================== +| DFTSplitRadix +\===================================================================*/ +static class DFTSplitRadix + +{ /* begin class DFTSplitRadix */ + +/*.................................................................... + DFTSplitRadix static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int transformLength +) { + if (FFTSetup.taboos.contains(new Integer(transformLength / 2)) + || FFTSetup.taboos.contains(new Integer(transformLength / 4))) { + return(-1L); + } + final long k = (long)(transformLength >> 1); + final long k2 = k >> 1L; + return(FFTSetup.FLALLOC * 12L + + FFTSetup.FLOP * (k2 * 24L) + + FFTSetup.FLASSIGN * (k * 2L + k2 * 2L + k2 * 2L + k2 * 28L) + + FFTSetup.INTALLOC * 15L + + FFTSetup.INTOP * (9L + k * 4L + 1L + k2 * 4L + 1L + k2 * 4L + 6L + + k2 * 7L) + + FFTSetup.INTASSIGN * (8L + k * 3L + 2L + k2 * 3L + 2L + k2 * 3L + 6L + + k2 * 6L) + + FFTSetup.IDX * (k * 4L + k2 * 4L + k2 * 4L + k2 * 20L) + + FFTSetup.NEWOBJ * 3L + + FFTSetup.cost(transformLength >> 1) + + 2L * FFTSetup.cost(transformLength >> 2) + ); +} /* end cost */ + +} /* end class DFTSplitRadix */ + +/*==================================================================== +| DFTSplitRadixDouble +\===================================================================*/ +static class DFTSplitRadixDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTSplitRadixDouble */ + +/*.................................................................... + DFTSplitRadixDouble static variables +....................................................................*/ +private double[] imBuffer; +private double[] imUnitRoot; +private double[] reBuffer; +private double[] reUnitRoot; + +/*.................................................................... + DFTSplitRadixDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTSplitRadixDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final double[] reUnitRoot, + final double[] imUnitRoot +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; +} /* end DFTSplitRadixDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int K4 = reUnitRoot.length / 3; +//;IJ.log("DFTSplitRadixDouble " + (4 * K4)); + final int K2 = K4 << 1; + final int doubleStride = stride << 1; + final int tripleStride = doubleStride + stride; + final int quadrupleStride = tripleStride + stride; + final FFTSetup fft2 = FFTSetup.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.ruritanian, fft2.chinese, fft2.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2Double(reData, imData, + startIndex, doubleStride).run(); + break; + } + case LENGTH3: { + throw(new IllegalStateException()); + } + case LENGTH4: { + new DFTLength4Double(reData, imData, + startIndex, doubleStride).run(); + break; + } + case LENGTH5: { + throw(new IllegalStateException()); + } + case LENGTH6: { + new DFTLength6Double(reData, imData, + startIndex, doubleStride).run(); + break; + } + case LENGTH8: { + new DFTLength8Double(reData, imData, + startIndex, doubleStride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, + fft2.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderDouble(reData, imData, + startIndex, doubleStride, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2Double(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + break; + } + } + final FFTSetup fft4 = FFTSetup.transforms.get(new Integer(K4)); + switch (fft4.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble).run(); + new DFTBruteForceDouble(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.ruritanian, fft4.chinese, + fft4.K1).run(); + new DFTCoprimeFactorDouble(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.ruritanian, fft4.chinese, + fft4.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + new DFTLength2Double(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength2Double(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH3: { + new DFTLength3Double(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength3Double(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH4: { + new DFTLength4Double(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength4Double(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH5: { + new DFTLength5Double(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength5Double(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH6: { + new DFTLength6Double(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength6Double(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH8: { + new DFTLength8Double(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength8Double(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble, + fft4.K1).run(); + new DFTMixedRadixDouble(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble, + fft4.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderDouble(reData, imData, + startIndex + stride, quadrupleStride, + fft4.reConvolverDouble, fft4.imConvolverDouble, + fft4.modular, fft4.inverseModular).run(); + new DFTPaddedRaderDouble(reData, imData, + startIndex + tripleStride, quadrupleStride, + fft4.reConvolverDouble, fft4.imConvolverDouble, + fft4.modular, fft4.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reConvolverDouble, fft4.imConvolverDouble, + fft4.modular, fft4.inverseModular).run(); + new DFTRaderDouble(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reConvolverDouble, fft4.imConvolverDouble, + fft4.modular, fft4.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2Double(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble).run(); + new DFTRadix2Double(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble).run(); + new DFTSplitRadixDouble(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble).run(); + break; + } + } + int m0 = startIndex; + int i0 = startIndex; + for (int m = 0; (m < K2); m++) { + reBuffer[i0] = reData[m0]; + imBuffer[i0] = imData[m0]; + m0 += doubleStride; + i0 += stride; + } + m0 = startIndex + stride; + for (int m = 0; (m < K4); m++) { + reBuffer[i0] = reData[m0]; + imBuffer[i0] = imData[m0]; + m0 += quadrupleStride; + i0 += stride; + } + m0 = startIndex + tripleStride; + for (int m = 0; (m < K4); m++) { + reBuffer[i0] = reData[m0]; + imBuffer[i0] = imData[m0]; + m0 += quadrupleStride; + i0 += stride; + } + i0 = startIndex; + int i1 = i0 + K4 * stride; + int i2 = i1 + K4 * stride; + int i3 = i2 + K4 * stride; + for (int m1 = 0, m3 = 0; (m1 < K4); m1++, m3 += 3) { + double re = reBuffer[i2]; + double im = imBuffer[i2]; + double reRoot = reUnitRoot[m1]; + double imRoot = imUnitRoot[m1]; + double reButterfly = re * reRoot - im * imRoot; + double imButterfly = re * imRoot + im * reRoot; + re = reBuffer[i3]; + im = imBuffer[i3]; + reRoot = reUnitRoot[m3]; + imRoot = imUnitRoot[m3]; + double reDragonfly = re * reRoot - im * imRoot; + double imDragonfly = re * imRoot + im * reRoot; + final double reLadybug = reButterfly + reDragonfly; + final double imLadybug = imButterfly + imDragonfly; + final double reMoth = reButterfly - reDragonfly; + final double imMoth = imButterfly - imDragonfly; + reButterfly = reBuffer[i0]; + imButterfly = imBuffer[i0]; + reDragonfly = reBuffer[i1]; + imDragonfly = imBuffer[i1]; + reData[i0] = reButterfly + reLadybug; + imData[i0] = imButterfly + imLadybug; + reData[i1] = reDragonfly + imMoth; + imData[i1] = imDragonfly - reMoth; + reData[i2] = reButterfly - reLadybug; + imData[i2] = imButterfly - imLadybug; + reData[i3] = reDragonfly - imMoth; + imData[i3] = imDragonfly + reMoth; + i0 += stride; + i1 += stride; + i2 += stride; + i3 += stride; + } +} /* end run */ + +/*.................................................................... + DFTSplitRadixDouble static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static double[] getImUnitRoot ( + final int transformLength +) { + final int fourthTransformLength = transformLength >> 2; + final int threeFourthTransformLength = 3 * fourthTransformLength; + final double[] imUnitRoot = new double[threeFourthTransformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < threeFourthTransformLength); k++) { + imUnitRoot[k] = sin((double)k * angularStep); + } + return(imUnitRoot); +} /* end getImUnitRoot */ + +/*------------------------------------------------------------------*/ +static double[] getReUnitRoot ( + final int transformLength +) { + final int fourthTransformLength = transformLength >> 2; + final int threeFourthTransformLength = 3 * fourthTransformLength; + final double[] reUnitRoot = new double[threeFourthTransformLength]; + final double angularStep = -2.0 * PI / (double)transformLength; + for (int k = 0; (k < threeFourthTransformLength); k++) { + reUnitRoot[k] = cos((double)k * angularStep); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +} /* end class DFTSplitRadixDouble */ + +/*==================================================================== +| DFTSplitRadixFloat +\===================================================================*/ +static class DFTSplitRadixFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTSplitRadixFloat */ + +/*.................................................................... + DFTSplitRadixFloat static variables +....................................................................*/ +private float[] imBuffer; +private float[] imUnitRoot; +private float[] reBuffer; +private float[] reUnitRoot; + +/*.................................................................... + DFTSplitRadixFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTSplitRadixFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final float[] reUnitRoot, + final float[] imUnitRoot +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; +} /* end DFTSplitRadixFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int K4 = reUnitRoot.length / 3; +//;IJ.log("DFTSplitRadixFloat " + (4 * K4)); + final int K2 = K4 << 1; + final int doubleStride = stride << 1; + final int tripleStride = doubleStride + stride; + final int quadrupleStride = tripleStride + stride; + final FFTSetup fft2 = FFTSetup.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.ruritanian, fft2.chinese, + fft2.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2Float(reData, imData, + startIndex, doubleStride).run(); + break; + } + case LENGTH3: { + throw(new IllegalStateException()); + } + case LENGTH4: { + new DFTLength4Float(reData, imData, + startIndex, doubleStride).run(); + break; + } + case LENGTH5: { + throw(new IllegalStateException()); + } + case LENGTH6: { + new DFTLength6Float(reData, imData, + startIndex, doubleStride).run(); + break; + } + case LENGTH8: { + new DFTLength8Float(reData, imData, + startIndex, doubleStride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, + fft2.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderFloat(reData, imData, + startIndex, doubleStride, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2Float(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + break; + } + } + final FFTSetup fft4 = FFTSetup.transforms.get(new Integer(K4)); + switch (fft4.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat).run(); + new DFTBruteForceFloat(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.ruritanian, fft4.chinese, + fft4.K1).run(); + new DFTCoprimeFactorFloat(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.ruritanian, fft4.chinese, + fft4.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + new DFTLength2Float(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength2Float(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH3: { + new DFTLength3Float(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength3Float(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH4: { + new DFTLength4Float(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength4Float(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH5: { + new DFTLength5Float(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength5Float(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH6: { + new DFTLength6Float(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength6Float(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH8: { + new DFTLength8Float(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength8Float(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat, + fft4.K1).run(); + new DFTMixedRadixFloat(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat, + fft4.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderFloat(reData, imData, + startIndex + stride, quadrupleStride, + fft4.reConvolverFloat, fft4.imConvolverFloat, + fft4.modular, fft4.inverseModular).run(); + new DFTPaddedRaderFloat(reData, imData, + startIndex + tripleStride, quadrupleStride, + fft4.reConvolverFloat, fft4.imConvolverFloat, + fft4.modular, fft4.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reConvolverFloat, fft4.imConvolverFloat, + fft4.modular, fft4.inverseModular).run(); + new DFTRaderFloat(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reConvolverFloat, fft4.imConvolverFloat, + fft4.modular, fft4.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2Float(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat).run(); + new DFTRadix2Float(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat).run(); + new DFTSplitRadixFloat(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat).run(); + break; + } + } + int m0 = startIndex; + int i0 = startIndex; + for (int m = 0; (m < K2); m++) { + reBuffer[i0] = reData[m0]; + imBuffer[i0] = imData[m0]; + m0 += doubleStride; + i0 += stride; + } + m0 = startIndex + stride; + for (int m = 0; (m < K4); m++) { + reBuffer[i0] = reData[m0]; + imBuffer[i0] = imData[m0]; + m0 += quadrupleStride; + i0 += stride; + } + m0 = startIndex + tripleStride; + for (int m = 0; (m < K4); m++) { + reBuffer[i0] = reData[m0]; + imBuffer[i0] = imData[m0]; + m0 += quadrupleStride; + i0 += stride; + } + i0 = startIndex; + int i1 = i0 + K4 * stride; + int i2 = i1 + K4 * stride; + int i3 = i2 + K4 * stride; + for (int m1 = 0, m3 = 0; (m1 < K4); m1++, m3 += 3) { + float re = reBuffer[i2]; + float im = imBuffer[i2]; + float reRoot = reUnitRoot[m1]; + float imRoot = imUnitRoot[m1]; + float reButterfly = re * reRoot - im * imRoot; + float imButterfly = re * imRoot + im * reRoot; + re = reBuffer[i3]; + im = imBuffer[i3]; + reRoot = reUnitRoot[m3]; + imRoot = imUnitRoot[m3]; + float reDragonfly = re * reRoot - im * imRoot; + float imDragonfly = re * imRoot + im * reRoot; + final float reLadybug = reButterfly + reDragonfly; + final float imLadybug = imButterfly + imDragonfly; + final float reMoth = reButterfly - reDragonfly; + final float imMoth = imButterfly - imDragonfly; + reButterfly = reBuffer[i0]; + imButterfly = imBuffer[i0]; + reDragonfly = reBuffer[i1]; + imDragonfly = imBuffer[i1]; + reData[i0] = reButterfly + reLadybug; + imData[i0] = imButterfly + imLadybug; + reData[i1] = reDragonfly + imMoth; + imData[i1] = imDragonfly - reMoth; + reData[i2] = reButterfly - reLadybug; + imData[i2] = imButterfly - imLadybug; + reData[i3] = reDragonfly - imMoth; + imData[i3] = imDragonfly + reMoth; + i0 += stride; + i1 += stride; + i2 += stride; + i3 += stride; + } +} /* end run */ + +/*.................................................................... + DFTSplitRadixFloat static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static float[] getImUnitRoot ( + final int transformLength +) { + final int fourthTransformLength = transformLength >> 2; + final int threeFourthTransformLength = 3 * fourthTransformLength; + final float[] imUnitRoot = new float[threeFourthTransformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < threeFourthTransformLength); k++) { + imUnitRoot[k] = (float)sin((double)((float)k * angularStep)); + } + return(imUnitRoot); +} /* end getImUnitRoot */ + +/*------------------------------------------------------------------*/ +static float[] getReUnitRoot ( + final int transformLength +) { + final int fourthTransformLength = transformLength >> 2; + final int threeFourthTransformLength = 3 * fourthTransformLength; + final float[] reUnitRoot = new float[threeFourthTransformLength]; + final float angularStep = -2.0F * (float)PI / (float)transformLength; + for (int k = 0; (k < threeFourthTransformLength); k++) { + reUnitRoot[k] = (float)cos((double)((float)k * angularStep)); + } + return(reUnitRoot); +} /* end getReUnitRoot */ + +} /* end class DFTSplitRadixFloat */ + +/*==================================================================== +| DFTSplitRadixReal +\===================================================================*/ +static class DFTSplitRadixReal + +{ /* begin class DFTSplitRadixReal */ + +/*.................................................................... + DFTSplitRadixReal static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int transformLength +) { + if (FFTSetup.taboos.contains(new Integer(transformLength / 2)) + || FFTSetup.taboos.contains(new Integer(transformLength / 4))) { + return(-1L); + } + final long k = (long)(transformLength >> 1); + final long k2 = k >> 1L; + final long k4 = k2 >> 1L; + return(FFTSetup.FLALLOC * 8L + + FFTSetup.FLOP * ((k4 + 1L) * 16L + 1L + k4 * 2L + (k2 - k4) * 2L + 1L + + k4 * 3L + (k2 - k4) * 2L + 1L) + + FFTSetup.FLASSIGN * ((k2 + 1L) * 2L + (k4 + 1L) * 16L + 2L + k4 * 2L + + (k2 - k4) * 2L + 2L + k4 * 2L + (k2 - k4) * 2L + 2L) + + FFTSetup.INTALLOC * 18L + + FFTSetup.INTOP * (12L + (k2 + 1L) * 4L + 6L + (k4 + 1L) * 7L + 4L + + k4 * 4L + 4L + (k2 - k4) * 4L + 5L + k4 * 5L + 4L + + (k2 - k4) * 5L) + + FFTSetup.INTASSIGN * (10L + (k2 + 1L) * 3L + 6L + (k4 + 1L) * 6L + 5L + + k4 * 3L + 2L + (k2 - k4) * 3L + 4L + k4 * 4L + 2L + + (k2 - k4) * 4L) + + FFTSetup.IDX * ((k2 + 1L) * 4L + (k4 + 1L) * 12L + 4L + k4 * 6L + + (k2 - k4) * 6L + 4L + k4 * 6L + (k2 - k4) * 6L + 4L) + + FFTSetup.NEWOBJ * 3L + + FFTSetupReal.cost(transformLength >> 1) + + FFTSetupDuoReal.cost(transformLength >> 2) + ); +} /* end cost */ + +} /* end class DFTSplitRadixReal */ + +/*==================================================================== +| DFTSplitRadixRealDouble +\===================================================================*/ +static class DFTSplitRadixRealDouble + extends + DFTDouble + implements + Runnable + +{ /* begin class DFTSplitRadixRealDouble */ + +/*.................................................................... + DFTSplitRadixRealDouble static variables +....................................................................*/ +private double[] imBuffer; +private double[] imUnitRoot; +private double[] reBuffer; +private double[] reUnitRoot; + +/*.................................................................... + DFTSplitRadixRealDouble constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTSplitRadixRealDouble ( + final double[] reData, + final double[] imData, + final double[] reBuffer, + final double[] imBuffer, + final int startIndex, + final int stride, + final double[] reUnitRoot, + final double[] imUnitRoot +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; +} /* end DFTSplitRadixRealDouble */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int K4 = reUnitRoot.length / 3; +//;IJ.log("DFTSplitRadixRealDouble " + (4 * K4)); + final int halfK4 = (K4 >> 1) + 1; + final int K2 = K4 << 1; + final int halfK2 = K4 + 1; + final int doubleStride = stride << 1; + final int tripleStride = doubleStride + stride; + final int quadrupleStride = tripleStride + stride; + final FFTSetupReal fft2 = FFTSetupReal.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.ruritanian, fft2.chinese, fft2.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + break; + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2RealDouble(reData, imData, + startIndex, doubleStride).run(); + break; + } + case LENGTH3: { + throw(new IllegalStateException()); + } + case LENGTH4: { + new DFTLength4RealDouble(reData, imData, + startIndex, doubleStride).run(); + break; + } + case LENGTH5: { + throw(new IllegalStateException()); + } + case LENGTH6: { + new DFTLength6RealDouble(reData, imData, + startIndex, doubleStride).run(); + break; + } + case LENGTH8: { + new DFTLength8RealDouble(reData, imData, + startIndex, doubleStride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, + fft2.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealDouble(reData, imData, + startIndex, doubleStride, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootEvenDouble, fft2.imUnitRootEvenDouble, + fft2.reUnitRootOddDouble, fft2.imUnitRootOddDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealDouble(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootDouble, fft2.imUnitRootDouble).run(); + break; + } + } + final FFTSetupDuoReal fft4 = + FFTSetupDuoReal.transforms.get(new Integer(K4)); + switch (fft4.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble).run(); + new DFTBruteForceRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.ruritanian, fft4.chinese, + fft4.K1).run(); + new DFTCoprimeFactorRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.ruritanian, fft4.chinese, + fft4.K1).run(); + break; + } + case DUOREAL: { + new DFTDuoRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, startIndex + tripleStride, + quadrupleStride, K4).run(); + break; + } + case EVENREAL: { + new DFTEvenRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble).run(); + new DFTEvenRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble).run(); + break; + } + case LENGTH1: { + imData[startIndex + stride] = 0.0; + imData[startIndex + tripleStride] = 0.0; + break; + } + case LENGTH2: { + new DFTLength2RealDouble(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength2RealDouble(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH3: { + new DFTLength3RealDouble(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength3RealDouble(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH4: { + new DFTLength4RealDouble(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength4RealDouble(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH5: { + new DFTLength5RealDouble(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength5RealDouble(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH6: { + new DFTLength6RealDouble(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength6RealDouble(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH8: { + new DFTLength8RealDouble(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength8RealDouble(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble, + fft4.K1).run(); + new DFTMixedRadixRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble, + fft4.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealDouble(reData, imData, + startIndex + stride, quadrupleStride, + fft4.reConvolverDouble, fft4.imConvolverDouble, + fft4.modular, fft4.inverseModular).run(); + new DFTPaddedRaderRealDouble(reData, imData, + startIndex + tripleStride, quadrupleStride, + fft4.reConvolverDouble, fft4.imConvolverDouble, + fft4.modular, fft4.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reConvolverDouble, fft4.imConvolverDouble, + fft4.modular, fft4.inverseModular).run(); + new DFTRaderRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reConvolverDouble, fft4.imConvolverDouble, + fft4.modular, fft4.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootEvenDouble, fft4.imUnitRootEvenDouble, + fft4.reUnitRootOddDouble, fft4.imUnitRootOddDouble).run(); + new DFTRadix2RealDouble(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootEvenDouble, fft4.imUnitRootEvenDouble, + fft4.reUnitRootOddDouble, fft4.imUnitRootOddDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble).run(); + new DFTSplitRadixRealDouble(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootDouble, fft4.imUnitRootDouble).run(); + break; + } + } + int p = startIndex; + int q = startIndex; + for (int m = 0; (m < halfK2); m++) { + reBuffer[p] = reData[q]; + imBuffer[p] = imData[q]; + q += doubleStride; + p += stride; + } + p = startIndex + K2 * stride; + q = p + K4 * stride; + int r = startIndex + stride; + int s = r + doubleStride; + for (int m = 0, n = 0; (m < halfK4); m++, n += 3) { + double re = reData[r]; + double im = imData[r]; + double reRoot = reUnitRoot[m]; + double imRoot = imUnitRoot[m]; + double reButterfly = re * reRoot - im * imRoot; + double imButterfly = re * imRoot + im * reRoot; + re = reData[s]; + im = imData[s]; + reRoot = reUnitRoot[n]; + imRoot = imUnitRoot[n]; + final double reDragonfly = re * reRoot - im * imRoot; + final double imDragonfly = re * imRoot + im * reRoot; + reBuffer[p] = reButterfly + reDragonfly; + imBuffer[p] = imButterfly + imDragonfly; + reBuffer[q] = reButterfly - reDragonfly; + imBuffer[q] = imButterfly - imDragonfly; + r += quadrupleStride; + s += quadrupleStride; + p += stride; + q += stride; + }; + p = startIndex; + q = startIndex + K2 * stride; + reData[p] = reBuffer[p] + reBuffer[q]; + imData[p] = 0.0; + p += stride; + q += stride; + for (int n = 1; (n < halfK4); n++) { + reData[p] = reBuffer[p] + reBuffer[q]; + imData[p] = imBuffer[p] + imBuffer[q]; + p += stride; + q += stride; + } + q = startIndex + (2 * K2 - halfK4) * stride; + for (int n = halfK4; (n < K4); n++) { + reData[p] = reBuffer[p] - imBuffer[q]; + imData[p] = imBuffer[p] - reBuffer[q]; + p += stride; + q -= stride; + } + reData[p] = reBuffer[p]; + imData[p] = -reBuffer[q]; + p += stride; + q += stride; + r = startIndex + (K4 - 1) * stride; + for (int n = 1; (n < halfK4); n++) { + reData[p] = reBuffer[r] + imBuffer[q]; + imData[p] = -imBuffer[r] - reBuffer[q]; + p += stride; + q += stride; + r -= stride; + } + q = startIndex + (K2 + K4 - halfK4) * stride; + for (int n = halfK4; (n < K4); n++) { + reData[p] = reBuffer[r] - reBuffer[q]; + imData[p] = imBuffer[q] - imBuffer[r]; + p += stride; + q -= stride; + r -= stride; + } + reData[p] = reBuffer[startIndex] - reBuffer[q]; + imData[p] = 0.0; +} /* end run */ + +} /* end class DFTSplitRadixRealDouble */ + +/*==================================================================== +| DFTSplitRadixRealFloat +\===================================================================*/ +static class DFTSplitRadixRealFloat + extends + DFTFloat + implements + Runnable + +{ /* begin class DFTSplitRadixRealFloat */ + +/*.................................................................... + DFTSplitRadixRealFloat static variables +....................................................................*/ +private float[] imBuffer; +private float[] imUnitRoot; +private float[] reBuffer; +private float[] reUnitRoot; + +/*.................................................................... + DFTSplitRadixRealFloat constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +DFTSplitRadixRealFloat ( + final float[] reData, + final float[] imData, + final float[] reBuffer, + final float[] imBuffer, + final int startIndex, + final int stride, + final float[] reUnitRoot, + final float[] imUnitRoot +) { + super(reData, imData, startIndex, stride); + this.reBuffer = reBuffer; + this.imBuffer = imBuffer; + this.reUnitRoot = reUnitRoot; + this.imUnitRoot = imUnitRoot; +} /* end DFTSplitRadixRealFloat */ + +/*.................................................................... + Runnable methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public void run ( +) { + final int K4 = reUnitRoot.length / 3; +//;IJ.log("DFTSplitRadixRealFloat " + (4 * K4)); + final int halfK4 = (K4 >> 1) + 1; + final int K2 = K4 << 1; + final int halfK2 = K4 + 1; + final int doubleStride = stride << 1; + final int tripleStride = doubleStride + stride; + final int quadrupleStride = tripleStride + stride; + final FFTSetupReal fft2 = FFTSetupReal.transforms.get(new Integer(K2)); + switch (fft2.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.ruritanian, fft2.chinese, fft2.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + break; + } + case LENGTH1: { + throw(new IllegalStateException()); + } + case LENGTH2: { + new DFTLength2RealFloat(reData, imData, + startIndex, doubleStride).run(); + break; + } + case LENGTH3: { + throw(new IllegalStateException()); + } + case LENGTH4: { + new DFTLength4RealFloat(reData, imData, + startIndex, doubleStride).run(); + break; + } + case LENGTH5: { + throw(new IllegalStateException()); + } + case LENGTH6: { + new DFTLength6RealFloat(reData, imData, + startIndex, doubleStride).run(); + break; + } + case LENGTH8: { + new DFTLength8RealFloat(reData, imData, + startIndex, doubleStride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, + fft2.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealFloat(reData, imData, + startIndex, doubleStride, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootEvenFloat, fft2.imUnitRootEvenFloat, + fft2.reUnitRootOddFloat, fft2.imUnitRootOddFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealFloat(reData, imData, reBuffer, imBuffer, + startIndex, doubleStride, + fft2.reUnitRootFloat, fft2.imUnitRootFloat).run(); + break; + } + } + final FFTSetupDuoReal fft4 = + FFTSetupDuoReal.transforms.get(new Integer(K4)); + switch (fft4.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat).run(); + new DFTBruteForceRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.ruritanian, fft4.chinese, + fft4.K1).run(); + new DFTCoprimeFactorRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.ruritanian, fft4.chinese, + fft4.K1).run(); + break; + } + case DUOREAL: { + new DFTDuoRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, startIndex + tripleStride, + quadrupleStride, K4).run(); + break; + } + case EVENREAL: { + new DFTEvenRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat).run(); + new DFTEvenRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat).run(); + break; + } + case LENGTH1: { + imData[startIndex + stride] = 0.0F; + imData[startIndex + tripleStride] = 0.0F; + break; + } + case LENGTH2: { + new DFTLength2RealFloat(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength2RealFloat(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH3: { + new DFTLength3RealFloat(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength3RealFloat(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH4: { + new DFTLength4RealFloat(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength4RealFloat(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH5: { + new DFTLength5RealFloat(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength5RealFloat(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH6: { + new DFTLength6RealFloat(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength6RealFloat(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case LENGTH8: { + new DFTLength8RealFloat(reData, imData, + startIndex + stride, quadrupleStride).run(); + new DFTLength8RealFloat(reData, imData, + startIndex + tripleStride, quadrupleStride).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat, + fft4.K1).run(); + new DFTMixedRadixRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat, + fft4.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealFloat(reData, imData, + startIndex + stride, quadrupleStride, + fft4.reConvolverFloat, fft4.imConvolverFloat, + fft4.modular, fft4.inverseModular).run(); + new DFTPaddedRaderRealFloat(reData, imData, + startIndex + tripleStride, quadrupleStride, + fft4.reConvolverFloat, fft4.imConvolverFloat, + fft4.modular, fft4.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reConvolverFloat, fft4.imConvolverFloat, + fft4.modular, fft4.inverseModular).run(); + new DFTRaderRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reConvolverFloat, fft4.imConvolverFloat, + fft4.modular, fft4.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootEvenFloat, fft4.imUnitRootEvenFloat, + fft4.reUnitRootOddFloat, fft4.imUnitRootOddFloat).run(); + new DFTRadix2RealFloat(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootEvenFloat, fft4.imUnitRootEvenFloat, + fft4.reUnitRootOddFloat, fft4.imUnitRootOddFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + stride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat).run(); + new DFTSplitRadixRealFloat(reData, imData, reBuffer, imBuffer, + startIndex + tripleStride, quadrupleStride, + fft4.reUnitRootFloat, fft4.imUnitRootFloat).run(); + break; + } + } + int p = startIndex; + int q = startIndex; + for (int m = 0; (m < halfK2); m++) { + reBuffer[p] = reData[q]; + imBuffer[p] = imData[q]; + q += doubleStride; + p += stride; + } + p = startIndex + K2 * stride; + q = p + K4 * stride; + int r = startIndex + stride; + int s = r + doubleStride; + for (int m = 0, n = 0; (m < halfK4); m++, n += 3) { + float re = reData[r]; + float im = imData[r]; + float reRoot = reUnitRoot[m]; + float imRoot = imUnitRoot[m]; + float reButterfly = re * reRoot - im * imRoot; + float imButterfly = re * imRoot + im * reRoot; + re = reData[s]; + im = imData[s]; + reRoot = reUnitRoot[n]; + imRoot = imUnitRoot[n]; + final float reDragonfly = re * reRoot - im * imRoot; + final float imDragonfly = re * imRoot + im * reRoot; + reBuffer[p] = reButterfly + reDragonfly; + imBuffer[p] = imButterfly + imDragonfly; + reBuffer[q] = reButterfly - reDragonfly; + imBuffer[q] = imButterfly - imDragonfly; + r += quadrupleStride; + s += quadrupleStride; + p += stride; + q += stride; + }; + p = startIndex; + q = startIndex + K2 * stride; + reData[p] = reBuffer[p] + reBuffer[q]; + imData[p] = 0.0F; + p += stride; + q += stride; + for (int n = 1; (n < halfK4); n++) { + reData[p] = reBuffer[p] + reBuffer[q]; + imData[p] = imBuffer[p] + imBuffer[q]; + p += stride; + q += stride; + } + q = startIndex + (2 * K2 - halfK4) * stride; + for (int n = halfK4; (n < K4); n++) { + reData[p] = reBuffer[p] - imBuffer[q]; + imData[p] = imBuffer[p] - reBuffer[q]; + p += stride; + q -= stride; + } + reData[p] = reBuffer[p]; + imData[p] = -reBuffer[q]; + p += stride; + q += stride; + r = startIndex + (K4 - 1) * stride; + for (int n = 1; (n < halfK4); n++) { + reData[p] = reBuffer[r] + imBuffer[q]; + imData[p] = -imBuffer[r] - reBuffer[q]; + p += stride; + q += stride; + r -= stride; + } + q = startIndex + (K2 + K4 - halfK4) * stride; + for (int n = halfK4; (n < K4); n++) { + reData[p] = reBuffer[r] - reBuffer[q]; + imData[p] = imBuffer[q] - imBuffer[r]; + p += stride; + q -= stride; + r -= stride; + } + reData[p] = reBuffer[startIndex] - reBuffer[q]; + imData[p] = 0.0F; +} /* end run */ + +} /* end class DFTSplitRadixRealFloat */ + +/*==================================================================== +| FFTSetup +\===================================================================*/ +static class FFTSetup + +{ /* begin class FFTSetup */ + +/*.................................................................... + FFTSetup static private variables +....................................................................*/ +private static final HashSet<Integer> composites = new HashSet<Integer>(); +private static final HashSet<Integer> primes = new HashSet<Integer>(); + +/*.................................................................... + FFTSetup static protected variables +....................................................................*/ +protected static final HashMap<Integer, Algorithm> algorithms = + new HashMap<Integer, Algorithm>(); +protected static final HashMap<Integer, Integer> lengths = + new HashMap<Integer, Integer>(); +protected static final HashMap<Integer, FFTSetup> transforms = + new HashMap<Integer, FFTSetup>(); +protected static final HashMap<Integer, Long> costs = + new HashMap<Integer, Long>(); +protected static final HashSet<Integer> taboos = + new HashSet<Integer>(); +protected static final long FLASSIGN = 2L; +protected static final long FLOP = 4L; +protected static final long IDX = 1L; +protected static final long INTASSIGN = 1L; +protected static final long INTOP = 2L; +protected static final long NEWOBJ = 50L; + +protected static final long FLALLOC = FLOP + FLASSIGN; +protected static final long INTALLOC = INTOP + INTASSIGN; + +/*.................................................................... + FFTSetup static private variables +....................................................................*/ +private static int futurePrime = 7; + +/*.................................................................... + FFTSetup protected variables +....................................................................*/ +protected Algorithm algorithm; +protected double[] imConvolverDouble; +protected double[] imUnitRootDouble; +protected double[] reConvolverDouble; +protected double[] reUnitRootDouble; +protected float[] imConvolverFloat; +protected float[] imUnitRootFloat; +protected float[] reConvolverFloat; +protected float[] reUnitRootFloat; +protected int[] chinese; +protected int[] inverseModular; +protected int[] modular; +protected int[] ruritanian; +protected int K1; + +/*.................................................................... + FFTSetup inner classes +....................................................................*/ +/*==================================================================== +| FFTCostPrediction +\===================================================================*/ +static class FFTCostPrediction + +{ /* begin class FFTCostPrediction */ + +/*.................................................................... + FFTCostPrediction variables +....................................................................*/ +protected Algorithm algorithm; +protected int length; +protected long cost; + +/*.................................................................... + FFTCostPrediction constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +FFTCostPrediction ( + final Algorithm algorithm, + final int length, + final long cost +) { + this.algorithm = algorithm; + this.length = length; + this.cost = cost; +} /* end FFTCostPrediction */ + +/*.................................................................... + Object methods +....................................................................*/ +/*------------------------------------------------------------------*/ +@Override +public boolean equals ( + final Object o +) { + return((((FFTCostPrediction)o).algorithm == algorithm) + && (((FFTCostPrediction)o).length == length)); +} /* end equals */ + +} /* end class FFTCostPrediction */ + +/*.................................................................... + FFTSetup static initialization block +....................................................................*/ +static { + initialize(); +} + +/*.................................................................... + FFTSetup constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +FFTSetup ( + final int transformLength +) { + if (transforms.containsKey(new Integer(transformLength))) { + return; + } + transforms.put(new Integer(transformLength), this); + cost(transformLength); + algorithm = algorithms.get(new Integer(transformLength)); + switch (algorithm) { + case BRUTEFORCE: { + reUnitRootDouble = + DFTBruteForceDouble.getReUnitRoot(transformLength); + imUnitRootDouble = + DFTBruteForceDouble.getImUnitRoot(transformLength); + reUnitRootFloat = + DFTBruteForceFloat.getReUnitRoot(transformLength); + imUnitRootFloat = + DFTBruteForceFloat.getImUnitRoot(transformLength); + break; + } + case COPRIMEFACTOR: { + K1 = lengths.get(new Integer(transformLength)).intValue(); + final int K2 = transformLength / K1; + ruritanian = DFTCoprimeFactor.getRuritanianShuffling(K1, K2); + chinese = DFTCoprimeFactor.getChineseRemainderShuffling(K1, K2); + new FFTSetup(K1); + new FFTSetup(K2); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + break; + } + case LENGTH3: { + break; + } + case LENGTH4: { + break; + } + case LENGTH5: { + break; + } + case LENGTH6: { + break; + } + case LENGTH8: { + break; + } + case MIXEDRADIX: { + K1 = lengths.get(new Integer(transformLength)).intValue(); + final int K2 = transformLength / K1; + reUnitRootDouble = + DFTMixedRadixDouble.getReUnitRoot(transformLength); + imUnitRootDouble = + DFTMixedRadixDouble.getImUnitRoot(transformLength); + reUnitRootFloat = + DFTMixedRadixFloat.getReUnitRoot(transformLength); + imUnitRootFloat = + DFTMixedRadixFloat.getImUnitRoot(transformLength); + new FFTSetup(K1); + new FFTSetup(K2); + break; + } + case PADDEDRADER: { + final int paddedLength = + lengths.get(new Integer(transformLength)).intValue(); + modular = DFTRader.getModularPowerShuffling(transformLength); + inverseModular = + DFTPaddedRader.getInverseModularPowerShuffling(modular, + paddedLength); + new FFTSetup(paddedLength); + final double[][] convolverD = + DFTPaddedRaderDouble.getConvolverReAndIm(modular, + paddedLength); + reConvolverDouble = convolverD[0]; + imConvolverDouble = convolverD[1]; + final float[][] convolverF = + DFTPaddedRaderFloat.getConvolverReAndIm(modular, + paddedLength); + reConvolverFloat = convolverF[0]; + imConvolverFloat = convolverF[1]; + break; + } + case RADER: { + modular = DFTRader.getModularPowerShuffling(transformLength); + inverseModular = DFTRader.getInverseModularPowerShuffling(modular); + new FFTSetup(transformLength - 1); + final double[][] convolverD = + DFTRaderDouble.getConvolverReAndIm(modular); + reConvolverDouble = convolverD[0]; + imConvolverDouble = convolverD[1]; + final float[][] convolverF = + DFTRaderFloat.getConvolverReAndIm(modular); + reConvolverFloat = convolverF[0]; + imConvolverFloat = convolverF[1]; + break; + } + case RADIX2: { + reUnitRootDouble = + DFTRadix2Double.getReUnitRoot(transformLength); + imUnitRootDouble = + DFTRadix2Double.getImUnitRoot(transformLength); + reUnitRootFloat = + DFTRadix2Float.getReUnitRoot(transformLength); + imUnitRootFloat = + DFTRadix2Float.getImUnitRoot(transformLength); + new FFTSetup(transformLength >> 1); + break; + } + case SPLITRADIX: { + reUnitRootDouble = + DFTSplitRadixDouble.getReUnitRoot(transformLength); + imUnitRootDouble = + DFTSplitRadixDouble.getImUnitRoot(transformLength); + reUnitRootFloat = + DFTSplitRadixFloat.getReUnitRoot(transformLength); + imUnitRootFloat = + DFTSplitRadixFloat.getImUnitRoot(transformLength); + new FFTSetup(transformLength >> 1); + new FFTSetup(transformLength >> 2); + break; + } + } +} /* end FFTSetup */ + +/*.................................................................... + FFTSetup static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int transformLength +) { + if (costs.containsKey(new Integer(transformLength))) { + return(costs.get(new Integer(transformLength)).longValue()); + } + final Vector<FFTCostPrediction> costPredictions = + new Vector<FFTCostPrediction>(); + switch (transformLength) { + case 1: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH1, -1, 0L)); + break; + } + case 2: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH2, -1, DFTLength2.cost())); + break; + } + case 3: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH3, -1, DFTLength3.cost())); + break; + } + case 4: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH4, -1, DFTLength4.cost())); + break; + } + case 5: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH5, -1, DFTLength5.cost())); + break; + } + case 6: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH6, -1, DFTLength6.cost())); + break; + } + case 8: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH8, -1, DFTLength8.cost())); + break; + } + default: { + final HashSet<Integer> divisors = divisors(transformLength); + if (divisors.isEmpty()) { + costPredictions.add(new FFTCostPrediction( + Algorithm.RADER, -1, DFTRader.cost(transformLength))); + while (futurePrime < transformLength) { + cost(futurePrime); + } + do { + futurePrime +=2; + } while (!isPrime(futurePrime)); + int minPaddedLength = 2 * transformLength - 3; + int maxPaddedLength = 1; + int n = minPaddedLength; + while (0 < n) { + n >>= 1; + maxPaddedLength <<= 1; + } + for (n = transformLength; (n < minPaddedLength); n += 2) { + if (costs.containsKey(new Integer(n))) { + continue; + } + if (isPrime(n)) { + taboos.add(new Integer(n)); + taboos.add(new Integer(2 * n)); + taboos.add(new Integer(3 * n)); + } + } + for (n = minPaddedLength; (n <= maxPaddedLength); n += 2) { + if (costs.containsKey(new Integer(n))) { + continue; + } + if (isPrime(n)) { + taboos.add(new Integer(n)); + taboos.add(new Integer(2 * n)); + } + } + for (n = minPaddedLength; (n <= maxPaddedLength); n++) { + costPredictions.add(new FFTCostPrediction( + Algorithm.PADDEDRADER, n, + DFTPaddedRader.cost(transformLength, n))); + } + taboos.clear(); + } + else { + for (Integer d: divisors) { + final int K1 = d.intValue(); + final int K2 = transformLength / K1; + costPredictions.add(new FFTCostPrediction( + Algorithm.MIXEDRADIX, K1, + DFTMixedRadix.cost(K1, K2))); + if (1 == greatestCommonDivisor(K1, K2)) { + costPredictions.add(new FFTCostPrediction( + Algorithm.COPRIMEFACTOR, K1, + DFTCoprimeFactor.cost(K1, K2))); + } + if (2 == K1) { + costPredictions.add(new FFTCostPrediction( + Algorithm.RADIX2, -1, + DFTRadix2.cost(transformLength))); + } + if (4 == K1) { + costPredictions.add(new FFTCostPrediction( + Algorithm.SPLITRADIX, -1, + DFTSplitRadix.cost(transformLength))); + } + } + } + } + } + FFTCostPrediction best = new FFTCostPrediction( + Algorithm.BRUTEFORCE, -1, DFTBruteForce.cost(transformLength)); + long cheapest = best.cost; + for (FFTCostPrediction predicted: costPredictions) { + final long current = predicted.cost; + if (0L <= current) { + if (current < cheapest) { + cheapest = current; + best = predicted; + } + } + } + algorithms.put(new Integer(transformLength), best.algorithm); + lengths.put(new Integer(transformLength), new Integer(best.length)); + costs.put(new Integer(transformLength), new Long(cheapest)); + return(cheapest); +} /* end cost */ + +/*------------------------------------------------------------------*/ +static void reset ( +) { + composites.clear(); + primes.clear(); + algorithms.clear(); + lengths.clear(); + transforms.clear(); + costs.clear(); + taboos.clear(); + initialize(); +} /* end reset */ + +/*.................................................................... + FFTSetup private methods +....................................................................*/ +/*------------------------------------------------------------------*/ +private static HashSet<Integer> divisors ( + int number +) { + final HashSet<Integer> divisors = new HashSet<Integer>(); + for (int k = (int)floor(sqrt(0.5 + (double)number)); (2 <= k); k--) { + if (number == (k * (number / k))) { + divisors.add(new Integer(k)); + divisors.add(new Integer(number / k)); + } + } + return(divisors); +} /* end divisors */ + +/*------------------------------------------------------------------*/ +private static int greatestCommonDivisor ( + int m, + int n +) { + m = (m < 0) ? (-m) : (m); + n = (n < 0) ? (-n) : (n); + while (0 != n) { + final int p = n; + n = m - n * (m / n); + m = p; + } + return(m); +} /* end greatestCommonDivisor */ + +/*------------------------------------------------------------------*/ +static private void initialize ( +) { + primes.add(new Integer(2)); + primes.add(new Integer(3)); + primes.add(new Integer(5)); + new FFTSetup(2); + new FFTSetup(3); + new FFTSetup(5); + futurePrime = 7; +} /* end initialize */ + +/*------------------------------------------------------------------*/ +static private boolean isPrime ( + final int number +) { + if (composites.contains(new Integer(number))) { + return(false); + } + if (primes.contains(new Integer(number))) { + return(true); + } + boolean isComposite = false; + for (int k = (int)floor(sqrt(0.5 + (double)number)); (1 < k); k--) { + if (number == (k * (number / k))) { + isComposite = true; + break; + } + } + if (isComposite) { + composites.add(new Integer(number)); + return(false); + } + primes.add(new Integer(number)); + return(true); +} /* end isPrime */ + +} /* end class FFTSetup */ + +/*==================================================================== +| FFTSetupDuoReal +\===================================================================*/ +static class FFTSetupDuoReal + extends FFTSetupReal + +{ /* begin class FFTSetupDuoReal */ + +/*.................................................................... + FFTSetupDuoReal static protected variables +....................................................................*/ +protected static final HashMap<Integer, Algorithm> algorithms = + new HashMap<Integer, Algorithm>(); +protected static final HashMap<Integer, Integer> lengths = + new HashMap<Integer, Integer>(); +protected static final HashMap<Integer, FFTSetupDuoReal> transforms = + new HashMap<Integer, FFTSetupDuoReal>(); +protected static final HashMap<Integer, Long> costs = + new HashMap<Integer, Long>(); + +/*.................................................................... + FFTSetupDuoReal protected variables +....................................................................*/ +protected Algorithm algorithm; + +/*.................................................................... + FFTSetupDuoReal static initialization block +....................................................................*/ +static { + initialize(); +} + +/*.................................................................... + FFTSetupDuoReal constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +FFTSetupDuoReal ( + final int transformLength +) { + super(transformLength); + if (transforms.containsKey(new Integer(transformLength))) { + return; + } + new FFTSetupReal(transformLength); + algorithm = cheapest(transformLength); + FFTSetupReal fft = + FFTSetupReal.transforms.get(new Integer(transformLength)); + imConvolverDouble = fft.imConvolverDouble; + imUnitRootDouble = fft.imUnitRootDouble; + imUnitRootEvenDouble = fft.imUnitRootEvenDouble; + imUnitRootOddDouble = fft.imUnitRootOddDouble; + reConvolverDouble = fft.reConvolverDouble; + reUnitRootDouble = fft.reUnitRootDouble; + reUnitRootEvenDouble = fft.reUnitRootEvenDouble; + reUnitRootOddDouble = fft.reUnitRootOddDouble; + imConvolverFloat = fft.imConvolverFloat; + imUnitRootEvenFloat = fft.imUnitRootEvenFloat; + imUnitRootFloat = fft.imUnitRootFloat; + imUnitRootOddFloat = fft.imUnitRootOddFloat; + reConvolverFloat = fft.reConvolverFloat; + reUnitRootEvenFloat = fft.reUnitRootEvenFloat; + reUnitRootFloat = fft.reUnitRootFloat; + reUnitRootOddFloat = fft.reUnitRootOddFloat; + chinese = fft.chinese; + inverseModular = fft.inverseModular; + modular = fft.modular; + ruritanian = fft.ruritanian; + K1 = fft.K1; + transforms.put(new Integer(transformLength), this); +} /* end FFTSetupDuoReal */ + +/*.................................................................... + FFTSetupDuoReal static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static Algorithm cheapest ( + final int transformLength +) { + if (transforms.containsKey(new Integer(transformLength))) { + return(transforms.get(new Integer(transformLength)).algorithm); + } + final Vector<FFTCostPrediction> costPredictions = + new Vector<FFTCostPrediction>(); + costPredictions.add(new FFTCostPrediction( + FFTSetupReal.algorithms.get(new Integer(transformLength)), + FFTSetupReal.lengths.get(new Integer(transformLength)).intValue(), + 2L * FFTSetupReal.costs.get(new Integer(transformLength)).longValue())); + FFTCostPrediction best = new FFTCostPrediction( + Algorithm.DUOREAL, -1, DFTDuoReal.cost(transformLength)); + long cheapest = best.cost; + for (FFTCostPrediction predicted: costPredictions) { + final long current = predicted.cost; + if (0L <= current) { + if (current < cheapest) { + cheapest = current; + best = predicted; + } + } + } + algorithms.put(new Integer(transformLength), best.algorithm); + lengths.put(new Integer(transformLength), new Integer(best.length)); + costs.put(new Integer(transformLength), new Long(cheapest)); + return(best.algorithm); +} /* end cheapest */ + +/*------------------------------------------------------------------*/ +static void reset ( +) { + transforms.clear(); + initialize(); +} /* end reset */ + +/*------------------------------------------------------------------*/ +static private void initialize ( +) { + new FFTSetupDuoReal(2); + new FFTSetupDuoReal(3); + new FFTSetupDuoReal(5); +} /* end initialize */ + +} /* end class FFTSetupDuoReal */ + +/*==================================================================== +| FFTSetupReal +\===================================================================*/ +static class FFTSetupReal + extends FFTSetup + +{ /* begin class FFTSetupReal */ + +/*.................................................................... + FFTSetupReal static protected variables +....................................................................*/ +protected static final HashMap<Integer, Algorithm> algorithms = + new HashMap<Integer, Algorithm>(); +protected static final HashMap<Integer, Integer> lengths = + new HashMap<Integer, Integer>(); +protected static final HashMap<Integer, FFTSetupReal> transforms = + new HashMap<Integer, FFTSetupReal>(); +protected static final HashMap<Integer, Long> costs = + new HashMap<Integer, Long>(); + +/*.................................................................... + FFTSetupReal static private variables +....................................................................*/ +private static int futurePrime = 7; + +/*.................................................................... + FFTSetupReal protected variables +....................................................................*/ +protected Algorithm algorithm; +protected double[] imConvolverDouble; +protected double[] imUnitRootDouble; +protected double[] imUnitRootEvenDouble; +protected double[] imUnitRootOddDouble; +protected double[] reConvolverDouble; +protected double[] reUnitRootDouble; +protected double[] reUnitRootEvenDouble; +protected double[] reUnitRootOddDouble; +protected float[] imConvolverFloat; +protected float[] imUnitRootEvenFloat; +protected float[] imUnitRootFloat; +protected float[] imUnitRootOddFloat; +protected float[] reConvolverFloat; +protected float[] reUnitRootEvenFloat; +protected float[] reUnitRootFloat; +protected float[] reUnitRootOddFloat; +protected int[] chinese; +protected int[] inverseModular; +protected int[] modular; +protected int[] ruritanian; +protected int K1; + +/*.................................................................... + FFTSetupReal static initialization block +....................................................................*/ +static { + initialize(); +} + +/*.................................................................... + FFTSetupReal constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +FFTSetupReal ( + final int transformLength +) { + super(transformLength); + if (transforms.containsKey(new Integer(transformLength))) { + return; + } + transforms.put(new Integer(transformLength), this); + cost(transformLength); + algorithm = algorithms.get(new Integer(transformLength)); + switch (algorithm) { + case BRUTEFORCE: { + reUnitRootDouble = + DFTBruteForceDouble.getReUnitRoot(transformLength); + imUnitRootDouble = + DFTBruteForceDouble.getImUnitRoot(transformLength); + reUnitRootFloat = + DFTBruteForceFloat.getReUnitRoot(transformLength); + imUnitRootFloat = + DFTBruteForceFloat.getImUnitRoot(transformLength); + break; + } + case COPRIMEFACTOR: { + K1 = lengths.get(new Integer(transformLength)).intValue(); + final int K2 = transformLength / K1; + ruritanian = DFTCoprimeFactor.getRuritanianShuffling(K1, K2); + chinese = + DFTCoprimeFactorReal.getTruncatedChineseRemainderShuffling( + K1, K2); + new FFTSetup(K1); + new FFTSetupReal(K2); + new FFTSetupDuoReal(K2); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + reUnitRootDouble = + DFTEvenRealDouble.getReUnitRoot(transformLength); + imUnitRootDouble = + DFTEvenRealDouble.getImUnitRoot(transformLength); + reUnitRootFloat = + DFTEvenRealFloat.getReUnitRoot(transformLength); + imUnitRootFloat = + DFTEvenRealFloat.getImUnitRoot(transformLength); + new FFTSetup(transformLength >> 1); + break; + } + case LENGTH1: { + break; + } + case LENGTH2: { + break; + } + case LENGTH3: { + break; + } + case LENGTH4: { + break; + } + case LENGTH5: { + break; + } + case LENGTH6: { + break; + } + case LENGTH8: { + break; + } + case MIXEDRADIX: { + K1 = lengths.get(new Integer(transformLength)).intValue(); + final int K2 = transformLength / K1; + reUnitRootDouble = + DFTMixedRadixDouble.getReUnitRoot(transformLength); + imUnitRootDouble = + DFTMixedRadixDouble.getImUnitRoot(transformLength); + reUnitRootFloat = + DFTMixedRadixFloat.getReUnitRoot(transformLength); + imUnitRootFloat = + DFTMixedRadixFloat.getImUnitRoot(transformLength); + new FFTSetup(K1); + new FFTSetupReal(K2); + new FFTSetupDuoReal(K2); + break; + } + case PADDEDRADER: { + final int paddedLength = + lengths.get(new Integer(transformLength)).intValue(); + modular = DFTRader.getModularPowerShuffling(transformLength); + inverseModular = + DFTPaddedRader.getInverseModularPowerShuffling(modular, + paddedLength); + new FFTSetup(paddedLength); + new FFTSetupReal(paddedLength); + final double[][] convolverD = + DFTPaddedRaderDouble.getConvolverReAndIm(modular, + paddedLength); + reConvolverDouble = convolverD[0]; + imConvolverDouble = convolverD[1]; + final float[][] convolverF = + DFTPaddedRaderFloat.getConvolverReAndIm(modular, + paddedLength); + reConvolverFloat = convolverF[0]; + imConvolverFloat = convolverF[1]; + break; + } + case RADER: { + modular = DFTRader.getModularPowerShuffling(transformLength); + inverseModular = DFTRader.getInverseModularPowerShuffling(modular); + new FFTSetup(transformLength - 1); + new FFTSetupReal(transformLength - 1); + final double[][] convolverD = + DFTRaderDouble.getConvolverReAndIm(modular); + reConvolverDouble = convolverD[0]; + imConvolverDouble = convolverD[1]; + final float[][] convolverF = + DFTRaderFloat.getConvolverReAndIm(modular); + reConvolverFloat = convolverF[0]; + imConvolverFloat = convolverF[1]; + break; + } + case RADIX2: { + reUnitRootEvenDouble = + DFTRadix2RealDouble.getReUnitRootEven(transformLength); + imUnitRootEvenDouble = + DFTRadix2RealDouble.getImUnitRootEven(transformLength); + reUnitRootOddDouble = + DFTRadix2RealDouble.getReUnitRootOdd(transformLength); + imUnitRootOddDouble = + DFTRadix2RealDouble.getImUnitRootOdd(transformLength); + reUnitRootEvenFloat = + DFTRadix2RealFloat.getReUnitRootEven(transformLength); + imUnitRootEvenFloat = + DFTRadix2RealFloat.getImUnitRootEven(transformLength); + reUnitRootOddFloat = + DFTRadix2RealFloat.getReUnitRootOdd(transformLength); + imUnitRootOddFloat = + DFTRadix2RealFloat.getImUnitRootOdd(transformLength); + new FFTSetupDuoReal(transformLength >> 1); + break; + } + case SPLITRADIX: { + reUnitRootDouble = + DFTSplitRadixDouble.getReUnitRoot(transformLength); + imUnitRootDouble = + DFTSplitRadixDouble.getImUnitRoot(transformLength); + reUnitRootFloat = + DFTSplitRadixFloat.getReUnitRoot(transformLength); + imUnitRootFloat = + DFTSplitRadixFloat.getImUnitRoot(transformLength); + new FFTSetupReal(transformLength >> 1); + new FFTSetupDuoReal(transformLength >> 2); + break; + } + } +} /* end FFTSetupReal */ + +/*.................................................................... + FFTSetupReal static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +static long cost ( + final int transformLength +) { + if (costs.containsKey(new Integer(transformLength))) { + return(costs.get(new Integer(transformLength)).longValue()); + } + final Vector<FFTCostPrediction> costPredictions = + new Vector<FFTCostPrediction>(); + switch (transformLength) { + case 1: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH1, -1, 0L)); + break; + } + case 2: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH2, -1, DFTLength2Real.cost())); + break; + } + case 3: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH3, -1, DFTLength3Real.cost())); + break; + } + case 4: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH4, -1, DFTLength4Real.cost())); + break; + } + case 5: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH5, -1, DFTLength5Real.cost())); + break; + } + case 6: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH6, -1, DFTLength6Real.cost())); + break; + } + case 8: { + costPredictions.add(new FFTCostPrediction( + Algorithm.LENGTH8, -1, DFTLength8Real.cost())); + break; + } + default: { + final HashSet<Integer> divisors = + FFTSetup.divisors(transformLength); + if (divisors.isEmpty()) { + costPredictions.add(new FFTCostPrediction( + Algorithm.RADER, -1, DFTRaderReal.cost(transformLength))); + while (futurePrime < transformLength) { + cost(futurePrime); + } + do { + futurePrime +=2; + } while (!FFTSetup.isPrime(futurePrime)); + int minPaddedLength = 2 * transformLength - 3; + int maxPaddedLength = 1; + int n = minPaddedLength; + while (0 < n) { + n >>= 1; + maxPaddedLength <<= 1; + } + for (n = transformLength; (n < minPaddedLength); n += 2) { + if (costs.containsKey(new Integer(n))) { + continue; + } + if (FFTSetup.isPrime(n)) { + taboos.add(new Integer(n)); + taboos.add(new Integer(2 * n)); + taboos.add(new Integer(3 * n)); + } + } + for (n = minPaddedLength; (n <= maxPaddedLength); n += 2) { + if (costs.containsKey(new Integer(n))) { + continue; + } + if (FFTSetup.isPrime(n)) { + taboos.add(new Integer(n)); + taboos.add(new Integer(2 * n)); + } + } + for (n = minPaddedLength; (n <= maxPaddedLength); n++) { + costPredictions.add(new FFTCostPrediction( + Algorithm.PADDEDRADER, n, + DFTPaddedRaderReal.cost(transformLength, n))); + } + taboos.clear(); + } + else { + for (Integer d: divisors) { + final int K1 = d.intValue(); + final int K2 = transformLength / K1; + costPredictions.add(new FFTCostPrediction( + Algorithm.MIXEDRADIX, K1, + DFTMixedRadixReal.cost(K1, K2))); + if (1 == FFTSetup.greatestCommonDivisor(K1, K2)) { + costPredictions.add(new FFTCostPrediction( + Algorithm.COPRIMEFACTOR, K1, + DFTCoprimeFactorReal.cost(K1, K2))); + } + if (2 == K1) { + costPredictions.add(new FFTCostPrediction( + Algorithm.EVENREAL, -1, + DFTEvenReal.cost(transformLength))); + costPredictions.add(new FFTCostPrediction( + Algorithm.RADIX2, -1, + DFTRadix2Real.cost(transformLength))); + } + if (4 == K1) { + costPredictions.add(new FFTCostPrediction( + Algorithm.SPLITRADIX, -1, + DFTSplitRadixReal.cost(transformLength))); + } + } + } + } + } + FFTCostPrediction best = new FFTCostPrediction( + Algorithm.BRUTEFORCE, -1, DFTBruteForceReal.cost(transformLength)); + long cheapest = best.cost; + for (FFTCostPrediction predicted: costPredictions) { + final long current = predicted.cost; + if (0L <= current) { + if (current < cheapest) { + cheapest = current; + best = predicted; + } + } + } + algorithms.put(new Integer(transformLength), best.algorithm); + lengths.put(new Integer(transformLength), new Integer(best.length)); + costs.put(new Integer(transformLength), new Long(cheapest)); + return(cheapest); +} /* end cost */ + +/*------------------------------------------------------------------*/ +static void reset ( +) { + algorithms.clear(); + lengths.clear(); + transforms.clear(); + costs.clear(); + initialize(); +} /* end reset */ + +/*------------------------------------------------------------------*/ +static private void initialize ( +) { + new FFTSetupReal(2); + new FFTSetupReal(3); + new FFTSetupReal(5); + futurePrime = 7; +} /* end initialize */ + +} /* end class FFTSetupReal */ + +/*.................................................................... + SlowFourierTransform constructors +....................................................................*/ +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This constructor prepares a one-dimensional Fourier transform. It + depends on the length of the sequence but not on the actual data + being transformed, so that it can be reused for different data. + It specifies the convention to adopt for locating the origin of the + Fourier transform. + </p> + @param width The length of the sequence. + @param fourierOrigin1 The origin of the Fourier transform in the + Fourier domain. + ********************************************************************/ +public AcademicFourierTransform ( + final int width, + final int fourierOrigin1 +) { + if (width <= 0) { + throw(new IllegalArgumentException()); + } + this.width = new Integer(width); + this.height = new Integer(1); + this.depth = new Integer(1); + this.fourierOrigin1 = fourierOrigin1; + this.fourierOrigin2 = 0; + this.fourierOrigin3 = 0; + dimensions = 1; + dataLength = width; + new FFTSetup(width); + new FFTSetupReal(width); + new FFTSetupDuoReal(width); + firstDimension = 1; +} /* end SlowFourierTransform */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This constructor prepares a two-dimensional Fourier transform. It + depends on the width and height of the image but not on the actual + data being transformed, so that it can be reused for different data. + It specifies the convention to adopt for locating the origin of the + Fourier transform. + </p> + @param width The width of the image. + @param height The height of the image. + @param fourierOrigin1 The horizontal component of the origin of the + Fourier transform in the Fourier domain. + @param fourierOrigin2 The vertical component of the origin of the + Fourier transform in the Fourier domain. + ********************************************************************/ +public AcademicFourierTransform ( + final int width, + final int height, + final int fourierOrigin1, + final int fourierOrigin2 +) { + if ((width <= 0) || (height <= 0)) { + throw(new IllegalArgumentException()); + } + this.width = new Integer(width); + this.height = new Integer(height); + this.depth = new Integer(1); + this.fourierOrigin1 = fourierOrigin1; + this.fourierOrigin2 = fourierOrigin2; + this.fourierOrigin3 = 0; + dimensions = 2; + dataLength = width * height; + new FFTSetup(width); + new FFTSetupReal(width); + new FFTSetupDuoReal(width); + new FFTSetup(height); + new FFTSetupReal(height); + new FFTSetupDuoReal(height); + final long K1 = (long)width; + final long K2 = (long)height; + final long k1 = K1 >> 1L; + final long k2 = K2 >> 1L; + final long costColumnFirst = FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * (k2 * 1L + k2 * ((K1 - 1L) * 1L)) + + FFTSetup.FLASSIGN * (k2 * 2L + k2 * ((K1 - 1L) * 2L)) + + FFTSetup.INTALLOC * 10L + + FFTSetup.INTOP * (5L + K1 * 2L + 1L + (k2 + 1L) * 3L + 2L + + k2 * 3L + 3L + k2 * (4L + (K1 - 1L) * 4L)) + + FFTSetup.INTASSIGN * (4L + K1 * 1L + 2L + (k2 + 1L) * 2L + 2L + + k2 * 2L + 3L + k2 * (6L + (K1 - 1L) * 3L)) + + FFTSetup.IDX * (k2 * 4L + k2 * ((K1 - 1L) * 4L)) + + FFTSetup.NEWOBJ * (K1 + (k2 + 1L)) + + k1 * FFTSetupDuoReal.cost(height) + + (k2 + 1L) * FFTSetup.cost(width); + final long costRowFirst = FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * (k1 * 1L + (K2 - 1L) * (k1 * 1L)) + + FFTSetup.FLASSIGN * (k1 * 2L + (K2 - 1L) * (k1 * 2L)) + + FFTSetup.INTALLOC * 9L + + FFTSetup.INTOP * (5L + K2 * 3L + 1L + (k1 + 1L) * 2L + + k1 * 3L + 3L + (K2 - 1L) * (4L + k1 * 4L)) + + FFTSetup.INTASSIGN * (5L + K2 * 2L + 1L + (k1 + 1L) * 1L + 2L + + k1 * 2L + 3L + (K2 - 1L) * (6L + k1 * 3L)) + + FFTSetup.IDX * (k1 * 4L + (K2 - 1L) * (k1 * 4L)) + + FFTSetup.NEWOBJ * (K2 + (k1 + 1L)) + + k2 * FFTSetupDuoReal.cost(width) + + (k1 + 1L) * FFTSetup.cost(height); + firstDimension = (costRowFirst < costColumnFirst) ? (1) : (2); +} /* end SlowFourierTransform */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This constructor prepares a three-dimensional Fourier transform. It + depends on the width, height, and depth of the volume but not on the + actual data being transformed, so that it can be reused for different + data. It specifies the convention to adopt for locating the origin of + the Fourier transform. + </p> + @param width The width of the volume. + @param height The height of the volume. + @param depth The depth of the volume. + @param fourierOrigin1 The horizontal component of the origin of the + Fourier transform in the Fourier domain. + @param fourierOrigin2 The vertical component of the origin of the + Fourier transform in the Fourier domain. + @param fourierOrigin3 The depth component of the origin of the + Fourier transform in the Fourier domain. + ********************************************************************/ +public AcademicFourierTransform ( + final int width, + final int height, + final int depth, + final int fourierOrigin1, + final int fourierOrigin2, + final int fourierOrigin3 +) { + if ((width <= 0) || (height <= 0) || (depth <= 0)) { + throw(new IllegalArgumentException()); + } + this.width = new Integer(width); + this.height = new Integer(height); + this.depth = new Integer(depth); + this.fourierOrigin1 = fourierOrigin1; + this.fourierOrigin2 = fourierOrigin2; + this.fourierOrigin3 = fourierOrigin3; + dimensions = 3; + dataLength = width * height* depth; + new FFTSetup(width); + new FFTSetupReal(width); + new FFTSetupDuoReal(width); + new FFTSetup(height); + new FFTSetupReal(height); + new FFTSetupDuoReal(height); + new FFTSetup(depth); + new FFTSetupReal(depth); + new FFTSetupDuoReal(depth); + final long K1 = (long)width; + final long K2 = (long)height; + final long K3 = (long)depth; + final long k1 = K1 >> 1L; + final long k2 = K2 >> 1L; + final long k3 = K3 >> 1L; + final long costAcrossFirst = FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * (k3 * 1L + k3 * ((K2 - 1L) * 1L) + + k3 * ((K1 - 1L) * 1L) + k3 * ((K2 - 1L) * ((K1 - 1L) * 1L))) + + FFTSetup.FLASSIGN * (k3 * 2L + k3 * ((K2 - 1L) * 2L) + + k3 * ((K1 - 1L) * 2L) + k3 * ((K2 - 1L) * ((K1 - 1L) * 2L))) + + FFTSetup.INTALLOC * 23L + + FFTSetup.INTOP * (6L + K2 * K1 * 2L + 1L + (k3 + 1L) * (2L + K2 * 3L) + + 1L + (k3 + 1L) * (3L + K1 * 3L) + 1L + k3 * 3L + 2L + + k3 * (4L + (K2 - 1L) * 4L) + 5L + k3 * (4L + (K1 - 1L) * 4L) + 3L + + k3 * (4L + (K2 - 1L) * (4L + (K1 - 1L) * 4L))) + + FFTSetup.INTASSIGN * (6L + K2 * K1 * 1L + 2L + + (k3 + 1L) * (2L + K2 * 2L) + 2L + (k3 + 1L) * (3L + K1 * 1L) + 2L + + k3 * 2L + 3L + k3 * (6L + (K2 - 1L) * 3L) + 3L + + k3 * (6L + (K1 - 1L) * 3L) + 3L + + k3 * (4L + (K2 - 1L) * (4L + (K1 - 1L) * 3L))) + + FFTSetup.IDX * (k3 * 4L + k3 * ((K2 - 1L) * 4L) + + k3 * ((K1 - 1L) * 4L) + k3 * ((K2 - 1L) * ((K1 - 1L) * 4L))) + + FFTSetup.NEWOBJ * (K2 * K1 + (k3 + 1L) * K2+ (k3 + 1L) * K1) + + ((K2 * K1) >> 1) * FFTSetupDuoReal.cost(depth) + + (k3 + 1L) * K2 * FFTSetup.cost(width) + + (k3 + 1L) * K1 * FFTSetup.cost(height); + final long costColumnFirst = FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * (k2 * 1L + k2 * ((K2 - 1L) * 1L) + + (K3 - 1L) * (k2 * 1L) + (K3 - 1L) * (k2 * ((K1 - 1L) * 1L))) + + FFTSetup.FLASSIGN * (k2 * 2L + k2 * ((K1 - 1L) * 2L) + + (K3 - 1L) * (k2 * 2L) + (K3 - 1L) * (k2 * ((K1 - 1L) * 2L))) + + FFTSetup.INTALLOC * 26L + + FFTSetup.INTOP * (8L + K3 * K1 * 5L + 3L + K3 * (3L + (k2 + 1L) * 3L) + + 1L + (k2 + 1L) * (2L + K1 * 3L) + 1L + k2 * 3L + 2L + + k2 * (4L + (K1 - 1L) * 4L) + 3L + (K3 - 1L) * (4L + k2 * 4L) + 3L + + (K3 - 1L) * (6L + k2 * (4L + (K1 - 1L) * 4L))) + + FFTSetup.INTASSIGN * (8L + K3 * K1 * 1L + 3L + + K3 * (3L + (k2 + 1L) * 2L) + 2L + (k2 + 1L) * (2L + K1 * 2L) + 2L + + k2 * 2L + 3L + k2 * (6L + (K1 - 1L) * 3L) + 3L + + (K3 - 1L) * (6L + k2 * 3L) + 3L + + (K3 - 1L) * (4L + k2 * (4L + (K1 - 1L) * 3L))) + + FFTSetup.IDX * (k2 * 4L + k2 * ((K1 - 1L) * 4L) + + (K3 - 1L) * (k2 * 4L) + (K3 - 1L) * (k2 * ((K1 - 1L) * 4L))) + + FFTSetup.NEWOBJ * (K3 * K1 + K3 * (k2 + 1L) + (k2 + 1L) * K1) + + ((K3 * K1) >> 1) * FFTSetupDuoReal.cost(height) + + K3 * (k2 + 1L) * FFTSetup.cost(width) + + (k2 + 1L) * K1 * FFTSetup.cost(depth); + final long costRowFirst = FFTSetup.FLALLOC * 0L + + FFTSetup.FLOP * (k1 * 1L + (K2 - 1L) * (k1 * 1L) + + (K3 - 1L) * (k1 * 1L) + (K3 - 1L) * ((K2 - 1L) * (k1 * 1L))) + + FFTSetup.FLASSIGN * (k1 * 2L + (K2 - 1L) * (k1 * 2L) + + (K3 - 1L) * (k1 * 2L) + (K3 - 1L) * ((K2 - 1L) * (k1 * 2L))) + + FFTSetup.INTALLOC * 22L + + FFTSetup.INTOP * (7L + K3 * K2 * 3L + 1L + K3 * (3L + (k1 + 1L) * 3L) + + 1L + K2 * (3L + (k1 + 1L) * 3L) + k1 * 3L + 2L + + (K2 - 1L) * (4L + k1 * 4L) + 5L + (K3 - 1L) * (4L + k1 * 4L) + 3L + + (K3 - 1L) * (4L + (K2 - 1L) * (4L + k1 * 4L))) + + FFTSetup.INTASSIGN * (8L + K3 * K2 * 2L + 2L + + K3 * (3L + (k1 + 1L) * 1L) + 2L + K2 * (3L + (k1 + 1L) * 1L) + 2L + + k1 * 2L + 3L + (K2 - 1L) * (6L + k1 * 3L) + 3L + + (K3 - 1L) * (6L + k1 * 3L) + 3L + + (K3 - 1L) * (4L + (K2 - 1L) * (4L + k1 * 3L))) + + FFTSetup.IDX * (k1 * 4L + (K2 - 1L) * (k1 * 4L) + + (K3 - 1L) * (k1 * 4L) + (K3 - 1L) * ((K2 - 1L) * (k1 * 4L))) + + FFTSetup.NEWOBJ * (K3 * K2 + K3 * (k1 + 1L) + K2 * (k1 + 1L)) + + ((K3 * K2) >> 1) * FFTSetupDuoReal.cost(width) + + K3 * (k1 + 1L) * FFTSetup.cost(height) + + K2 * (k1 + 1L) * FFTSetup.cost(depth); + firstDimension = (costRowFirst < costColumnFirst) + ? ((costRowFirst < costAcrossFirst) ? (1) : (3)) + : ((costColumnFirst < costAcrossFirst) ? (2) : (3)); +} /* end SlowFourierTransform */ + +/*.................................................................... + SlowFourierTransform static methods +....................................................................*/ +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method converts complex data from an amplitude-phase + representation to a real-imaginary representation. The phase is + assumed to be provided in radian units. + </p> + @param amToRe At input, the amplitude of the data; at output, the + real part of the data. The processing is in-place. + @param phToIm At input, the phase of the data; at output, the + imaginary part of the data. The processing is in-place. + ********************************************************************/ +static public void amplitudePhaseToRealImaginary ( + final double[] amToRe, + final double[] phToIm +) { + if (amToRe.length != phToIm.length) { + throw(new IllegalArgumentException()); + } + for (int k = 0, K = amToRe.length; (k < K); k++) { + final double am = amToRe[k]; + final double ph = phToIm[k]; + amToRe[k] = am * cos(ph); + phToIm[k] = am * sin(ph); + } +} /* end amplitudePhaseToRealImaginary */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method converts complex data from an amplitude-phase + representation to a real-imaginary representation. The phase is + assumed to be provided in radian units. + </p> + @param amToRe At input, the amplitude of the data; at output, the + real part of the data. The processing is in-place. + @param phToIm At input, the phase of the data; at output, the + imaginary part of the data. The processing is in-place. + ********************************************************************/ +static public void amplitudePhaseToRealImaginary ( + final float[] amToRe, + final float[] phToIm +) { + if (amToRe.length != phToIm.length) { + throw(new IllegalArgumentException()); + } + for (int k = 0, K = amToRe.length; (k < K); k++) { + final float am = amToRe[k]; + final float ph = phToIm[k]; + amToRe[k] = am * (float)cos((double)ph); + phToIm[k] = am * (float)sin((double)ph); + } +} /* end amplitudePhaseToRealImaginary */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method converts complex data from a real-imaginary + representation to an amplitude-phase representation. The resulting + phase is provided in radian units in the range [−π, + π]. + </p> + @param reToAm At input, the real part of the data; at output, the + amplitude of the data. The processing is in-place. + @param imToPh At input, the imaginary part of the data; at output, + the phase of the data. The processing is in-place. + ********************************************************************/ +static public void realImaginaryToAmplitudePhase ( + final double[] reToAm, + final double[] imToPh +) { + if (reToAm.length != imToPh.length) { + throw(new IllegalArgumentException()); + } + for (int k = 0, K = reToAm.length; (k < K); k++) { + final double am = sqrt(reToAm[k] * reToAm[k] + imToPh[k] * imToPh[k]); + final double ph = atan2(imToPh[k], reToAm[k]); + reToAm[k] = am; + imToPh[k] = ph; + } +} /* end realImaginaryToAmplitudePhase */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method converts complex data from a real-imaginary + representation to an amplitude-phase representation. The resulting + phase is provided in radian units in the range [−π, + π]. + </p> + @param reToAm At input, the real part of the data; at output, the + amplitude of the data. The processing is in-place. + @param imToPh At input, the imaginary part of the data; at output, + the phase of the data. The processing is in-place. + ********************************************************************/ +static public void realImaginaryToAmplitudePhase ( + final float[] reToAm, + final float[] imToPh +) { + if (reToAm.length != imToPh.length) { + throw(new IllegalArgumentException()); + } + for (int k = 0, K = reToAm.length; (k < K); k++) { + final float am = (float)sqrt((double)(reToAm[k] * reToAm[k] + + imToPh[k] * imToPh[k])); + final float ph = (float)atan2((double)imToPh[k], (double)reToAm[k]); + reToAm[k] = am; + imToPh[k] = ph; + } +} /* end realImaginaryToAmplitudePhase */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + Several <code>static</code> auxiliary data are stored by this class, + as needs arise, to be reused by subsequent instances of this class; + this method frees the associated memory. + </p> + ********************************************************************/ +static public void reset ( +) { + FFTSetup.reset(); + FFTSetupReal.reset(); + FFTSetupDuoReal.reset(); +} /* end reset */ + +/*.................................................................... + SlowFourierTransform public methods +....................................................................*/ +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method computes the circular convolution of data provided in a + real-imaginary representation. The number of dimensions of the data + is determined by the constructor of this object. Likewise, the + dimensions themselves must match those provided to the constructor of + this object. + </p> + @param reData1 At input, the real part of the first operand; at + output, the real part of the convolution result. The processing is + in-place. + @param imData1 At input, the imaginary part of the first operand; at + output, the imaginary part of the convolution result. The processing + is in-place. + @param reData2 At input, the real part of the second operand; at + output, the real part of the Fourier transform of the second operand. + The origin of the Fourier transform of the second operand follows the + conventions for this object. The processing is in-place. + @param imData2 At input, the imaginary part of the second operand; at + output, the imaginary part of the Fourier transform of the second + operand. The origin of the Fourier transform of the second operand + follows the conventions for this object. The processing is in-place. + @param reBuffer Garbage in, garbage out. A temporary buffer of length + <code>reData1.length</code> is created internally if + <code>reBuffer</code> is <code>null</code>. + @param imBuffer Garbage in, garbage out. A temporary buffer of length + <code>imData1.length</code> is created internally if + <code>imBuffer</code> is <code>null</code>. + ********************************************************************/ +public void circularConvolution ( + final double[] reData1, + final double[] imData1, + final double[] reData2, + final double[] imData2, + double[] reBuffer, + double[] imBuffer +) { + if (null == reBuffer) { + reBuffer = new double[reData1.length]; + } + if (null == imBuffer) { + imBuffer = new double[imData1.length]; + } + if ((reData1.length != dataLength) + || (imData1.length != dataLength) + || (reData2.length != dataLength) + || (imData2.length != dataLength) + || (reBuffer.length != dataLength) + || (imBuffer.length != dataLength)) { + throw(new IllegalArgumentException()); + } + this.reBufferDouble = reBuffer; + this.imBufferDouble = imBuffer; + this.reDataDouble = reData2; + this.imDataDouble = imData2; + transformDouble(InputDataType.COMPLEXINPUT); + this.reDataDouble = reData1; + this.imDataDouble = imData1; + transformDouble(InputDataType.COMPLEXINPUT); + final double norm = 1.0 / (double)dataLength; + for (int k = 0; (k < dataLength); k++) { + final double re1 = norm * reData1[k]; + final double im1 = norm * imData1[k]; + reData1[k] = re1 * reData2[k] - im1 * imData2[k]; + imData1[k] = re1 * imData2[k] + im1 * reData2[k]; + } + reverseDouble(); + transformDouble(InputDataType.COMPLEXINPUT); + this.reDataDouble = reData2; + this.imDataDouble = imData2; + this.reBufferDouble = reBuffer; + this.imBufferDouble = imBuffer; + switch (dimensions) { + case 1: { + shiftDouble(fourierOrigin1); + break; + } + case 2: { + shiftDouble(fourierOrigin1, fourierOrigin2); + break; + } + case 3: { + shiftDouble(fourierOrigin1, fourierOrigin2, fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } +} /* end circularConvolution */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method computes the circular convolution of data provided in a + real-imaginary representation. The number of dimensions of the data + is determined by the constructor of this object. Likewise, the + dimensions themselves must match those provided to the constructor of + this object. + </p> + @param reData1 At input, the real part of the first operand; at + output, the real part of the convolution result. The processing is + in-place. + @param imData1 At input, the imaginary part of the first operand; at + output, the imaginary part of the convolution result. The processing + is in-place. + @param reData2 At input, the real part of the second operand; at + output, the real part of the Fourier transform of the second operand. + The origin of the Fourier transform of the second operand follows the + conventions for this object. The processing is in-place. + @param imData2 At input, the imaginary part of the second operand; at + output, the imaginary part of the Fourier transform of the second + operand. The origin of the Fourier transform of the second operand + follows the conventions for this object. The processing is in-place. + @param reBuffer Garbage in, garbage out. A temporary buffer of length + <code>reData1.length</code> is created internally if + <code>reBuffer</code> is <code>null</code>. + @param imBuffer Garbage in, garbage out. A temporary buffer of length + <code>imData1.length</code> is created internally if + <code>imBuffer</code> is <code>null</code>. + ********************************************************************/ +public void circularConvolution ( + final float[] reData1, + final float[] imData1, + final float[] reData2, + final float[] imData2, + float[] reBuffer, + float[] imBuffer +) { + if (null == reBuffer) { + reBuffer = new float[reData1.length]; + } + if (null == imBuffer) { + imBuffer = new float[imData1.length]; + } + if ((reData1.length != dataLength) + || (imData1.length != dataLength) + || (reData2.length != dataLength) + || (imData2.length != dataLength) + || (reBuffer.length != dataLength) + || (imBuffer.length != dataLength)) { + throw(new IllegalArgumentException()); + } + this.reBufferFloat = reBuffer; + this.imBufferFloat = imBuffer; + this.reDataFloat = reData2; + this.imDataFloat = imData2; + transformFloat(InputDataType.COMPLEXINPUT); + this.reDataFloat = reData1; + this.imDataFloat = imData1; + transformFloat(InputDataType.COMPLEXINPUT); + final float norm = 1.0F / (float)dataLength; + for (int k = 0; (k < dataLength); k++) { + final float re1 = norm * reData1[k]; + final float im1 = norm * imData1[k]; + reData1[k] = re1 * reData2[k] - im1 * imData2[k]; + imData1[k] = re1 * imData2[k] + im1 * reData2[k]; + } + reverseFloat(); + transformFloat(InputDataType.COMPLEXINPUT); + this.reDataFloat = reData2; + this.imDataFloat = imData2; + this.reBufferFloat = reBuffer; + this.imBufferFloat = imBuffer; + switch (dimensions) { + case 1: { + shiftFloat(fourierOrigin1); + break; + } + case 2: { + shiftFloat(fourierOrigin1, fourierOrigin2); + break; + } + case 3: { + shiftFloat(fourierOrigin1, fourierOrigin2, fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } +} /* end circularConvolution */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method computes the circular convolution of data; the first + operand is provided in a real-imaginary representation while the + second operand is provided in a real-imaginary Fourier + representation. The number of dimensions of the data is determined by + the constructor of this object. Likewise, the dimensions themselves + must match those provided to the constructor of this object. + </p> + @param reData1 At input, the real part of the first operand; at + output, the real part of the convolution result. The processing is + in-place. + @param imData1 At input, the imaginary part of the first operand; at + output, the imaginary part of the convolution result. The processing + is in-place. + @param reFourierData2 Real part of the Fourier transform of the + second operand. The origin of the Fourier transform of the second + operand follows the conventions for this object. + @param imFourierData2 Imaginary part of the Fourier transform of the + second operand. The origin of the Fourier transform of the second + operand follows the conventions for this object. + @param reBuffer Garbage in, garbage out. A temporary buffer of length + <code>reData1.length</code> is created internally if + <code>reBuffer</code> is <code>null</code>. + @param imBuffer Garbage in, garbage out. A temporary buffer of length + <code>imData1.length</code> is created internally if + <code>imBuffer</code> is <code>null</code>. + ********************************************************************/ +public void circularFourierConvolution ( + final double[] reData1, + final double[] imData1, + final double[] reFourierData2, + final double[] imFourierData2, + double[] reBuffer, + double[] imBuffer +) { + if (null == reBuffer) { + reBuffer = new double[reData1.length]; + } + if (null == imBuffer) { + imBuffer = new double[imData1.length]; + } + if ((reData1.length != dataLength) + || (imData1.length != dataLength) + || (reFourierData2.length != dataLength) + || (imFourierData2.length != dataLength) + || (reBuffer.length != dataLength) + || (imBuffer.length != dataLength)) { + throw(new IllegalArgumentException()); + } + this.reBufferDouble = reBuffer; + this.imBufferDouble = imBuffer; + this.reDataDouble = reData1; + this.imDataDouble = imData1; + transformDouble(InputDataType.COMPLEXINPUT); + switch (dimensions) { + case 1: { + shiftDouble(fourierOrigin1); + break; + } + case 2: { + shiftDouble(fourierOrigin1, fourierOrigin2); + break; + } + case 3: { + shiftDouble(fourierOrigin1, fourierOrigin2, fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } + final double norm = 1.0 / (double)dataLength; + for (int k = 0; (k < dataLength); k++) { + final double re1 = norm * reData1[k]; + final double im1 = norm * imData1[k]; + reData1[k] = re1 * reFourierData2[k] - im1 * imFourierData2[k]; + imData1[k] = re1 * imFourierData2[k] + im1 * reFourierData2[k]; + } + switch (dimensions) { + case 1: { + shiftDouble(-fourierOrigin1); + break; + } + case 2: { + shiftDouble(-fourierOrigin1, -fourierOrigin2); + break; + } + case 3: { + shiftDouble(-fourierOrigin1, -fourierOrigin2, -fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } + reverseDouble(); + transformDouble(InputDataType.COMPLEXINPUT); +} /* circularFourierConvolution */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method computes the circular convolution of data; the first + operand is provided in a real-imaginary representation while the + second operand is provided in a real-imaginary Fourier + representation. The number of dimensions of the data is determined by + the constructor of this object. Likewise, the dimensions themselves + must match those provided to the constructor of this object. + </p> + @param reData1 At input, the real part of the first operand; at + output, the real part of the convolution result. The processing is + in-place. + @param imData1 At input, the imaginary part of the first operand; at + output, the imaginary part of the convolution result. The processing + is in-place. + @param reFourierData2 Real part of the Fourier transform of the + second operand. The origin of the Fourier transform of the second + operand follows the conventions for this object. + @param imFourierData2 Imaginary part of the Fourier transform of the + second operand. The origin of the Fourier transform of the second + operand follows the conventions for this object. + @param reBuffer Garbage in, garbage out. A temporary buffer of length + <code>reData1.length</code> is created internally if + <code>reBuffer</code> is <code>null</code>. + @param imBuffer Garbage in, garbage out. A temporary buffer of length + <code>imData1.length</code> is created internally if + <code>imBuffer</code> is <code>null</code>. + ********************************************************************/ +public void circularFourierConvolution ( + final float[] reData1, + final float[] imData1, + final float[] reFourierData2, + final float[] imFourierData2, + float[] reBuffer, + float[] imBuffer +) { + if (null == reBuffer) { + reBuffer = new float[reData1.length]; + } + if (null == imBuffer) { + imBuffer = new float[imData1.length]; + } + if ((reData1.length != dataLength) + || (imData1.length != dataLength) + || (reFourierData2.length != dataLength) + || (imFourierData2.length != dataLength) + || (reBuffer.length != dataLength) + || (imBuffer.length != dataLength)) { + throw(new IllegalArgumentException()); + } + this.reBufferFloat = reBuffer; + this.imBufferFloat = imBuffer; + this.reDataFloat = reData1; + this.imDataFloat = imData1; + transformFloat(InputDataType.COMPLEXINPUT); + switch (dimensions) { + case 1: { + shiftFloat(fourierOrigin1); + break; + } + case 2: { + shiftFloat(fourierOrigin1, fourierOrigin2); + break; + } + case 3: { + shiftFloat(fourierOrigin1, fourierOrigin2, fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } + final float norm = 1.0F / (float)dataLength; + for (int k = 0; (k < dataLength); k++) { + final float re1 = norm * reData1[k]; + final float im1 = norm * imData1[k]; + reData1[k] = re1 * reFourierData2[k] - im1 * imFourierData2[k]; + imData1[k] = re1 * imFourierData2[k] + im1 * reFourierData2[k]; + } + switch (dimensions) { + case 1: { + shiftFloat(-fourierOrigin1); + break; + } + case 2: { + shiftFloat(-fourierOrigin1, -fourierOrigin2); + break; + } + case 3: { + shiftFloat(-fourierOrigin1, -fourierOrigin2, -fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } + reverseFloat(); + transformFloat(InputDataType.COMPLEXINPUT); +} /* circularFourierConvolution */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method computes the Fourier transform of data provided in a + real-imaginary representation. The number of dimensions of the data + is determined by the constructor of this object. Likewise, the + dimensions themselves must match those provided to the constructor of + this object. + </p> + @param reData At input, the real part of the data; at output, the + real part of the Fourier transform of the data. The processing is + in-place. + @param imData At input, the imaginary part of the data; at output, + the imaginary part of the Fourier transform of the data. The + processing is in-place. + @param reBuffer Garbage in, garbage out. A temporary buffer of length + <code>reData.length</code> is created internally if + <code>reBuffer</code> is <code>null</code>. + @param imBuffer Garbage in, garbage out. A temporary buffer of length + <code>imData.length</code> is created internally if + <code>imBuffer</code> is <code>null</code>. + @param inputDataType When set to + <code>SlowFourierTransform.InputDataType.REALINPUT</code>, disregards + the values provided in <code>imData</code> and assumes that every + element of <code>imData</code> is <code>0.0</code>; when set to + <code>SlowFourierTransform.InputDataType.COMPLEXINPUT</code>, honors + the values provided in <code>imData</code>. + ********************************************************************/ +public void directTransform ( + final double[] reData, + final double[] imData, + double[] reBuffer, + double[] imBuffer, + final InputDataType inputDataType +) { + if (null == reBuffer) { + reBuffer = new double[reData.length]; + } + if (null == imBuffer) { + imBuffer = new double[imData.length]; + } + if ((reData.length != dataLength) + || (imData.length != dataLength) + || (reBuffer.length != dataLength) + || (imBuffer.length != dataLength)) { + throw(new IllegalArgumentException()); + } + this.reDataDouble = reData; + this.imDataDouble = imData; + this.reBufferDouble = reBuffer; + this.imBufferDouble = imBuffer; + transformDouble(inputDataType); + switch (dimensions) { + case 1: { + shiftDouble(fourierOrigin1); + break; + } + case 2: { + shiftDouble(fourierOrigin1, fourierOrigin2); + break; + } + case 3: { + shiftDouble(fourierOrigin1, fourierOrigin2, fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } +} /* end directTransform */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method computes the Fourier transform of data provided in a + real-imaginary representation. The number of dimensions of the data + is determined by the constructor of this object. Likewise, the + dimensions themselves must match those provided to the constructor of + this object. + </p> + @param reData At input, the real part of the data; at output, the + real part of the Fourier transform of the data. The processing is + in-place. + @param imData At input, the imaginary part of the data; at output, + the imaginary part of the Fourier transform of the data. The + processing is in-place. + @param reBuffer Garbage in, garbage out. A temporary buffer of length + <code>reData.length</code> is created internally if + <code>reBuffer</code> is <code>null</code>. + @param imBuffer Garbage in, garbage out. A temporary buffer of length + <code>imData.length</code> is created internally if + <code>imBuffer</code> is <code>null</code>. + @param inputDataType When set to + <code>SlowFourierTransform.InputDataType.REALINPUT</code>, disregards + the values provided in <code>imData</code> and assumes that every + element of <code>imData</code> is <code>0.0</code>; when set to + <code>SlowFourierTransform.InputDataType.COMPLEXINPUT</code>, honors + the values provided in <code>imData</code>. + ********************************************************************/ +public void directTransform ( + final float[] reData, + final float[] imData, + float[] reBuffer, + float[] imBuffer, + final InputDataType inputDataType +) { + if (null == reBuffer) { + reBuffer = new float[reData.length]; + } + if (null == imBuffer) { + imBuffer = new float[imData.length]; + } + if ((reData.length != dataLength) + || (imData.length != dataLength) + || (reBuffer.length != dataLength) + || (imBuffer.length != dataLength)) { + throw(new IllegalArgumentException()); + } + this.reDataFloat = reData; + this.imDataFloat = imData; + this.reBufferFloat = reBuffer; + this.imBufferFloat = imBuffer; + transformFloat(inputDataType); + switch (dimensions) { + case 1: { + shiftFloat(fourierOrigin1); + break; + } + case 2: { + shiftFloat(fourierOrigin1, fourierOrigin2); + break; + } + case 3: { + shiftFloat(fourierOrigin1, fourierOrigin2, fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } +} /* end directTransform */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method computes the inverse Fourier transform of data provided + in a real-imaginary representation. The number of dimensions of the + data is determined by the constructor of this object. Likewise, the + dimensions themselves must match those provided to the constructor of + this object. + </p> + @param reData At input, the real part of the data; at output, the + real part of the Fourier transform of the data. The processing is + in-place. + @param imData At input, the imaginary part of the data; at output, + the imaginary part of the Fourier transform of the data. The + processing is in-place. + @param reBuffer Garbage in, garbage out. A temporary buffer of length + <code>reData.length</code> is created internally if + <code>reBuffer</code> is <code>null</code>. + @param imBuffer Garbage in, garbage out. A temporary buffer of length + <code>imData.length</code> is created internally if + <code>imBuffer</code> is <code>null</code>. + ********************************************************************/ +public void inverseTransform ( + final double[] reData, + final double[] imData, + double[] reBuffer, + double[] imBuffer +) { + if (null == reBuffer) { + reBuffer = new double[reData.length]; + } + if (null == imBuffer) { + imBuffer = new double[imData.length]; + } + if ((reData.length != dataLength) + || (imData.length != dataLength) + || (reBuffer.length != dataLength) + || (imBuffer.length != dataLength)) { + throw(new IllegalArgumentException()); + } + this.reDataDouble = reData; + this.imDataDouble = imData; + this.reBufferDouble = reBuffer; + this.imBufferDouble = imBuffer; + final double norm = 1.0 / (double)dataLength; + for (int k = 0; (k < dataLength); k++) { + reData[k] *= norm; + imData[k] *= norm; + } + switch (dimensions) { + case 1: { + shiftDouble(-fourierOrigin1); + break; + } + case 2: { + shiftDouble(-fourierOrigin1, -fourierOrigin2); + break; + } + case 3: { + shiftDouble(-fourierOrigin1, -fourierOrigin2, -fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } + reverseDouble(); + transformDouble(InputDataType.COMPLEXINPUT); +} /* end inverseTransform */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method computes the inverse Fourier transform of data provided + in a real-imaginary representation. The number of dimensions of the + data is determined by the constructor of this object. Likewise, the + dimensions themselves must match those provided to the constructor of + this object. + </p> + @param reData At input, the real part of the data; at output, the + real part of the Fourier transform of the data. The processing is + in-place. + @param imData At input, the imaginary part of the data; at output, + the imaginary part of the Fourier transform of the data. The + processing is in-place. + @param reBuffer Garbage in, garbage out. A temporary buffer of length + <code>reData.length</code> is created internally if + <code>reBuffer</code> is <code>null</code>. + @param imBuffer Garbage in, garbage out. A temporary buffer of length + <code>imData.length</code> is created internally if + <code>imBuffer</code> is <code>null</code>. + ********************************************************************/ +public void inverseTransform ( + final float[] reData, + final float[] imData, + float[] reBuffer, + float[] imBuffer +) { + if (null == reBuffer) { + reBuffer = new float[reData.length]; + } + if (null == imBuffer) { + imBuffer = new float[imData.length]; + } + if ((reData.length != dataLength) + || (imData.length != dataLength) + || (reBuffer.length != dataLength) + || (imBuffer.length != dataLength)) { + throw(new IllegalArgumentException()); + } + this.reDataFloat = reData; + this.imDataFloat = imData; + this.reBufferFloat = reBuffer; + this.imBufferFloat = imBuffer; + final float norm = 1.0F / (float)dataLength; + for (int k = 0; (k < dataLength); k++) { + reData[k] *= norm; + imData[k] *= norm; + } + switch (dimensions) { + case 1: { + shiftFloat(-fourierOrigin1); + break; + } + case 2: { + shiftFloat(-fourierOrigin1, -fourierOrigin2); + break; + } + case 3: { + shiftFloat(-fourierOrigin1, -fourierOrigin2, -fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } + reverseFloat(); + transformFloat(InputDataType.COMPLEXINPUT); +} /* end inverseTransform */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method enforces Hermitian symmetry to Fourier data provided in a + real-imaginary representation. The number of dimensions of the data + is determined by the constructor of this object. Likewise, the + dimensions themselves must match those provided to the constructor of + this object. The output Fourier data <i>Y</i> satisfies + ℜ(<i>Y</i>[0]) = ℜ(<i>X</i>[0]), ℑ(<i>Y</i>[0]) = 0, + <i>Y</i>[<i>n</i>] = ℜ(<i>X</i>[<i>n</i>] + <i>X</i>[<i>K</i> + − <i>n</i>]) ∕ 2 + j ℑ(<i>X</i>[<i>n</i>] − + <i>X</i>[<i>K</i> − <i>n</i>]) ∕ 2 for <i>n</i> ∈ + [1…<i>K</i> − 1], where <i>X</i> is the input Fourier + data. + </p> + @param reData At input, the real part of the data; at output, the + real part is symmetric with respect to the origin. The processing is + in-place. + @param imData At input, the imaginary part of the data; at output, + the imaginary part is antisymmetric with respect to the origin. The + processing is in-place. + @param reBuffer Garbage in, garbage out. A temporary buffer of length + <code>reData.length</code> is created internally if + <code>reBuffer</code> is <code>null</code>. + @param imBuffer Garbage in, garbage out. A temporary buffer of length + <code>imData.length</code> is created internally if + <code>imBuffer</code> is <code>null</code>. + ********************************************************************/ +public void makeHermitian ( + final double[] reData, + final double[] imData, + double[] reBuffer, + double[] imBuffer +) { + if (null == reBuffer) { + reBuffer = new double[reData.length]; + } + if (null == imBuffer) { + imBuffer = new double[imData.length]; + } + if ((reData.length != dataLength) + || (imData.length != dataLength) + || (reBuffer.length != dataLength) + || (imBuffer.length != dataLength)) { + throw(new IllegalArgumentException()); + } + this.reDataDouble = reData; + this.imDataDouble = imData; + this.reBufferDouble = reBuffer; + this.imBufferDouble = imBuffer; + switch (dimensions) { + case 1: { + shiftDouble(-fourierOrigin1); + break; + } + case 2: { + shiftDouble(-fourierOrigin1, -fourierOrigin2); + break; + } + case 3: { + shiftDouble(-fourierOrigin1, -fourierOrigin2, -fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } + System.arraycopy(reData, 0, reBuffer, 0, dataLength); + System.arraycopy(imData, 0, imBuffer, 0, dataLength); + reverseDouble(); + for (int k = 0; (k < dataLength); k++) { + reData[k] = 0.5 * (reBuffer[k] + reData[k]); + imData[k] = 0.5 * (imBuffer[k] - imData[k]); + } + switch (dimensions) { + case 1: { + shiftDouble(fourierOrigin1); + break; + } + case 2: { + shiftDouble(fourierOrigin1, fourierOrigin2); + break; + } + case 3: { + shiftDouble(fourierOrigin1, fourierOrigin2, fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } +} /* end makeHermitian */ + +/*------------------------------------------------------------------*/ +/********************************************************************* + <p> + This method enforces Hermitian symmetry to Fourier data provided in a + real-imaginary representation. The number of dimensions of the data + is determined by the constructor of this object. Likewise, the + dimensions themselves must match those provided to the constructor of + this object. The output Fourier data <i>Y</i> satisfies + ℜ(<i>Y</i>[0]) = ℜ(<i>X</i>[0]), ℑ(<i>Y</i>[0]) = 0, + <i>Y</i>[<i>n</i>] = ℜ(<i>X</i>[<i>n</i>] + <i>X</i>[<i>K</i> + − <i>n</i>]) ∕ 2 + j ℑ(<i>X</i>[<i>n</i>] − + <i>X</i>[<i>K</i> − <i>n</i>]) ∕ 2 for <i>n</i> ∈ + [1…<i>K</i> − 1], where <i>X</i> is the input Fourier + data. + </p> + @param reData At input, the real part of the data; at output, the + real part is symmetric with respect to the origin. The processing is + in-place. + @param imData At input, the imaginary part of the data; at output, + the imaginary part is antisymmetric with respect to the origin. The + processing is in-place. + @param reBuffer Garbage in, garbage out. A temporary buffer of length + <code>reData.length</code> is created internally if + <code>reBuffer</code> is <code>null</code>. + @param imBuffer Garbage in, garbage out. A temporary buffer of length + <code>imData.length</code> is created internally if + <code>imBuffer</code> is <code>null</code>. + ********************************************************************/ +public void makeHermitian ( + final float[] reData, + final float[] imData, + float[] reBuffer, + float[] imBuffer +) { + if (null == reBuffer) { + reBuffer = new float[reData.length]; + } + if (null == imBuffer) { + imBuffer = new float[imData.length]; + } + if ((reData.length != dataLength) + || (imData.length != dataLength) + || (reBuffer.length != dataLength) + || (imBuffer.length != dataLength)) { + throw(new IllegalArgumentException()); + } + this.reDataFloat = reData; + this.imDataFloat = imData; + this.reBufferFloat = reBuffer; + this.imBufferFloat = imBuffer; + switch (dimensions) { + case 1: { + shiftFloat(-fourierOrigin1); + break; + } + case 2: { + shiftFloat(-fourierOrigin1, -fourierOrigin2); + break; + } + case 3: { + shiftFloat(-fourierOrigin1, -fourierOrigin2, -fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } + System.arraycopy(reData, 0, reBuffer, 0, dataLength); + System.arraycopy(imData, 0, imBuffer, 0, dataLength); + reverseFloat(); + for (int k = 0; (k < dataLength); k++) { + reData[k] = 0.5F * (reBuffer[k] + reData[k]); + imData[k] = 0.5F * (imBuffer[k] - imData[k]); + } + switch (dimensions) { + case 1: { + shiftFloat(fourierOrigin1); + break; + } + case 2: { + shiftFloat(fourierOrigin1, fourierOrigin2); + break; + } + case 3: { + shiftFloat(fourierOrigin1, fourierOrigin2, fourierOrigin3); + break; + } + default: { + throw(new IllegalStateException()); + } + } +} /* end makeHermitian */ + +/*.................................................................... + SlowFourierTransform private methods +....................................................................*/ +/*------------------------------------------------------------------*/ +private void reverseDouble ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int K3 = depth.intValue(); + final int K1K2 = K1 * K2; + if (1 < K1) { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + int progressive = p; + int regressive = p + K1; + while (++progressive < --regressive) { + final double reSwap = reDataDouble[progressive]; + reDataDouble[progressive] = reDataDouble[regressive]; + reDataDouble[regressive] = reSwap; + final double imSwap = imDataDouble[progressive]; + imDataDouble[progressive] = imDataDouble[regressive]; + imDataDouble[regressive] = imSwap; + } + p += K1; + } + } + } + if (1 < K2) { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + int q = p; + for (int k1 = 0; (k1 < K1); k1++) { + int progressive = q + K1; + int regressive = q + K1 * (K2 - 1); + while (progressive < regressive) { + final double reSwap = reDataDouble[progressive]; + reDataDouble[progressive] = reDataDouble[regressive]; + reDataDouble[regressive] = reSwap; + final double imSwap = imDataDouble[progressive]; + imDataDouble[progressive] = imDataDouble[regressive]; + imDataDouble[regressive] = imSwap; + progressive += K1; + regressive -= K1; + } + q++; + } + p += K1K2; + } + } + if (1 < K3) { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + int progressive = p + K1K2; + int regressive = p + K1K2 * (K3 - 1); + while (progressive < regressive) { + final double reSwap = reDataDouble[progressive]; + reDataDouble[progressive] = reDataDouble[regressive]; + reDataDouble[regressive] = reSwap; + final double imSwap = imDataDouble[progressive]; + imDataDouble[progressive] = imDataDouble[regressive]; + imDataDouble[regressive] = imSwap; + progressive += K1K2; + regressive -= K1K2; + } + p++; + } + } + } +} /* end reverseDouble */ + +/*------------------------------------------------------------------*/ +private void reverseFloat ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int K3 = depth.intValue(); + final int K1K2 = K1 * K2; + if (1 < K1) { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + int progressive = p; + int regressive = p + K1; + while (++progressive < --regressive) { + final float reSwap = reDataFloat[progressive]; + reDataFloat[progressive] = reDataFloat[regressive]; + reDataFloat[regressive] = reSwap; + final float imSwap = imDataFloat[progressive]; + imDataFloat[progressive] = imDataFloat[regressive]; + imDataFloat[regressive] = imSwap; + } + p += K1; + } + } + } + if (1 < K2) { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + int q = p; + for (int k1 = 0; (k1 < K1); k1++) { + int progressive = q + K1; + int regressive = q + K1 * (K2 - 1); + while (progressive < regressive) { + final float reSwap = reDataFloat[progressive]; + reDataFloat[progressive] = reDataFloat[regressive]; + reDataFloat[regressive] = reSwap; + final float imSwap = imDataFloat[progressive]; + imDataFloat[progressive] = imDataFloat[regressive]; + imDataFloat[regressive] = imSwap; + progressive += K1; + regressive -= K1; + } + q++; + } + p += K1K2; + } + } + if (1 < K3) { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + int progressive = p + K1K2; + int regressive = p + K1K2 * (K3 - 1); + while (progressive < regressive) { + final float reSwap = reDataFloat[progressive]; + reDataFloat[progressive] = reDataFloat[regressive]; + reDataFloat[regressive] = reSwap; + final float imSwap = imDataFloat[progressive]; + imDataFloat[progressive] = imDataFloat[regressive]; + imDataFloat[regressive] = imSwap; + progressive += K1K2; + regressive -= K1K2; + } + p++; + } + } + } +} /* end reverseFloat */ + +/*------------------------------------------------------------------*/ +private void shiftDouble ( + int o1 +) { + final int K1 = width.intValue(); + o1 = (o1 < 0) ? (o1 + K1 * ((K1 - 1 - o1) / K1)) : (o1 - K1 * (o1 / K1)); + if (0 == o1) { + return; + } + final int range1 = K1 - o1; + System.arraycopy(reDataDouble, 0, reBufferDouble, o1, + range1); + System.arraycopy(imDataDouble, 0, imBufferDouble, o1, + range1); + System.arraycopy(reDataDouble, range1, reBufferDouble, 0, + o1); + System.arraycopy(imDataDouble, range1, imBufferDouble, 0, + o1); + System.arraycopy(reBufferDouble, 0, reDataDouble, 0, + K1); + System.arraycopy(imBufferDouble, 0, imDataDouble, 0, + K1); +} /* end shiftDouble */ + +/*------------------------------------------------------------------*/ +private void shiftDouble ( + int o1, + int o2 +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + o1 = (o1 < 0) ? (o1 + K1 * ((K1 - 1 - o1) / K1)) : (o1 - K1 * (o1 / K1)); + o2 = (o2 < 0) ? (o2 + K2 * ((K2 - 1 - o2) / K2)) : (o2 - K2 * (o2 / K2)); + if ((0 == o1) && (0 == o2)) { + return; + } + final int range1 = K1 - o1; + final int range2 = K2 - o2; + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + System.arraycopy(reDataDouble, p, reBufferDouble, p + o1, + range1); + System.arraycopy(imDataDouble, p, imBufferDouble, p + o1, + range1); + System.arraycopy(reDataDouble, p + range1, reBufferDouble, p, + o1); + System.arraycopy(imDataDouble, p + range1, imBufferDouble, p, + o1); + p += K1; + } + System.arraycopy(reBufferDouble, 0, reDataDouble, o2 * K1, + range2 * K1); + System.arraycopy(imBufferDouble, 0, imDataDouble, o2 * K1, + range2 * K1); + System.arraycopy(reBufferDouble, range2 * K1, reDataDouble, 0, + o2 * K1); + System.arraycopy(imBufferDouble, range2 * K1, imDataDouble, 0, + o2 * K1); +} /* end shiftDouble */ + +/*------------------------------------------------------------------*/ +private void shiftDouble ( + int o1, + int o2, + int o3 +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int K3 = depth.intValue(); + o1 = (o1 < 0) ? (o1 + K1 * ((K1 - 1 - o1) / K1)) : (o1 - K1 * (o1 / K1)); + o2 = (o2 < 0) ? (o2 + K2 * ((K2 - 1 - o2) / K2)) : (o2 - K2 * (o2 / K2)); + o3 = (o3 < 0) ? (o3 + K3 * ((K3 - 1 - o3) / K3)) : (o3 - K3 * (o3 / K3)); + if ((0 == o1) && (0 == o2) && (0 == o3)) { + return; + } + final int K1K2 = K1 * K2; + final int range1 = K1 - o1; + final int range2 = K2 - o2; + final int range3 = K3 - o3; + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + System.arraycopy(reDataDouble, p, reBufferDouble, p + o1, + range1); + System.arraycopy(imDataDouble, p, imBufferDouble, p + o1, + range1); + System.arraycopy(reDataDouble, p + range1, reBufferDouble, p, + o1); + System.arraycopy(imDataDouble, p + range1, imBufferDouble, p, + o1); + p += K1; + } + } + p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + System.arraycopy(reBufferDouble, p, reDataDouble, o2 * K1, + range2 * K1); + System.arraycopy(imBufferDouble, p, imDataDouble, o2 * K1, + range2 * K1); + System.arraycopy(reBufferDouble, range2 * K1, reDataDouble, p, + o2 * K1); + System.arraycopy(imBufferDouble, range2 * K1, imDataDouble, p, + fourierOrigin2 * K1); + p += K1K2; + } + System.arraycopy(reDataDouble, 0, reBufferDouble, o3 * K1K2, + range3 * K1K2); + System.arraycopy(imDataDouble, 0, imBufferDouble, o3 * K1K2, + range3 * K1K2); + System.arraycopy(reDataDouble, range3 * K1K2, reBufferDouble, 0, + o3 * K1K2); + System.arraycopy(imDataDouble, range3 * K1K2, imBufferDouble, 0, + o3 * K1K2); + System.arraycopy(reBufferDouble, 0, reDataDouble, 0, + dataLength); + System.arraycopy(imBufferDouble, 0, imDataDouble, 0, + dataLength); +} /* end shiftDouble */ + +/*------------------------------------------------------------------*/ +private void shiftFloat ( + int o1 +) { + final int K1 = width.intValue(); + o1 = (o1 < 0) ? (o1 + K1 * ((K1 - 1 - o1) / K1)) : (o1 - K1 * (o1 / K1)); + if (0 == o1) { + return; + } + final int range1 = K1 - o1; + System.arraycopy(reDataFloat, 0, reBufferFloat, o1, + range1); + System.arraycopy(imDataFloat, 0, imBufferFloat, o1, + range1); + System.arraycopy(reDataFloat, range1, reBufferFloat, 0, + o1); + System.arraycopy(imDataFloat, range1, imBufferFloat, 0, + o1); + System.arraycopy(reBufferFloat, 0, reDataFloat, 0, + K1); + System.arraycopy(imBufferFloat, 0, imDataFloat, 0, + K1); +} /* end shiftFloat */ + +/*------------------------------------------------------------------*/ +private void shiftFloat ( + int o1, + int o2 +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + o1 = (o1 < 0) ? (o1 + K1 * ((K1 - 1 - o1) / K1)) : (o1 - K1 * (o1 / K1)); + o2 = (o2 < 0) ? (o2 + K2 * ((K2 - 1 - o2) / K2)) : (o2 - K2 * (o2 / K2)); + if ((0 == o1) && (0 == o2)) { + return; + } + final int range1 = K1 - o1; + final int range2 = K2 - o2; + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + System.arraycopy(reDataFloat, p, reBufferFloat, p + o1, + range1); + System.arraycopy(imDataFloat, p, imBufferFloat, p + o1, + range1); + System.arraycopy(reDataFloat, p + range1, reBufferFloat, p, + o1); + System.arraycopy(imDataFloat, p + range1, imBufferFloat, p, + o1); + p += K1; + } + System.arraycopy(reBufferFloat, 0, reDataFloat, o2 * K1, + range2 * K1); + System.arraycopy(imBufferFloat, 0, imDataFloat, o2 * K1, + range2 * K1); + System.arraycopy(reBufferFloat, range2 * K1, reDataFloat, 0, + o2 * K1); + System.arraycopy(imBufferFloat, range2 * K1, imDataFloat, 0, + o2 * K1); +} /* end shiftFloat */ + +/*------------------------------------------------------------------*/ +private void shiftFloat ( + int o1, + int o2, + int o3 +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int K3 = depth.intValue(); + o1 = (o1 < 0) ? (o1 + K1 * ((K1 - 1 - o1) / K1)) : (o1 - K1 * (o1 / K1)); + o2 = (o2 < 0) ? (o2 + K2 * ((K2 - 1 - o2) / K2)) : (o2 - K2 * (o2 / K2)); + o3 = (o3 < 0) ? (o3 + K3 * ((K3 - 1 - o3) / K3)) : (o3 - K3 * (o3 / K3)); + if ((0 == o1) && (0 == o2) && (0 == o3)) { + return; + } + final int K1K2 = K1 * K2; + final int range1 = K1 - o1; + final int range2 = K2 - o2; + final int range3 = K3 - o3; + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + System.arraycopy(reDataFloat, p, reBufferFloat, p + o1, + range1); + System.arraycopy(imDataFloat, p, imBufferFloat, p + o1, + range1); + System.arraycopy(reDataFloat, p + range1, reBufferFloat, p, + o1); + System.arraycopy(imDataFloat, p + range1, imBufferFloat, p, + o1); + p += K1; + } + } + p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + System.arraycopy(reBufferFloat, p, reDataFloat, o2 * K1, + range2 * K1); + System.arraycopy(imBufferFloat, p, imDataFloat, o2 * K1, + range2 * K1); + System.arraycopy(reBufferFloat, range2 * K1, reDataFloat, p, + o2 * K1); + System.arraycopy(imBufferFloat, range2 * K1, imDataFloat, p, + fourierOrigin2 * K1); + p += K1K2; + } + System.arraycopy(reDataFloat, 0, reBufferFloat, o3 * K1K2, + range3 * K1K2); + System.arraycopy(imDataFloat, 0, imBufferFloat, o3 * K1K2, + range3 * K1K2); + System.arraycopy(reDataFloat, range3 * K1K2, reBufferFloat, 0, + o3 * K1K2); + System.arraycopy(imDataFloat, range3 * K1K2, imBufferFloat, 0, + o3 * K1K2); + System.arraycopy(reBufferFloat, 0, reDataFloat, 0, + dataLength); + System.arraycopy(imBufferFloat, 0, imDataFloat, 0, + dataLength); +} /* end shiftFloat */ + +/*------------------------------------------------------------------*/ +private void transformDouble ( + final InputDataType inputDataType +) { + switch (inputDataType) { + case COMPLEXINPUT: { + switch (dimensions) { + case 1: { + transformDouble1D(); + break; + } + case 2: { + transformDouble2D(); + break; + } + case 3: { + transformDouble3D(); + break; + } + default: { + throw(new IllegalStateException()); + } + } + break; + } + case REALINPUT: { + switch (dimensions) { + case 1: { + transformRealDouble1D(); + break; + } + case 2: { + switch (firstDimension) { + case 1: { + transformRealDouble2DRowFirst(); + break; + } + case 2: { + transformRealDouble2DColumnFirst(); + break; + } + default: { + throw(new IllegalStateException()); + } + } + break; + } + case 3: { + switch (firstDimension) { + case 1: { + transformRealDouble3DRowFirst(); + break; + } + case 2: { + transformRealDouble3DColumnFirst(); + break; + } + case 3: { + transformRealDouble3DAcrossFirst(); + break; + } + default: { + throw(new IllegalStateException()); + } + } + break; + } + default: { + throw(new IllegalStateException()); + } + } + break; + } + } +} /* end transformDouble */ + +/*------------------------------------------------------------------*/ +private void transformDouble1D ( +) { + final FFTSetup fft = FFTSetup.transforms.get(width); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + new DFTLength2Double(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH3: { + new DFTLength3Double(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH4: { + new DFTLength4Double(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH5: { + new DFTLength5Double(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH6: { + new DFTLength6Double(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH8: { + new DFTLength8Double(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderDouble(reDataDouble, imDataDouble, 0, 1, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2Double(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } +} /* end transformDouble1D */ + +/*------------------------------------------------------------------*/ +private void transformDouble2D ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + // Rows + final FFTSetup fft1 = FFTSetup.transforms.get(width); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + p += K1; + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.ruritanian, fft1.chinese, fft1.K1)); + p += K1; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength2Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH3: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength3Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH4: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength4Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH5: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength5Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH6: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength6Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH8: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength8Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1)); + p += K1; + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, p, 1, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + p += K1; + } + break; + } + case RADER: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + p += K1; + } + break; + } + case RADIX2: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + p += K1; + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + p += K1; + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Columns + final FFTSetup fft2 = FFTSetup.transforms.get(height); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft2.algorithm) { + case BRUTEFORCE: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + } + break; + } + case COPRIMEFACTOR: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength2Double( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH3: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength3Double( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH4: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength4Double( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH5: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength5Double( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH6: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength6Double( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH8: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength8Double( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case MIXEDRADIX: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, fft2.K1)); + } + break; + } + case PADDEDRADER: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, k1, K1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + } + break; + } + case RADER: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + } + break; + } + case RADIX2: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + } + break; + } + case SPLITRADIX: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } +} /* end transformDouble2D */ + +/*------------------------------------------------------------------*/ +private void transformDouble3D ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int K3 = depth.intValue(); + final int K1K2 = K1 * K2; + // Rows + final FFTSetup fft1 = FFTSetup.transforms.get(width); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + p += K1; + } + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.ruritanian, fft1.chinese, fft1.K1)); + p += K1; + } + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength2Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + } + break; + } + case LENGTH3: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength3Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + } + break; + } + case LENGTH4: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength4Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + } + break; + } + case LENGTH5: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength5Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + } + break; + } + case LENGTH6: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength6Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + } + break; + } + case LENGTH8: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength8Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1)); + p += K1; + } + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, p, 1, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + p += K1; + } + } + break; + } + case RADER: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + p += K1; + } + } + break; + } + case RADIX2: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + p += K1; + } + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + p += K1; + } + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Columns + final FFTSetup fft2 = FFTSetup.transforms.get(height); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft2.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + } + p += K1K2; + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + } + p += K1K2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength2Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH3: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength3Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH4: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength4Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH5: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength5Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH6: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength6Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH8: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength8Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, fft2.K1)); + } + p += K1K2; + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, p + k1, K1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + } + p += K1K2; + } + break; + } + case RADER: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + } + p += K1K2; + } + break; + } + case RADIX2: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + } + p += K1K2; + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + } + p += K1K2; + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Across + final FFTSetup fft3 = FFTSetup.transforms.get(depth); + final ExecutorService executor3 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft3.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, K1K2, + fft3.reUnitRootDouble, fft3.imUnitRootDouble)); + p++; + } + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, K1K2, + fft3.ruritanian, fft3.chinese, fft3.K1)); + p++; + } + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength2Double( + reDataDouble, imDataDouble, p, K1K2)); + p++; + } + } + break; + } + case LENGTH3: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength3Double( + reDataDouble, imDataDouble, p, K1K2)); + p++; + } + } + break; + } + case LENGTH4: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength4Double( + reDataDouble, imDataDouble, p, K1K2)); + p++; + } + } + break; + } + case LENGTH5: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength5Double( + reDataDouble, imDataDouble, p, K1K2)); + p++; + } + } + break; + } + case LENGTH6: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength6Double( + reDataDouble, imDataDouble, p, K1K2)); + p++; + } + } + break; + } + case LENGTH8: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength8Double( + reDataDouble, imDataDouble, p, K1K2)); + p++; + } + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, K1K2, + fft3.reUnitRootDouble, fft3.imUnitRootDouble, fft3.K1)); + p++; + } + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, p, K1K2, + fft3.reConvolverDouble, fft3.imConvolverDouble, + fft3.modular, fft3.inverseModular)); + p++; + } + } + break; + } + case RADER: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, K1K2, + fft3.reConvolverDouble, fft3.imConvolverDouble, + fft3.modular, fft3.inverseModular)); + p++; + } + } + break; + } + case RADIX2: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, K1K2, + fft3.reUnitRootDouble, fft3.imUnitRootDouble)); + p++; + } + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, K1K2, + fft3.reUnitRootDouble, fft3.imUnitRootDouble)); + p++; + } + } + break; + } + } + try { + executor3.shutdown(); + executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } +} /* end transformDouble3D */ + +/*------------------------------------------------------------------*/ +private void transformFloat ( + final InputDataType inputDataType +) { + switch (inputDataType) { + case COMPLEXINPUT: { + switch (dimensions) { + case 1: { + transformFloat1D(); + break; + } + case 2: { + transformFloat2D(); + break; + } + case 3: { + transformFloat3D(); + break; + } + default: { + throw(new IllegalStateException()); + } + } + break; + } + case REALINPUT: { + switch (dimensions) { + case 1: { + transformRealFloat1D(); + break; + } + case 2: { + switch (firstDimension) { + case 1: { + transformRealFloat2DRowFirst(); + break; + } + case 2: { + transformRealFloat2DColumnFirst(); + break; + } + default: { + throw(new IllegalStateException()); + } + } + break; + } + case 3: { + switch (firstDimension) { + case 1: { + transformRealFloat3DRowFirst(); + break; + } + case 2: { + transformRealFloat3DColumnFirst(); + break; + } + case 3: { + transformRealFloat3DAcrossFirst(); + break; + } + default: { + throw(new IllegalStateException()); + } + } + break; + } + default: { + throw(new IllegalStateException()); + } + } + break; + } + } +} /* end transformFloat */ + +/*------------------------------------------------------------------*/ +private void transformFloat1D ( +) { + final FFTSetup fft = FFTSetup.transforms.get(width); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + new DFTLength2Float(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH3: { + new DFTLength3Float(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH4: { + new DFTLength4Float(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH5: { + new DFTLength5Float(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH6: { + new DFTLength6Float(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH8: { + new DFTLength8Float(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderFloat(reDataFloat, imDataFloat, 0, 1, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2Float(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } +} /* end transformFloat1D */ + +/*------------------------------------------------------------------*/ +private void transformFloat2D ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + // Rows + final FFTSetup fft1 = FFTSetup.transforms.get(width); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + p += K1; + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.ruritanian, fft1.chinese, fft1.K1)); + p += K1; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength2Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH3: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength3Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH4: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength4Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH5: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength5Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH6: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength6Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH8: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength8Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat, fft1.K1)); + p += K1; + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, p, 1, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + p += K1; + } + break; + } + case RADER: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + p += K1; + } + break; + } + case RADIX2: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + p += K1; + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + p += K1; + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Columns + final FFTSetup fft2 = FFTSetup.transforms.get(height); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft2.algorithm) { + case BRUTEFORCE: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + } + break; + } + case COPRIMEFACTOR: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength2Float( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH3: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength3Float( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH4: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength4Float( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH5: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength5Float( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH6: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength6Float( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH8: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength8Float( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case MIXEDRADIX: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, fft2.K1)); + } + break; + } + case PADDEDRADER: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, k1, K1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + } + break; + } + case RADER: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + } + break; + } + case RADIX2: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + } + break; + } + case SPLITRADIX: { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } +} /* end transformFloat2D */ + +/*------------------------------------------------------------------*/ +private void transformFloat3D ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int K3 = depth.intValue(); + final int K1K2 = K1 * K2; + // Rows + final FFTSetup fft1 = FFTSetup.transforms.get(width); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + p += K1; + } + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.ruritanian, fft1.chinese, fft1.K1)); + p += K1; + } + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength2Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + } + break; + } + case LENGTH3: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength3Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + } + break; + } + case LENGTH4: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength4Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + } + break; + } + case LENGTH5: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength5Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + } + break; + } + case LENGTH6: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength6Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + } + break; + } + case LENGTH8: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTLength8Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat, + fft1.K1)); + p += K1; + } + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, p, 1, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + p += K1; + } + } + break; + } + case RADER: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + p += K1; + } + } + break; + } + case RADIX2: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + p += K1; + } + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor1.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + p += K1; + } + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Columns + final FFTSetup fft2 = FFTSetup.transforms.get(height); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft2.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + } + p += K1K2; + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + } + p += K1K2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength2Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH3: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength3Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH4: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength4Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH5: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength5Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH6: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength6Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH8: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTLength8Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, fft2.K1)); + } + p += K1K2; + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, p + k1, K1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + } + p += K1K2; + } + break; + } + case RADER: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + } + p += K1K2; + } + break; + } + case RADIX2: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + } + p += K1K2; + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor2.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + } + p += K1K2; + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Across + final FFTSetup fft3 = FFTSetup.transforms.get(depth); + final ExecutorService executor3 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft3.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, K1K2, + fft3.reUnitRootFloat, fft3.imUnitRootFloat)); + p++; + } + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, K1K2, + fft3.ruritanian, fft3.chinese, fft3.K1)); + p++; + } + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength2Float( + reDataFloat, imDataFloat, p, K1K2)); + p++; + } + } + break; + } + case LENGTH3: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength3Float( + reDataFloat, imDataFloat, p, K1K2)); + p++; + } + } + break; + } + case LENGTH4: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength4Float( + reDataFloat, imDataFloat, p, K1K2)); + p++; + } + } + break; + } + case LENGTH5: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength5Float( + reDataFloat, imDataFloat, p, K1K2)); + p++; + } + } + break; + } + case LENGTH6: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength6Float( + reDataFloat, imDataFloat, p, K1K2)); + p++; + } + } + break; + } + case LENGTH8: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength8Float( + reDataFloat, imDataFloat, p, K1K2)); + p++; + } + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, K1K2, + fft3.reUnitRootFloat, fft3.imUnitRootFloat, fft3.K1)); + p++; + } + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, p, K1K2, + fft3.reConvolverFloat, fft3.imConvolverFloat, + fft3.modular, fft3.inverseModular)); + p++; + } + } + break; + } + case RADER: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, K1K2, + fft3.reConvolverFloat, fft3.imConvolverFloat, + fft3.modular, fft3.inverseModular)); + p++; + } + } + break; + } + case RADIX2: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, K1K2, + fft3.reUnitRootFloat, fft3.imUnitRootFloat)); + p++; + } + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, K1K2, + fft3.reUnitRootFloat, fft3.imUnitRootFloat)); + p++; + } + } + break; + } + } + try { + executor3.shutdown(); + executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } +} /* end transformFloat3D */ + +/*------------------------------------------------------------------*/ +private void transformRealDouble1D ( +) { + final FFTSetupReal fft = FFTSetupReal.transforms.get(width); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + } + case LENGTH1: { + imDataDouble[0] = 0.0; + break; + } + case LENGTH2: { + new DFTLength2RealDouble(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH3: { + new DFTLength3RealDouble(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH4: { + new DFTLength4RealDouble(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH5: { + new DFTLength5RealDouble(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH6: { + new DFTLength6RealDouble(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH8: { + new DFTLength8RealDouble(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealDouble(reDataDouble, imDataDouble, 0, 1, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble, + fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + int progressive = 0; + int regressive = width.intValue(); + while (++progressive < --regressive) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + } +} /* end transformRealDouble1D */ + +/*------------------------------------------------------------------*/ +private void transformRealDouble2DColumnFirst ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int halfK2 = (K2 >> 1) + 1; + int k1 = -1; + // First column for an odd width + if (1 == (K1 & 1)) { + final FFTSetupReal fft = FFTSetupReal.transforms.get(height); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case LENGTH1: { + imDataDouble[0] = 0.0; + break; + } + case LENGTH2: { + new DFTLength2RealDouble(reDataDouble, imDataDouble, + 0, K1).run(); + break; + } + case LENGTH3: { + new DFTLength3RealDouble(reDataDouble, imDataDouble, + 0, K1).run(); + break; + } + case LENGTH4: { + new DFTLength4RealDouble(reDataDouble, imDataDouble, + 0, K1).run(); + break; + } + case LENGTH5: { + new DFTLength5RealDouble(reDataDouble, imDataDouble, + 0, K1).run(); + break; + } + case LENGTH6: { + new DFTLength6RealDouble(reDataDouble, imDataDouble, + 0, K1).run(); + break; + } + case LENGTH8: { + new DFTLength8RealDouble(reDataDouble, imDataDouble, + 0, K1).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealDouble(reDataDouble, imDataDouble, 0, K1, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble, + fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + k1 = 0; + } + // Remaining columns + final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(height); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + while (++k1 < K1) { + executor1.execute(new DFTBruteForceRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + } + break; + } + case COPRIMEFACTOR: { + while (++k1 < K1) { + executor1.execute(new DFTCoprimeFactorRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft1.ruritanian, fft1.chinese, fft1.K1)); + } + break; + } + case DUOREAL: { + while (++k1 < K1) { + executor1.execute(new DFTDuoRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, k1 + 1, K1, K2)); + ++k1; + } + break; + } + case EVENREAL: { + while (++k1 < K1) { + executor1.execute(new DFTEvenRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + } + break; + } + case LENGTH1: { + while (++k1 < K1) { + imDataDouble[k1] = 0.0; + } + break; + } + case LENGTH2: { + while (++k1 < K1) { + executor1.execute(new DFTLength2RealDouble( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH3: { + while (++k1 < K1) { + executor1.execute(new DFTLength3RealDouble( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH4: { + while (++k1 < K1) { + executor1.execute(new DFTLength4RealDouble( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH5: { + while (++k1 < K1) { + executor1.execute(new DFTLength5RealDouble( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH6: { + while (++k1 < K1) { + executor1.execute(new DFTLength6RealDouble( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH8: { + while (++k1 < K1) { + executor1.execute(new DFTLength8RealDouble( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case MIXEDRADIX: { + while (++k1 < K1) { + executor1.execute(new DFTMixedRadixRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1)); + } + break; + } + case PADDEDRADER: { + while (++k1 < K1) { + executor1.execute(new DFTPaddedRaderRealDouble( + reDataDouble, imDataDouble, k1, K1, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + } + break; + } + case RADER: { + while (++k1 < K1) { + executor1.execute(new DFTRaderRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + } + break; + } + case RADIX2: { + while (++k1 < K1) { + executor1.execute(new DFTRadix2RealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft1.reUnitRootEvenDouble, fft1.imUnitRootEvenDouble, + fft1.reUnitRootOddDouble, fft1.imUnitRootOddDouble)); + } + break; + } + case SPLITRADIX: { + while (++k1 < K1) { + executor1.execute(new DFTSplitRadixRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Rows + final FFTSetup fft2 = FFTSetup.transforms.get(width); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + int p = 0; + switch (fft2.algorithm) { + case BRUTEFORCE: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + p += K1; + } + break; + } + case COPRIMEFACTOR: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + p += K1; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength2Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH3: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength3Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH4: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength4Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH5: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength5Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH6: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength6Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH8: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength8Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case MIXEDRADIX: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, fft2.K1)); + p += K1; + } + break; + } + case PADDEDRADER: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, p, 1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + p += K1; + } + break; + } + case RADER: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + p += K1; + } + break; + } + case RADIX2: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + p += K1; + } + break; + } + case SPLITRADIX: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + p += K1; + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + int progressive = K1; + int regressive = K1 * (K2 - 1); + while (progressive < regressive) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive += K1; + regressive -= K1; + } + p = K1 + 1; + int q = K1 * K2 - 1; + for (int k2 = halfK2; (k2 < K2); k2++) { + progressive = p; + regressive = q; + for (k1 = 1; (k1 < K1); k1++) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive++; + regressive--; + } + p += K1; + q -= K1; + } +} /* end transformRealDouble2DColumnFirst */ + +/*------------------------------------------------------------------*/ +private void transformRealDouble2DRowFirst ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int halfK1 = (K1 >> 1) + 1; + int p = 0; + int k2 = 0; + // First row for an odd height + if (1 == (K2 & 1)) { + final FFTSetupReal fft = FFTSetupReal.transforms.get(width); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case LENGTH1: { + imDataDouble[0] = 0.0; + break; + } + case LENGTH2: { + new DFTLength2RealDouble(reDataDouble, imDataDouble, + 0, 1).run(); + break; + } + case LENGTH3: { + new DFTLength3RealDouble(reDataDouble, imDataDouble, + 0, 1).run(); + break; + } + case LENGTH4: { + new DFTLength4RealDouble(reDataDouble, imDataDouble, + 0, 1).run(); + break; + } + case LENGTH5: { + new DFTLength5RealDouble(reDataDouble, imDataDouble, + 0, 1).run(); + break; + } + case LENGTH6: { + new DFTLength6RealDouble(reDataDouble, imDataDouble, + 0, 1).run(); + break; + } + case LENGTH8: { + new DFTLength8RealDouble(reDataDouble, imDataDouble, + 0, 1).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealDouble(reDataDouble, imDataDouble, 0, 1, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble, + fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + p = K1; + k2 = 1; + } + // Remaining rows + final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(width); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + while (k2++ < K2) { + executor1.execute(new DFTBruteForceRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + p += K1; + } + break; + } + case COPRIMEFACTOR: { + while (k2++ < K2) { + executor1.execute(new DFTCoprimeFactorRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.ruritanian, fft1.chinese, fft1.K1)); + p += K1; + } + break; + } + case DUOREAL: { + while (k2++ < K2) { + executor1.execute(new DFTDuoRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, p + K1, 1, K1)); + p += 2 * K1; + k2++; + } + break; + } + case EVENREAL: { + while (k2++ < K2) { + executor1.execute(new DFTEvenRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + p += K1; + } + break; + } + case LENGTH1: { + while (k2++ < K2) { + imDataDouble[p] = 0.0; + p += K1; + } + break; + } + case LENGTH2: { + while (k2++ < K2) { + executor1.execute(new DFTLength2RealDouble( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH3: { + while (k2++ < K2) { + executor1.execute(new DFTLength3RealDouble( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH4: { + while (k2++ < K2) { + executor1.execute(new DFTLength4RealDouble( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH5: { + while (k2++ < K2) { + executor1.execute(new DFTLength5RealDouble( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH6: { + while (k2++ < K2) { + executor1.execute(new DFTLength6RealDouble( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH8: { + while (k2++ < K2) { + executor1.execute(new DFTLength8RealDouble( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case MIXEDRADIX: { + while (k2++ < K2) { + executor1.execute(new DFTMixedRadixRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1)); + p += K1; + } + break; + } + case PADDEDRADER: { + while (k2++ < K2) { + executor1.execute(new DFTPaddedRaderRealDouble( + reDataDouble, imDataDouble, p, 1, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + p += K1; + } + break; + } + case RADER: { + while (k2++ < K2) { + executor1.execute(new DFTRaderRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + p += K1; + } + break; + } + case RADIX2: { + while (k2++ < K2) { + executor1.execute(new DFTRadix2RealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootEvenDouble, fft1.imUnitRootEvenDouble, + fft1.reUnitRootOddDouble, fft1.imUnitRootOddDouble)); + p += K1; + } + break; + } + case SPLITRADIX: { + while (k2++ < K2) { + executor1.execute(new DFTSplitRadixRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + p += K1; + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Columns + final FFTSetup fft2 = FFTSetup.transforms.get(height); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft2.algorithm) { + case BRUTEFORCE: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + } + break; + } + case COPRIMEFACTOR: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength2Double( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH3: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength3Double( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH4: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength4Double( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH5: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength5Double( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH6: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength6Double( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case LENGTH8: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength8Double( + reDataDouble, imDataDouble, k1, K1)); + } + break; + } + case MIXEDRADIX: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, fft2.K1)); + } + break; + } + case PADDEDRADER: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, k1, K1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + } + break; + } + case RADER: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + } + break; + } + case RADIX2: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + } + break; + } + case SPLITRADIX: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + int progressive = 0; + int regressive = K1; + while (++progressive < --regressive) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + } + p = K1 + 1; + int q = K1 * K2 - 1; + for (k2 = 1; (k2 < K2); k2++) { + progressive = p; + regressive = q; + for (int k1 = halfK1; (k1 < K1); k1++) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive++; + regressive--; + } + p += K1; + q -= K1; + } +} /* end transformRealDouble2DRowFirst */ + +/*------------------------------------------------------------------*/ +private void transformRealDouble3DAcrossFirst ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int K3 = depth.intValue(); + final int K1K2 = K1 * K2; + final int halfK3 = (K3 >> 1) + 1; + int k = -1; + // First across for an odd (width * height) + if (1 == (K1K2 & 1)) { + final FFTSetupReal fft = FFTSetupReal.transforms.get(depth); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1K2, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1K2, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1K2, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case LENGTH1: { + imDataDouble[0] = 0.0; + break; + } + case LENGTH2: { + new DFTLength2RealDouble(reDataDouble, imDataDouble, + 0, K1K2).run(); + break; + } + case LENGTH3: { + new DFTLength3RealDouble(reDataDouble, imDataDouble, + 0, K1K2).run(); + break; + } + case LENGTH4: { + new DFTLength4RealDouble(reDataDouble, imDataDouble, + 0, K1K2).run(); + break; + } + case LENGTH5: { + new DFTLength5RealDouble(reDataDouble, imDataDouble, + 0, K1K2).run(); + break; + } + case LENGTH6: { + new DFTLength6RealDouble(reDataDouble, imDataDouble, + 0, K1K2).run(); + break; + } + case LENGTH8: { + new DFTLength8RealDouble(reDataDouble, imDataDouble, + 0, K1K2).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1K2, + fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealDouble( + reDataDouble, imDataDouble, 0, K1K2, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1K2, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1K2, + fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble, + fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1K2, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + k = 0; + } + // Remaining across + final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(depth); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + while (++k < K1K2) { + executor1.execute(new DFTBruteForceRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k, K1K2, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + } + break; + } + case COPRIMEFACTOR: { + while (++k < K1K2) { + executor1.execute(new DFTCoprimeFactorRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k, K1K2, + fft1.ruritanian, fft1.chinese, fft1.K1)); + } + break; + } + case DUOREAL: { + while (++k < K1K2) { + executor1.execute(new DFTDuoRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k, k + 1, K1K2, K3)); + k++; + } + break; + } + case EVENREAL: { + while (++k < K1K2) { + executor1.execute(new DFTEvenRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k, K1K2, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + } + break; + } + case LENGTH1: { + while (++k < K1K2) { + imDataDouble[k] = 0.0; + } + break; + } + case LENGTH2: { + while (++k < K1K2) { + executor1.execute(new DFTLength2RealDouble( + reDataDouble, imDataDouble, k, K1K2)); + } + break; + } + case LENGTH3: { + while (++k < K1K2) { + executor1.execute(new DFTLength3RealDouble( + reDataDouble, imDataDouble, k, K1K2)); + } + break; + } + case LENGTH4: { + while (++k < K1K2) { + executor1.execute(new DFTLength4RealDouble( + reDataDouble, imDataDouble, k, K1K2)); + } + break; + } + case LENGTH5: { + while (++k < K1K2) { + executor1.execute(new DFTLength5RealDouble( + reDataDouble, imDataDouble, k, K1K2)); + } + break; + } + case LENGTH6: { + while (++k < K1K2) { + executor1.execute(new DFTLength6RealDouble( + reDataDouble, imDataDouble, k, K1K2)); + } + break; + } + case LENGTH8: { + while (++k < K1K2) { + executor1.execute(new DFTLength8RealDouble( + reDataDouble, imDataDouble, k, K1K2)); + } + break; + } + case MIXEDRADIX: { + while (++k < K1K2) { + executor1.execute(new DFTMixedRadixRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k, K1K2, + fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1)); + } + break; + } + case PADDEDRADER: { + while (++k < K1K2) { + executor1.execute(new DFTPaddedRaderRealDouble( + reDataDouble, imDataDouble, k, K1K2, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + } + break; + } + case RADER: { + while (++k < K1K2) { + executor1.execute(new DFTRaderRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k, K1K2, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + } + break; + } + case RADIX2: { + while (++k < K1K2) { + executor1.execute(new DFTRadix2RealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k, K1K2, + fft1.reUnitRootEvenDouble, fft1.imUnitRootEvenDouble, + fft1.reUnitRootOddDouble, fft1.imUnitRootOddDouble)); + } + break; + } + case SPLITRADIX: { + while (++k < K1K2) { + executor1.execute(new DFTSplitRadixRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k, K1K2, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Rows + final FFTSetup fft2 = FFTSetup.transforms.get(width); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft2.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + p += K1; + } + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + p += K1; + } + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTLength2Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + } + break; + } + case LENGTH3: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTLength3Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + } + break; + } + case LENGTH4: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTLength4Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + } + break; + } + case LENGTH5: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTLength5Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + } + break; + } + case LENGTH6: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTLength6Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + } + break; + } + case LENGTH8: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTLength8Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, + fft2.K1)); + p += K1; + } + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, p, 1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + p += K1; + } + } + break; + } + case RADER: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + p += K1; + } + } + break; + } + case RADIX2: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + p += K1; + } + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + p += K1; + } + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Columns + final FFTSetup fft3 = FFTSetup.transforms.get(height); + final ExecutorService executor3 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft3.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft3.reUnitRootDouble, fft3.imUnitRootDouble)); + } + p += K1K2; + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft3.ruritanian, fft3.chinese, fft3.K1)); + } + p += K1K2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength2Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH3: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength3Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH4: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength4Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH5: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength5Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH6: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength6Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH8: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength8Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft3.reUnitRootDouble, fft3.imUnitRootDouble, fft3.K1)); + } + p += K1K2; + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, p + k1, K1, + fft3.reConvolverDouble, fft3.imConvolverDouble, + fft3.modular, fft3.inverseModular)); + } + p += K1K2; + } + break; + } + case RADER: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft3.reConvolverDouble, fft3.imConvolverDouble, + fft3.modular, fft3.inverseModular)); + } + p += K1K2; + } + break; + } + case RADIX2: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft3.reUnitRootDouble, fft3.imUnitRootDouble)); + } + p += K1K2; + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft3.reUnitRootDouble, fft3.imUnitRootDouble)); + } + p += K1K2; + } + break; + } + } + try { + executor3.shutdown(); + executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + int progressive = K1K2; + int regressive = dataLength - K1K2; + while (progressive < regressive) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive += K1K2; + regressive -= K1K2; + } + int p = K1K2 + K1; + int q = dataLength - K1; + for (int k3 = halfK3; (k3 < K3); k3++) { + progressive = p; + regressive = q; + for (int k2 = 1; (k2 < K2); k2++) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive += K1; + regressive -= K1; + } + p += K1K2; + q -= K1K2; + } + p = K1K2 + 1; + q = K1K2 * (K3 - 1) + K1 - 1; + for (int k3 = halfK3; (k3 < K3); k3++) { + progressive = p; + regressive = q; + for (int k1 = 1; (k1 < K1); k1++) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive++; + regressive--; + } + p += K1K2; + q -= K1K2; + } + progressive = K1K2 + K1 + 1; + regressive = dataLength - 1; + for (int k3 = halfK3; (k3 < K3); k3++) { + for (int k2 = 1; (k2 < K2); k2++) { + for (int k1 = 1; (k1 < K1); k1++) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive++; + regressive--; + } + progressive++; + regressive--; + } + progressive += K1; + regressive -= K1; + } +} /* end transformRealDouble3DAcrossFirst */ + +/*------------------------------------------------------------------*/ +private void transformRealDouble3DColumnFirst ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int K3 = depth.intValue(); + final int K1K2 = K1 * K2; + final int k1K2 = K1K2 - K1; + final int K1K3 = K1 * K3; + final int halfK2 = (K2 >> 1) + 1; + int k = -1; + // First column for an odd (width * depth) + if (1 == (K1K3 & 1)) { + final FFTSetupReal fft = FFTSetupReal.transforms.get(height); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case LENGTH1: { + imDataDouble[0] = 0.0; + break; + } + case LENGTH2: { + new DFTLength2RealDouble( + reDataDouble, imDataDouble, 0, K1).run(); + break; + } + case LENGTH3: { + new DFTLength3RealDouble( + reDataDouble, imDataDouble, 0, K1).run(); + break; + } + case LENGTH4: { + new DFTLength4RealDouble( + reDataDouble, imDataDouble, 0, K1).run(); + break; + } + case LENGTH5: { + new DFTLength5RealDouble( + reDataDouble, imDataDouble, 0, K1).run(); + break; + } + case LENGTH6: { + new DFTLength6RealDouble( + reDataDouble, imDataDouble, 0, K1).run(); + break; + } + case LENGTH8: { + new DFTLength8RealDouble( + reDataDouble, imDataDouble, 0, K1).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealDouble(reDataDouble, imDataDouble, 0, K1, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble, + fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, K1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + k = 0; + } + // Remaining columns + final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(height); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + while (++k < K1K3) { + executor1.execute(new DFTBruteForceRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + } + break; + } + case COPRIMEFACTOR: { + while (++k < K1K3) { + executor1.execute(new DFTCoprimeFactorRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1, + fft1.ruritanian, fft1.chinese, fft1.K1)); + } + break; + } + case DUOREAL: { + while (++k < K1K3) { + final int p = k1K2 * (k / K1) + k; + k++; + final int q = k1K2 * (k / K1) + k; + executor1.execute(new DFTDuoRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, q, K1, K2)); + } + break; + } + case EVENREAL: { + while (++k < K1K3) { + executor1.execute(new DFTEvenRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + } + break; + } + case LENGTH1: { + while (++k < K1K3) { + imDataDouble[k] = 0.0; + } + break; + } + case LENGTH2: { + while (++k < K1K3) { + executor1.execute(new DFTLength2RealDouble( + reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1)); + } + break; + } + case LENGTH3: { + while (++k < K1K3) { + executor1.execute(new DFTLength3RealDouble( + reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1)); + } + break; + } + case LENGTH4: { + while (++k < K1K3) { + executor1.execute(new DFTLength4RealDouble( + reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1)); + } + break; + } + case LENGTH5: { + while (++k < K1K3) { + executor1.execute(new DFTLength5RealDouble( + reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1)); + } + break; + } + case LENGTH6: { + while (++k < K1K3) { + executor1.execute(new DFTLength6RealDouble( + reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1)); + } + break; + } + case LENGTH8: { + while (++k < K1K3) { + executor1.execute(new DFTLength8RealDouble( + reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1)); + } + break; + } + case MIXEDRADIX: { + while (++k < K1K3) { + executor1.execute(new DFTMixedRadixRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1)); + } + break; + } + case PADDEDRADER: { + while (++k < K1K3) { + executor1.execute(new DFTPaddedRaderRealDouble( + reDataDouble, imDataDouble, k1K2 * (k / K1) + k, K1, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + } + break; + } + case RADER: { + while (++k < K1K3) { + executor1.execute(new DFTRaderRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + } + break; + } + case RADIX2: { + while (++k < K1K3) { + executor1.execute(new DFTRadix2RealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1, + fft1.reUnitRootEvenDouble, fft1.imUnitRootEvenDouble, + fft1.reUnitRootOddDouble, fft1.imUnitRootOddDouble)); + } + break; + } + case SPLITRADIX: { + while (++k < K1K3) { + executor1.execute(new DFTSplitRadixRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, k1K2 * (k / K1) + k, K1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Rows + final FFTSetup fft2 = FFTSetup.transforms.get(width); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + final int skippedRows = (K2 - halfK2) * K1; + switch (fft2.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + p += K1; + } + p += skippedRows; + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + p += K1; + } + p += skippedRows; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength2Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + p += skippedRows; + } + break; + } + case LENGTH3: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength3Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + p += skippedRows; + } + break; + } + case LENGTH4: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength4Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + p += skippedRows; + } + break; + } + case LENGTH5: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength5Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + p += skippedRows; + } + break; + } + case LENGTH6: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength6Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + p += skippedRows; + } + break; + } + case LENGTH8: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength8Double( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + p += skippedRows; + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, + fft2.K1)); + p += K1; + } + p += skippedRows; + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, p, 1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + p += K1; + } + p += skippedRows; + } + break; + } + case RADER: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + p += K1; + } + p += skippedRows; + } + break; + } + case RADIX2: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + p += K1; + } + p += skippedRows; + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + p += K1; + } + p += skippedRows; + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Across + final FFTSetup fft3 = FFTSetup.transforms.get(depth); + final ExecutorService executor3 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft3.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, K1K2, + fft3.reUnitRootDouble, fft3.imUnitRootDouble)); + p++; + } + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, K1K2, + fft3.ruritanian, fft3.chinese, fft3.K1)); + p++; + } + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength2Double( + reDataDouble, imDataDouble, p, K1K2)); + p++; + } + } + break; + } + case LENGTH3: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength3Double( + reDataDouble, imDataDouble, p, K1K2)); + p++; + } + } + break; + } + case LENGTH4: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength4Double( + reDataDouble, imDataDouble, p, K1K2)); + p++; + } + } + break; + } + case LENGTH5: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength5Double( + reDataDouble, imDataDouble, p, K1K2)); + p++; + } + } + break; + } + case LENGTH6: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength6Double( + reDataDouble, imDataDouble, p, K1K2)); + p++; + } + } + break; + } + case LENGTH8: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength8Double( + reDataDouble, imDataDouble, p, K1K2)); + p++; + } + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, K1K2, + fft3.reUnitRootDouble, fft3.imUnitRootDouble, fft3.K1)); + p++; + } + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, p, K1K2, + fft3.reConvolverDouble, fft3.imConvolverDouble, + fft3.modular, fft3.inverseModular)); + p++; + } + } + break; + } + case RADER: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, K1K2, + fft3.reConvolverDouble, fft3.imConvolverDouble, + fft3.modular, fft3.inverseModular)); + p++; + } + } + break; + } + case RADIX2: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, K1K2, + fft3.reUnitRootDouble, fft3.imUnitRootDouble)); + p++; + } + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, K1K2, + fft3.reUnitRootDouble, fft3.imUnitRootDouble)); + p++; + } + } + break; + } + } + try { + executor3.shutdown(); + executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + int progressive = K1; + int regressive = K1K2 - K1; + while (progressive < regressive) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive += K1; + regressive -= K1; + } + int p = K1 + 1; + int q = K1K2 - 1; + for (int k2 = halfK2; (k2 < K2); k2++) { + progressive = p; + regressive = q; + for (int k1 = 1; (k1 < K1); k1++) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive++; + regressive--; + } + p += K1; + q -= K1; + } + p = K1K2 + K1; + q = dataLength - K1; + for (int k3 = 1; (k3 < K3); k3++) { + progressive = p; + regressive = q; + for (int k2 = halfK2; (k2 < K2); k2++) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive += K1; + regressive -= K1; + } + p += K1K2; + q -= K1K2; + } + progressive = K1K2 + K1 + 1; + regressive = dataLength - 1; + for (int k3 = 1; (k3 < K3); k3++) { + for (int k2 = halfK2; (k2 < K2); k2++) { + for (int k1 = 1; (k1 < K1); k1++) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive++; + regressive--; + } + progressive++; + regressive--; + } + progressive += halfK2 * K1; + regressive -= halfK2 * K1; + } +} /* end transformRealDouble3DColumnFirst */ + +/*------------------------------------------------------------------*/ +private void transformRealDouble3DRowFirst ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int K3 = depth.intValue(); + final int K1K2 = K1 * K2; + final int K2K3 = K2 * K3; + final int halfK1 = (K1 >> 1) + 1; + int k = 0; + int p = 0; + // First row for an odd (height * depth) + if (1 == (K2K3 & 1)) { + final FFTSetupReal fft = FFTSetupReal.transforms.get(width); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + case LENGTH1: { + imDataDouble[0] = 0.0; + break; + } + case LENGTH2: { + new DFTLength2RealDouble(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH3: { + new DFTLength3RealDouble(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH4: { + new DFTLength4RealDouble(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH5: { + new DFTLength5RealDouble(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH6: { + new DFTLength6RealDouble(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case LENGTH8: { + new DFTLength8RealDouble(reDataDouble, imDataDouble, 0, 1).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealDouble(reDataDouble, imDataDouble, 0, 1, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reConvolverDouble, fft.imConvolverDouble, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootEvenDouble, fft.imUnitRootEvenDouble, + fft.reUnitRootOddDouble, fft.imUnitRootOddDouble).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealDouble(reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, 0, 1, + fft.reUnitRootDouble, fft.imUnitRootDouble).run(); + break; + } + } + k = 1; + p = K1; + } + // Remaining rows + final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(width); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + while (k++ < K2K3) { + executor1.execute(new DFTBruteForceRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + p += K1; + } + break; + } + case COPRIMEFACTOR: { + while (k++ < K2K3) { + executor1.execute(new DFTCoprimeFactorRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.ruritanian, fft1.chinese, fft1.K1)); + p += K1; + } + break; + } + case DUOREAL: { + while (k++ < K2K3) { + executor1.execute(new DFTDuoRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, p + K1, 1, K1)); + p += 2 * K1; + k++; + } + break; + } + case EVENREAL: { + while (k++ < K2K3) { + executor1.execute(new DFTEvenRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + p += K1; + } + break; + } + case LENGTH1: { + while (k++ < K2K3) { + imDataDouble[p] = 0.0; + p += K1; + } + break; + } + case LENGTH2: { + while (k++ < K2K3) { + executor1.execute(new DFTLength2RealDouble( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH3: { + while (k++ < K2K3) { + executor1.execute(new DFTLength3RealDouble( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH4: { + while (k++ < K2K3) { + executor1.execute(new DFTLength4RealDouble( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH5: { + while (k++ < K2K3) { + executor1.execute(new DFTLength5RealDouble( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH6: { + while (k++ < K2K3) { + executor1.execute(new DFTLength6RealDouble( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case LENGTH8: { + while (k++ < K2K3) { + executor1.execute(new DFTLength8RealDouble( + reDataDouble, imDataDouble, p, 1)); + p += K1; + } + break; + } + case MIXEDRADIX: { + while (k++ < K2K3) { + executor1.execute(new DFTMixedRadixRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble, fft1.K1)); + p += K1; + } + break; + } + case PADDEDRADER: { + while (k++ < K2K3) { + executor1.execute(new DFTPaddedRaderRealDouble( + reDataDouble, imDataDouble, p, 1, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + p += K1; + } + break; + } + case RADER: { + while (k++ < K2K3) { + executor1.execute(new DFTRaderRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reConvolverDouble, fft1.imConvolverDouble, + fft1.modular, fft1.inverseModular)); + p += K1; + } + break; + } + case RADIX2: { + while (k++ < K2K3) { + executor1.execute(new DFTRadix2RealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootEvenDouble, fft1.imUnitRootEvenDouble, + fft1.reUnitRootOddDouble, fft1.imUnitRootOddDouble)); + p += K1; + } + break; + } + case SPLITRADIX: { + while (k++ < K2K3) { + executor1.execute(new DFTSplitRadixRealDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p, 1, + fft1.reUnitRootDouble, fft1.imUnitRootDouble)); + p += K1; + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Columns + final FFTSetup fft2 = FFTSetup.transforms.get(height); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + p = 0; + switch (fft2.algorithm) { + case BRUTEFORCE: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + } + p += K1K2; + } + break; + } + case COPRIMEFACTOR: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + } + p += K1K2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength2Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH3: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength3Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH4: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength4Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH5: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength5Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH6: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength6Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH8: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength8Double( + reDataDouble, imDataDouble, p + k1, K1)); + } + p += K1K2; + } + break; + } + case MIXEDRADIX: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble, fft2.K1)); + } + p += K1K2; + } + break; + } + case PADDEDRADER: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, p + k1, K1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + } + p += K1K2; + } + break; + } + case RADER: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft2.reConvolverDouble, fft2.imConvolverDouble, + fft2.modular, fft2.inverseModular)); + } + p += K1K2; + } + break; + } + case RADIX2: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + } + p += K1K2; + } + break; + } + case SPLITRADIX: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1, + fft2.reUnitRootDouble, fft2.imUnitRootDouble)); + } + p += K1K2; + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Across + final FFTSetup fft3 = FFTSetup.transforms.get(depth); + final ExecutorService executor3 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + p = 0; + switch (fft3.algorithm) { + case BRUTEFORCE: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTBruteForceDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1K2, + fft3.reUnitRootDouble, fft3.imUnitRootDouble)); + } + p += K1; + } + break; + } + case COPRIMEFACTOR: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTCoprimeFactorDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1K2, + fft3.ruritanian, fft3.chinese, fft3.K1)); + } + p += K1; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTLength2Double( + reDataDouble, imDataDouble, p + k1, K1K2)); + } + p += K1; + } + break; + } + case LENGTH3: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTLength3Double( + reDataDouble, imDataDouble, p + k1, K1K2)); + } + p += K1; + } + break; + } + case LENGTH4: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTLength4Double( + reDataDouble, imDataDouble, p + k1, K1K2)); + } + p += K1; + } + break; + } + case LENGTH5: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTLength5Double( + reDataDouble, imDataDouble, p + k1, K1K2)); + } + p += K1; + } + break; + } + case LENGTH6: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTLength6Double( + reDataDouble, imDataDouble, p + k1, K1K2)); + } + p += K1; + } + break; + } + case LENGTH8: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTLength8Double( + reDataDouble, imDataDouble, p + k1, K1K2)); + } + p += K1; + } + break; + } + case MIXEDRADIX: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTMixedRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1K2, + fft3.reUnitRootDouble, fft3.imUnitRootDouble, fft3.K1)); + } + p += K1; + } + break; + } + case PADDEDRADER: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTPaddedRaderDouble( + reDataDouble, imDataDouble, p + k1, K1K2, + fft3.reConvolverDouble, fft3.imConvolverDouble, + fft3.modular, fft3.inverseModular)); + } + p += K1; + } + break; + } + case RADER: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTRaderDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1K2, + fft3.reConvolverDouble, fft3.imConvolverDouble, + fft3.modular, fft3.inverseModular)); + } + p += K1; + } + break; + } + case RADIX2: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTRadix2Double( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1K2, + fft3.reUnitRootDouble, fft3.imUnitRootDouble)); + } + p += K1; + } + break; + } + case SPLITRADIX: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTSplitRadixDouble( + reDataDouble, imDataDouble, + reBufferDouble, imBufferDouble, p + k1, K1K2, + fft3.reUnitRootDouble, fft3.imUnitRootDouble)); + } + p += K1; + } + break; + } + } + try { + executor3.shutdown(); + executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + int progressive = 0; + int regressive = K1; + while (++progressive < --regressive) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + } + p = K1 + 1; + int q = K1K2 - 1; + for (int k2 = 1; (k2 < K2); k2++) { + progressive = p; + regressive = q; + for (int k1 = halfK1; (k1 < K1); k1++) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive++; + regressive--; + } + p += K1; + q -= K1; + } + p = K1K2 + 1; + q = K1K2 * (K3 - 1) + K1 - 1; + for (int k3 = 1; (k3 < K3); k3++) { + progressive = p; + regressive = q; + for (int k1 = halfK1; (k1 < K1); k1++) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive++; + regressive--; + } + p += K1K2; + q -= K1K2; + } + progressive = K1K2 + K1 + 1; + regressive = dataLength - 1; + for (int k3 = 1; (k3 < K3); k3++) { + for (int k2 = 1; (k2 < K2); k2++) { + for (int k1 = halfK1; (k1 < K1); k1++) { + reDataDouble[regressive] = reDataDouble[progressive]; + imDataDouble[regressive] = -imDataDouble[progressive]; + progressive++; + regressive--; + } + progressive += halfK1; + regressive -= halfK1; + } + progressive += K1; + regressive -= K1; + } +} /* end transformRealDouble3DRowFirst */ + +/*------------------------------------------------------------------*/ +private void transformRealFloat1D ( +) { + final FFTSetupReal fft = FFTSetupReal.transforms.get(width); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + } + case LENGTH1: { + imDataFloat[0] = 0.0F; + break; + } + case LENGTH2: { + new DFTLength2RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH3: { + new DFTLength3RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH4: { + new DFTLength4RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH5: { + new DFTLength5RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH6: { + new DFTLength6RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH8: { + new DFTLength8RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealFloat(reDataFloat, imDataFloat, 0, 1, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat, + fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + int progressive = 0; + int regressive = width.intValue(); + while (++progressive < --regressive) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + } +} /* end transformRealFloat1D */ + +/*------------------------------------------------------------------*/ +private void transformRealFloat2DColumnFirst ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int halfK2 = (K2 >> 1) + 1; + int k1 = -1; + // First column for an odd width + if (1 == (K1 & 1)) { + final FFTSetupReal fft = FFTSetupReal.transforms.get(height); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case LENGTH1: { + imDataFloat[0] = 0.0F; + break; + } + case LENGTH2: { + new DFTLength2RealFloat(reDataFloat, imDataFloat, 0, K1).run(); + break; + } + case LENGTH3: { + new DFTLength3RealFloat(reDataFloat, imDataFloat, 0, K1).run(); + break; + } + case LENGTH4: { + new DFTLength4RealFloat(reDataFloat, imDataFloat, 0, K1).run(); + break; + } + case LENGTH5: { + new DFTLength5RealFloat(reDataFloat, imDataFloat, 0, K1).run(); + break; + } + case LENGTH6: { + new DFTLength6RealFloat(reDataFloat, imDataFloat, 0, K1).run(); + break; + } + case LENGTH8: { + new DFTLength8RealFloat(reDataFloat, imDataFloat, 0, K1).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealFloat(reDataFloat, imDataFloat, 0, K1, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat, + fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + k1 = 0; + } + // Remaining columns + final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(height); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + while (++k1 < K1) { + executor1.execute(new DFTBruteForceRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + } + break; + } + case COPRIMEFACTOR: { + while (++k1 < K1) { + executor1.execute(new DFTCoprimeFactorRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft1.ruritanian, fft1.chinese, fft1.K1)); + } + break; + } + case DUOREAL: { + while (++k1 < K1) { + executor1.execute(new DFTDuoRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, k1 + 1, K1, K2)); + ++k1; + } + break; + } + case EVENREAL: { + while (++k1 < K1) { + executor1.execute(new DFTEvenRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + } + break; + } + case LENGTH1: { + while (++k1 < K1) { + imDataFloat[k1] = 0.0F; + } + break; + } + case LENGTH2: { + while (++k1 < K1) { + executor1.execute(new DFTLength2RealFloat( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH3: { + while (++k1 < K1) { + executor1.execute(new DFTLength3RealFloat( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH4: { + while (++k1 < K1) { + executor1.execute(new DFTLength4RealFloat( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH5: { + while (++k1 < K1) { + executor1.execute(new DFTLength5RealFloat( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH6: { + while (++k1 < K1) { + executor1.execute(new DFTLength6RealFloat( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH8: { + while (++k1 < K1) { + executor1.execute(new DFTLength8RealFloat( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case MIXEDRADIX: { + while (++k1 < K1) { + executor1.execute(new DFTMixedRadixRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat, fft1.K1)); + } + break; + } + case PADDEDRADER: { + while (++k1 < K1) { + executor1.execute(new DFTPaddedRaderRealFloat( + reDataFloat, imDataFloat, k1, K1, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + } + break; + } + case RADER: { + while (++k1 < K1) { + executor1.execute(new DFTRaderRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + } + break; + } + case RADIX2: { + while (++k1 < K1) { + executor1.execute(new DFTRadix2RealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft1.reUnitRootEvenFloat, fft1.imUnitRootEvenFloat, + fft1.reUnitRootOddFloat, fft1.imUnitRootOddFloat)); + } + break; + } + case SPLITRADIX: { + while (++k1 < K1) { + executor1.execute(new DFTSplitRadixRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Rows + final FFTSetup fft2 = FFTSetup.transforms.get(width); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + int p = 0; + switch (fft2.algorithm) { + case BRUTEFORCE: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + p += K1; + } + break; + } + case COPRIMEFACTOR: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + p += K1; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength2Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH3: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength3Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH4: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength4Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH5: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength5Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH6: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength6Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH8: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength8Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case MIXEDRADIX: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, fft2.K1)); + p += K1; + } + break; + } + case PADDEDRADER: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, p, 1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + p += K1; + } + break; + } + case RADER: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + p += K1; + } + break; + } + case RADIX2: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + p += K1; + } + break; + } + case SPLITRADIX: { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + p += K1; + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + int progressive = K1; + int regressive = K1 * (K2 - 1); + while (progressive < regressive) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive += K1; + regressive -= K1; + } + p = K1 + 1; + int q = K1 * K2 - 1; + for (int k2 = halfK2; (k2 < K2); k2++) { + progressive = p; + regressive = q; + for (k1 = 1; (k1 < K1); k1++) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive++; + regressive--; + } + p += K1; + q -= K1; + } +} /* transformRealFloat2DColumnFirst */ + +/*------------------------------------------------------------------*/ +private void transformRealFloat2DRowFirst ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int halfK1 = (K1 >> 1) + 1; + int p = 0; + int k2 = 0; + // First row for an odd height + if (1 == (K2 & 1)) { + final FFTSetupReal fft = FFTSetupReal.transforms.get(width); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case LENGTH1: { + imDataFloat[0] = 0.0F; + break; + } + case LENGTH2: { + new DFTLength2RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH3: { + new DFTLength3RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH4: { + new DFTLength4RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH5: { + new DFTLength5RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH6: { + new DFTLength6RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH8: { + new DFTLength8RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealFloat(reDataFloat, imDataFloat, 0, 1, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat, + fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + p = K1; + k2 = 1; + } + // Remaining rows + final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(width); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + while (k2++ < K2) { + executor1.execute(new DFTBruteForceRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + p += K1; + } + break; + } + case COPRIMEFACTOR: { + while (k2++ < K2) { + executor1.execute(new DFTCoprimeFactorRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.ruritanian, fft1.chinese, fft1.K1)); + p += K1; + } + break; + } + case DUOREAL: { + while (k2++ < K2) { + executor1.execute(new DFTDuoRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, p + K1, 1, K1)); + p += 2 * K1; + k2++; + } + break; + } + case EVENREAL: { + while (k2++ < K2) { + executor1.execute(new DFTEvenRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + p += K1; + } + break; + } + case LENGTH1: { + while (k2++ < K2) { + imDataFloat[p] = 0.0F; + p += K1; + } + break; + } + case LENGTH2: { + while (k2++ < K2) { + executor1.execute(new DFTLength2RealFloat( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH3: { + while (k2++ < K2) { + executor1.execute(new DFTLength3RealFloat( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH4: { + while (k2++ < K2) { + executor1.execute(new DFTLength4RealFloat( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH5: { + while (k2++ < K2) { + executor1.execute(new DFTLength5RealFloat( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH6: { + while (k2++ < K2) { + executor1.execute(new DFTLength6RealFloat( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH8: { + while (k2++ < K2) { + executor1.execute(new DFTLength8RealFloat( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case MIXEDRADIX: { + while (k2++ < K2) { + executor1.execute(new DFTMixedRadixRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat, fft1.K1)); + p += K1; + } + break; + } + case PADDEDRADER: { + while (k2++ < K2) { + executor1.execute(new DFTPaddedRaderRealFloat( + reDataFloat, imDataFloat, p, 1, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + p += K1; + } + break; + } + case RADER: { + while (k2++ < K2) { + executor1.execute(new DFTRaderRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + p += K1; + } + break; + } + case RADIX2: { + while (k2++ < K2) { + executor1.execute(new DFTRadix2RealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootEvenFloat, fft1.imUnitRootEvenFloat, + fft1.reUnitRootOddFloat, fft1.imUnitRootOddFloat)); + p += K1; + } + break; + } + case SPLITRADIX: { + while (k2++ < K2) { + executor1.execute(new DFTSplitRadixRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + p += K1; + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Columns + final FFTSetup fft2 = FFTSetup.transforms.get(height); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft2.algorithm) { + case BRUTEFORCE: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + } + break; + } + case COPRIMEFACTOR: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength2Float( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH3: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength3Float( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH4: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength4Float( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH5: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength5Float( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH6: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength6Float( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case LENGTH8: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength8Float( + reDataFloat, imDataFloat, k1, K1)); + } + break; + } + case MIXEDRADIX: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, fft2.K1)); + } + break; + } + case PADDEDRADER: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, k1, K1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + } + break; + } + case RADER: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + } + break; + } + case RADIX2: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + } + break; + } + case SPLITRADIX: { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + int progressive = 0; + int regressive = K1; + while (++progressive < --regressive) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + } + p = K1 + 1; + int q = K1 * K2 - 1; + for (k2 = 1; (k2 < K2); k2++) { + progressive = p; + regressive = q; + for (int k1 = halfK1; (k1 < K1); k1++) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive++; + regressive--; + } + p += K1; + q -= K1; + } +} /* end transformRealFloat2DRowFirst */ + +/*------------------------------------------------------------------*/ +private void transformRealFloat3DAcrossFirst ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int K3 = depth.intValue(); + final int K1K2 = K1 * K2; + final int halfK3 = (K3 >> 1) + 1; + int k = -1; + // First across for an odd (width * height) + if (1 == (K1K2 & 1)) { + final FFTSetupReal fft = FFTSetupReal.transforms.get(depth); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1K2, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1K2, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1K2, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case LENGTH1: { + imDataFloat[0] = 0.0F; + break; + } + case LENGTH2: { + new DFTLength2RealFloat(reDataFloat, imDataFloat, + 0, K1K2).run(); + break; + } + case LENGTH3: { + new DFTLength3RealFloat(reDataFloat, imDataFloat, + 0, K1K2).run(); + break; + } + case LENGTH4: { + new DFTLength4RealFloat(reDataFloat, imDataFloat, + 0, K1K2).run(); + break; + } + case LENGTH5: { + new DFTLength5RealFloat(reDataFloat, imDataFloat, + 0, K1K2).run(); + break; + } + case LENGTH6: { + new DFTLength6RealFloat(reDataFloat, imDataFloat, + 0, K1K2).run(); + break; + } + case LENGTH8: { + new DFTLength8RealFloat(reDataFloat, imDataFloat, + 0, K1K2).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1K2, + fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealFloat(reDataFloat, imDataFloat, 0, K1K2, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1K2, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1K2, + fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat, + fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1K2, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + k = 0; + } + // Remaining across + final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(depth); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + while (++k < K1K2) { + executor1.execute(new DFTBruteForceRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k, K1K2, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + } + break; + } + case COPRIMEFACTOR: { + while (++k < K1K2) { + executor1.execute(new DFTCoprimeFactorRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k, K1K2, + fft1.ruritanian, fft1.chinese, fft1.K1)); + } + break; + } + case DUOREAL: { + while (++k < K1K2) { + executor1.execute(new DFTDuoRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k, k + 1, K1K2, K3)); + k++; + } + break; + } + case EVENREAL: { + while (++k < K1K2) { + executor1.execute(new DFTEvenRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k, K1K2, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + } + break; + } + case LENGTH1: { + while (++k < K1K2) { + imDataFloat[k] = 0.0F; + } + break; + } + case LENGTH2: { + while (++k < K1K2) { + executor1.execute(new DFTLength2RealFloat( + reDataFloat, imDataFloat, k, K1K2)); + } + break; + } + case LENGTH3: { + while (++k < K1K2) { + executor1.execute(new DFTLength3RealFloat( + reDataFloat, imDataFloat, k, K1K2)); + } + break; + } + case LENGTH4: { + while (++k < K1K2) { + executor1.execute(new DFTLength4RealFloat( + reDataFloat, imDataFloat, k, K1K2)); + } + break; + } + case LENGTH5: { + while (++k < K1K2) { + executor1.execute(new DFTLength5RealFloat( + reDataFloat, imDataFloat, k, K1K2)); + } + break; + } + case LENGTH6: { + while (++k < K1K2) { + executor1.execute(new DFTLength6RealFloat( + reDataFloat, imDataFloat, k, K1K2)); + } + break; + } + case LENGTH8: { + while (++k < K1K2) { + executor1.execute(new DFTLength8RealFloat( + reDataFloat, imDataFloat, k, K1K2)); + } + break; + } + case MIXEDRADIX: { + while (++k < K1K2) { + executor1.execute(new DFTMixedRadixRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k, K1K2, + fft1.reUnitRootFloat, fft1.imUnitRootFloat, fft1.K1)); + } + break; + } + case PADDEDRADER: { + while (++k < K1K2) { + executor1.execute(new DFTPaddedRaderRealFloat( + reDataFloat, imDataFloat, k, K1K2, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + } + break; + } + case RADER: { + while (++k < K1K2) { + executor1.execute(new DFTRaderRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k, K1K2, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + } + break; + } + case RADIX2: { + while (++k < K1K2) { + executor1.execute(new DFTRadix2RealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k, K1K2, + fft1.reUnitRootEvenFloat, fft1.imUnitRootEvenFloat, + fft1.reUnitRootOddFloat, fft1.imUnitRootOddFloat)); + } + break; + } + case SPLITRADIX: { + while (++k < K1K2) { + executor1.execute(new DFTSplitRadixRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k, K1K2, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Rows + final FFTSetup fft2 = FFTSetup.transforms.get(width); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft2.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + p += K1; + } + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + p += K1; + } + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTLength2Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + } + break; + } + case LENGTH3: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTLength3Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + } + break; + } + case LENGTH4: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTLength4Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + } + break; + } + case LENGTH5: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTLength5Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + } + break; + } + case LENGTH6: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTLength6Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + } + break; + } + case LENGTH8: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTLength8Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, + fft2.K1)); + p += K1; + } + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, p, 1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + p += K1; + } + } + break; + } + case RADER: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + p += K1; + } + } + break; + } + case RADIX2: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + p += K1; + } + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k2 = 0; (k2 < K2); k2++) { + executor2.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + p += K1; + } + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Columns + final FFTSetup fft3 = FFTSetup.transforms.get(height); + final ExecutorService executor3 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft3.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft3.reUnitRootFloat, fft3.imUnitRootFloat)); + } + p += K1K2; + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft3.ruritanian, fft3.chinese, fft3.K1)); + } + p += K1K2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength2Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH3: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength3Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH4: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength4Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH5: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength5Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH6: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength6Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH8: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength8Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft3.reUnitRootFloat, fft3.imUnitRootFloat, fft3.K1)); + } + p += K1K2; + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, p + k1, K1, + fft3.reConvolverFloat, fft3.imConvolverFloat, + fft3.modular, fft3.inverseModular)); + } + p += K1K2; + } + break; + } + case RADER: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft3.reConvolverFloat, fft3.imConvolverFloat, + fft3.modular, fft3.inverseModular)); + } + p += K1K2; + } + break; + } + case RADIX2: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft3.reUnitRootFloat, fft3.imUnitRootFloat)); + } + p += K1K2; + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k3 = 0; (k3 < halfK3); k3++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft3.reUnitRootFloat, fft3.imUnitRootFloat)); + } + p += K1K2; + } + break; + } + } + try { + executor3.shutdown(); + executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + int progressive = K1K2; + int regressive = dataLength - K1K2; + while (progressive < regressive) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive += K1K2; + regressive -= K1K2; + } + int p = K1K2 + K1; + int q = dataLength - K1; + for (int k3 = halfK3; (k3 < K3); k3++) { + progressive = p; + regressive = q; + for (int k2 = 1; (k2 < K2); k2++) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive += K1; + regressive -= K1; + } + p += K1K2; + q -= K1K2; + } + p = K1K2 + 1; + q = K1K2 * (K3 - 1) + K1 - 1; + for (int k3 = halfK3; (k3 < K3); k3++) { + progressive = p; + regressive = q; + for (int k1 = 1; (k1 < K1); k1++) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive++; + regressive--; + } + p += K1K2; + q -= K1K2; + } + progressive = K1K2 + K1 + 1; + regressive = dataLength - 1; + for (int k3 = halfK3; (k3 < K3); k3++) { + for (int k2 = 1; (k2 < K2); k2++) { + for (int k1 = 1; (k1 < K1); k1++) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive++; + regressive--; + } + progressive++; + regressive--; + } + progressive += K1; + regressive -= K1; + } +} /* end transformRealFloat3DAcrossFirst */ + +/*------------------------------------------------------------------*/ +private void transformRealFloat3DColumnFirst ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int K3 = depth.intValue(); + final int K1K2 = K1 * K2; + final int k1K2 = K1K2 - K1; + final int K1K3 = K1 * K3; + final int halfK2 = (K2 >> 1) + 1; + int k = -1; + // First column for an odd (width * depth) + if (1 == (K1K3 & 1)) { + final FFTSetupReal fft = FFTSetupReal.transforms.get(height); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case LENGTH1: { + imDataFloat[0] = 0.0F; + break; + } + case LENGTH2: { + new DFTLength2RealFloat(reDataFloat, imDataFloat, 0, K1).run(); + break; + } + case LENGTH3: { + new DFTLength3RealFloat(reDataFloat, imDataFloat, 0, K1).run(); + break; + } + case LENGTH4: { + new DFTLength4RealFloat(reDataFloat, imDataFloat, 0, K1).run(); + break; + } + case LENGTH5: { + new DFTLength5RealFloat(reDataFloat, imDataFloat, 0, K1).run(); + break; + } + case LENGTH6: { + new DFTLength6RealFloat(reDataFloat, imDataFloat, 0, K1).run(); + break; + } + case LENGTH8: { + new DFTLength8RealFloat(reDataFloat, imDataFloat, 0, K1).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealFloat(reDataFloat, imDataFloat, 0, K1, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat, + fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, K1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + k = 0; + } + // Remaining columns + final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(height); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + while (++k < K1K3) { + executor1.execute(new DFTBruteForceRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + } + break; + } + case COPRIMEFACTOR: { + while (++k < K1K3) { + executor1.execute(new DFTCoprimeFactorRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1, + fft1.ruritanian, fft1.chinese, fft1.K1)); + } + break; + } + case DUOREAL: { + while (++k < K1K3) { + final int p = k1K2 * (k / K1) + k; + k++; + final int q = k1K2 * (k / K1) + k; + executor1.execute(new DFTDuoRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, q, K1, K2)); + } + break; + } + case EVENREAL: { + while (++k < K1K3) { + executor1.execute(new DFTEvenRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + } + break; + } + case LENGTH1: { + while (++k < K1K3) { + imDataFloat[k] = 0.0F; + } + break; + } + case LENGTH2: { + while (++k < K1K3) { + executor1.execute(new DFTLength2RealFloat( + reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1)); + } + break; + } + case LENGTH3: { + while (++k < K1K3) { + executor1.execute(new DFTLength3RealFloat( + reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1)); + } + break; + } + case LENGTH4: { + while (++k < K1K3) { + executor1.execute(new DFTLength4RealFloat( + reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1)); + } + break; + } + case LENGTH5: { + while (++k < K1K3) { + executor1.execute(new DFTLength5RealFloat( + reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1)); + } + break; + } + case LENGTH6: { + while (++k < K1K3) { + executor1.execute(new DFTLength6RealFloat( + reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1)); + } + break; + } + case LENGTH8: { + while (++k < K1K3) { + executor1.execute(new DFTLength8RealFloat( + reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1)); + } + break; + } + case MIXEDRADIX: { + while (++k < K1K3) { + executor1.execute(new DFTMixedRadixRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat, fft1.K1)); + } + break; + } + case PADDEDRADER: { + while (++k < K1K3) { + executor1.execute(new DFTPaddedRaderRealFloat( + reDataFloat, imDataFloat, k1K2 * (k / K1) + k, K1, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + } + break; + } + case RADER: { + while (++k < K1K3) { + executor1.execute(new DFTRaderRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + } + break; + } + case RADIX2: { + while (++k < K1K3) { + executor1.execute(new DFTRadix2RealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1, + fft1.reUnitRootEvenFloat, fft1.imUnitRootEvenFloat, + fft1.reUnitRootOddFloat, fft1.imUnitRootOddFloat)); + } + break; + } + case SPLITRADIX: { + while (++k < K1K3) { + executor1.execute(new DFTSplitRadixRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, k1K2 * (k / K1) + k, K1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Rows + final FFTSetup fft2 = FFTSetup.transforms.get(width); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + final int skippedRows = (K2 - halfK2) * K1; + switch (fft2.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + p += K1; + } + p += skippedRows; + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + p += K1; + } + p += skippedRows; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength2Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + p += skippedRows; + } + break; + } + case LENGTH3: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength3Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + p += skippedRows; + } + break; + } + case LENGTH4: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength4Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + p += skippedRows; + } + break; + } + case LENGTH5: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength5Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + p += skippedRows; + } + break; + } + case LENGTH6: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength6Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + p += skippedRows; + } + break; + } + case LENGTH8: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTLength8Float( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + p += skippedRows; + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, + fft2.K1)); + p += K1; + } + p += skippedRows; + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, p, 1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + p += K1; + } + p += skippedRows; + } + break; + } + case RADER: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + p += K1; + } + p += skippedRows; + } + break; + } + case RADIX2: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + p += K1; + } + p += skippedRows; + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k3 = 0; (k3 < K3); k3++) { + for (int k2 = 0; (k2 < halfK2); k2++) { + executor2.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + p += K1; + } + p += skippedRows; + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Across + final FFTSetup fft3 = FFTSetup.transforms.get(depth); + final ExecutorService executor3 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft3.algorithm) { + case BRUTEFORCE: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, K1K2, + fft3.reUnitRootFloat, fft3.imUnitRootFloat)); + p++; + } + } + break; + } + case COPRIMEFACTOR: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, K1K2, + fft3.ruritanian, fft3.chinese, fft3.K1)); + p++; + } + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength2Float( + reDataFloat, imDataFloat, p, K1K2)); + p++; + } + } + break; + } + case LENGTH3: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength3Float( + reDataFloat, imDataFloat, p, K1K2)); + p++; + } + } + break; + } + case LENGTH4: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength4Float( + reDataFloat, imDataFloat, p, K1K2)); + p++; + } + } + break; + } + case LENGTH5: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength5Float( + reDataFloat, imDataFloat, p, K1K2)); + p++; + } + } + break; + } + case LENGTH6: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength6Float( + reDataFloat, imDataFloat, p, K1K2)); + p++; + } + } + break; + } + case LENGTH8: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTLength8Float( + reDataFloat, imDataFloat, p, K1K2)); + p++; + } + } + break; + } + case MIXEDRADIX: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, K1K2, + fft3.reUnitRootFloat, fft3.imUnitRootFloat, fft3.K1)); + p++; + } + } + break; + } + case PADDEDRADER: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, p, K1K2, + fft3.reConvolverFloat, fft3.imConvolverFloat, + fft3.modular, fft3.inverseModular)); + p++; + } + } + break; + } + case RADER: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, K1K2, + fft3.reConvolverFloat, fft3.imConvolverFloat, + fft3.modular, fft3.inverseModular)); + p++; + } + } + break; + } + case RADIX2: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, K1K2, + fft3.reUnitRootFloat, fft3.imUnitRootFloat)); + p++; + } + } + break; + } + case SPLITRADIX: { + int p = 0; + for (int k2 = 0; (k2 < halfK2); k2++) { + for (int k1 = 0; (k1 < K1); k1++) { + executor3.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, K1K2, + fft3.reUnitRootFloat, fft3.imUnitRootFloat)); + p++; + } + } + break; + } + } + try { + executor3.shutdown(); + executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + int progressive = K1; + int regressive = K1K2 - K1; + while (progressive < regressive) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive += K1; + regressive -= K1; + } + int p = K1 + 1; + int q = K1K2 - 1; + for (int k2 = halfK2; (k2 < K2); k2++) { + progressive = p; + regressive = q; + for (int k1 = 1; (k1 < K1); k1++) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive++; + regressive--; + } + p += K1; + q -= K1; + } + p = K1K2 + K1; + q = dataLength - K1; + for (int k3 = 1; (k3 < K3); k3++) { + progressive = p; + regressive = q; + for (int k2 = halfK2; (k2 < K2); k2++) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive += K1; + regressive -= K1; + } + p += K1K2; + q -= K1K2; + } + progressive = K1K2 + K1 + 1; + regressive = dataLength - 1; + for (int k3 = 1; (k3 < K3); k3++) { + for (int k2 = halfK2; (k2 < K2); k2++) { + for (int k1 = 1; (k1 < K1); k1++) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive++; + regressive--; + } + progressive++; + regressive--; + } + progressive += halfK2 * K1; + regressive -= halfK2 * K1; + } +} /* end transformRealFloat3DColumnFirst */ + +/*------------------------------------------------------------------*/ +private void transformRealFloat3DRowFirst ( +) { + final int K1 = width.intValue(); + final int K2 = height.intValue(); + final int K3 = depth.intValue(); + final int K1K2 = K1 * K2; + final int K2K3 = K2 * K3; + final int halfK1 = (K1 >> 1) + 1; + int k = 0; + int p = 0; + // First row for an odd (height * depth) + if (1 == (K2K3 & 1)) { + final FFTSetupReal fft = FFTSetupReal.transforms.get(width); + switch (fft.algorithm) { + case BRUTEFORCE: { + new DFTBruteForceRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case COPRIMEFACTOR: { + new DFTCoprimeFactorRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.ruritanian, fft.chinese, fft.K1).run(); + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + new DFTEvenRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + case LENGTH1: { + imDataFloat[0] = 0.0F; + break; + } + case LENGTH2: { + new DFTLength2RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH3: { + new DFTLength3RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH4: { + new DFTLength4RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH5: { + new DFTLength5RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH6: { + new DFTLength6RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case LENGTH8: { + new DFTLength8RealFloat(reDataFloat, imDataFloat, 0, 1).run(); + break; + } + case MIXEDRADIX: { + new DFTMixedRadixRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat, fft.K1).run(); + break; + } + case PADDEDRADER: { + new DFTPaddedRaderRealFloat(reDataFloat, imDataFloat, 0, 1, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADER: { + new DFTRaderRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reConvolverFloat, fft.imConvolverFloat, + fft.modular, fft.inverseModular).run(); + break; + } + case RADIX2: { + new DFTRadix2RealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootEvenFloat, fft.imUnitRootEvenFloat, + fft.reUnitRootOddFloat, fft.imUnitRootOddFloat).run(); + break; + } + case SPLITRADIX: { + new DFTSplitRadixRealFloat(reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, 0, 1, + fft.reUnitRootFloat, fft.imUnitRootFloat).run(); + break; + } + } + k = 1; + p = K1; + } + // Remaining rows + final FFTSetupDuoReal fft1 = FFTSetupDuoReal.transforms.get(width); + final ExecutorService executor1 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + switch (fft1.algorithm) { + case BRUTEFORCE: { + while (k++ < K2K3) { + executor1.execute(new DFTBruteForceRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + p += K1; + } + break; + } + case COPRIMEFACTOR: { + while (k++ < K2K3) { + executor1.execute(new DFTCoprimeFactorRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.ruritanian, fft1.chinese, fft1.K1)); + p += K1; + } + break; + } + case DUOREAL: { + while (k++ < K2K3) { + executor1.execute(new DFTDuoRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, p + K1, 1, K1)); + p += 2 * K1; + k++; + } + break; + } + case EVENREAL: { + while (k++ < K2K3) { + executor1.execute(new DFTEvenRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + p += K1; + } + break; + } + case LENGTH1: { + while (k++ < K2K3) { + imDataFloat[p] = 0.0F; + p += K1; + } + break; + } + case LENGTH2: { + while (k++ < K2K3) { + executor1.execute(new DFTLength2RealFloat( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH3: { + while (k++ < K2K3) { + executor1.execute(new DFTLength3RealFloat( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH4: { + while (k++ < K2K3) { + executor1.execute(new DFTLength4RealFloat( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH5: { + while (k++ < K2K3) { + executor1.execute(new DFTLength5RealFloat( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH6: { + while (k++ < K2K3) { + executor1.execute(new DFTLength6RealFloat( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case LENGTH8: { + while (k++ < K2K3) { + executor1.execute(new DFTLength8RealFloat( + reDataFloat, imDataFloat, p, 1)); + p += K1; + } + break; + } + case MIXEDRADIX: { + while (k++ < K2K3) { + executor1.execute(new DFTMixedRadixRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat, fft1.K1)); + p += K1; + } + break; + } + case PADDEDRADER: { + while (k++ < K2K3) { + executor1.execute(new DFTPaddedRaderRealFloat( + reDataFloat, imDataFloat, p, 1, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + p += K1; + } + break; + } + case RADER: { + while (k++ < K2K3) { + executor1.execute(new DFTRaderRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reConvolverFloat, fft1.imConvolverFloat, + fft1.modular, fft1.inverseModular)); + p += K1; + } + break; + } + case RADIX2: { + while (k++ < K2K3) { + executor1.execute(new DFTRadix2RealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootEvenFloat, fft1.imUnitRootEvenFloat, + fft1.reUnitRootOddFloat, fft1.imUnitRootOddFloat)); + p += K1; + } + break; + } + case SPLITRADIX: { + while (k++ < K2K3) { + executor1.execute(new DFTSplitRadixRealFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p, 1, + fft1.reUnitRootFloat, fft1.imUnitRootFloat)); + p += K1; + } + break; + } + } + try { + executor1.shutdown(); + executor1.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Columns + final FFTSetup fft2 = FFTSetup.transforms.get(height); + final ExecutorService executor2 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + p = 0; + switch (fft2.algorithm) { + case BRUTEFORCE: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + } + p += K1K2; + } + break; + } + case COPRIMEFACTOR: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft2.ruritanian, fft2.chinese, fft2.K1)); + } + p += K1K2; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength2Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH3: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength3Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH4: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength4Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH5: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength5Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH6: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength6Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case LENGTH8: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTLength8Float( + reDataFloat, imDataFloat, p + k1, K1)); + } + p += K1K2; + } + break; + } + case MIXEDRADIX: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat, fft2.K1)); + } + p += K1K2; + } + break; + } + case PADDEDRADER: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, p + k1, K1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + } + p += K1K2; + } + break; + } + case RADER: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft2.reConvolverFloat, fft2.imConvolverFloat, + fft2.modular, fft2.inverseModular)); + } + p += K1K2; + } + break; + } + case RADIX2: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + } + p += K1K2; + } + break; + } + case SPLITRADIX: { + for (int k3 = 0; (k3 < K3); k3++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor2.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1, + fft2.reUnitRootFloat, fft2.imUnitRootFloat)); + } + p += K1K2; + } + break; + } + } + try { + executor2.shutdown(); + executor2.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + // Across + final FFTSetup fft3 = FFTSetup.transforms.get(depth); + final ExecutorService executor3 = + (PARALLELPROCESSING) ? (Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors())) + : (Executors.newSingleThreadExecutor()); + p = 0; + switch (fft3.algorithm) { + case BRUTEFORCE: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTBruteForceFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1K2, + fft3.reUnitRootFloat, fft3.imUnitRootFloat)); + } + p += K1; + } + break; + } + case COPRIMEFACTOR: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTCoprimeFactorFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1K2, + fft3.ruritanian, fft3.chinese, fft3.K1)); + } + p += K1; + } + break; + } + case DUOREAL: { + throw(new IllegalStateException()); + } + case EVENREAL: { + throw(new IllegalStateException()); + } + case LENGTH1: { + break; + } + case LENGTH2: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTLength2Float( + reDataFloat, imDataFloat, p + k1, K1K2)); + } + p += K1; + } + break; + } + case LENGTH3: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTLength3Float( + reDataFloat, imDataFloat, p + k1, K1K2)); + } + p += K1; + } + break; + } + case LENGTH4: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTLength4Float( + reDataFloat, imDataFloat, p + k1, K1K2)); + } + p += K1; + } + break; + } + case LENGTH5: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTLength5Float( + reDataFloat, imDataFloat, p + k1, K1K2)); + } + p += K1; + } + break; + } + case LENGTH6: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTLength6Float( + reDataFloat, imDataFloat, p + k1, K1K2)); + } + p += K1; + } + break; + } + case LENGTH8: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTLength8Float( + reDataFloat, imDataFloat, p + k1, K1K2)); + } + p += K1; + } + break; + } + case MIXEDRADIX: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTMixedRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1K2, + fft3.reUnitRootFloat, fft3.imUnitRootFloat, fft3.K1)); + } + p += K1; + } + break; + } + case PADDEDRADER: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTPaddedRaderFloat( + reDataFloat, imDataFloat, p + k1, K1K2, + fft3.reConvolverFloat, fft3.imConvolverFloat, + fft3.modular, fft3.inverseModular)); + } + p += K1; + } + break; + } + case RADER: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTRaderFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1K2, + fft3.reConvolverFloat, fft3.imConvolverFloat, + fft3.modular, fft3.inverseModular)); + } + p += K1; + } + break; + } + case RADIX2: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTRadix2Float( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1K2, + fft3.reUnitRootFloat, fft3.imUnitRootFloat)); + } + p += K1; + } + break; + } + case SPLITRADIX: { + for (int k2 = 0; (k2 < K2); k2++) { + for (int k1 = 0; (k1 < halfK1); k1++) { + executor3.execute(new DFTSplitRadixFloat( + reDataFloat, imDataFloat, + reBufferFloat, imBufferFloat, p + k1, K1K2, + fft3.reUnitRootFloat, fft3.imUnitRootFloat)); + } + p += K1; + } + break; + } + } + try { + executor3.shutdown(); + executor3.awaitTermination((long)(Integer.MAX_VALUE), TimeUnit.DAYS); + } + catch (InterruptedException ignored) { + } + int progressive = 0; + int regressive = K1; + while (++progressive < --regressive) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + } + p = K1 + 1; + int q = K1K2 - 1; + for (int k2 = 1; (k2 < K2); k2++) { + progressive = p; + regressive = q; + for (int k1 = halfK1; (k1 < K1); k1++) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive++; + regressive--; + } + p += K1; + q -= K1; + } + p = K1K2 + 1; + q = K1K2 * (K3 - 1) + K1 - 1; + for (int k3 = 1; (k3 < K3); k3++) { + progressive = p; + regressive = q; + for (int k1 = halfK1; (k1 < K1); k1++) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive++; + regressive--; + } + p += K1K2; + q -= K1K2; + } + progressive = K1K2 + K1 + 1; + regressive = dataLength - 1; + for (int k3 = 1; (k3 < K3); k3++) { + for (int k2 = 1; (k2 < K2); k2++) { + for (int k1 = halfK1; (k1 < K1); k1++) { + reDataFloat[regressive] = reDataFloat[progressive]; + imDataFloat[regressive] = -imDataFloat[progressive]; + progressive++; + regressive--; + } + progressive += halfK1; + regressive -= halfK1; + } + progressive += K1; + regressive -= K1; + } +} /* end transformRealFloat3DRowFirst */ + +} /* end class SlowFourierTransform */ diff --git a/src/bilib/src/bilib/optimization/.DS_Store b/src/bilib/src/bilib/optimization/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..1819d85b732e46e0739ef8393b27c7199da021a0 GIT binary patch literal 6148 zcmZQzU|@7AO)+F(5MW?n;9!8z3~dZp0Z1N%F(jFwB8(vOz-HtzBr&8i6fvYT<U-{L zQ6RlS3^@#`3}p<d49NNu8HyMR8A=&IbP7XBN^x>dQht68Ap=L{M?+vV1V%$(Gz3ON zU^E2qh5)GU1=auDcvX!`j)uT!2#mxK0F@64khVR81C(xn&>$%WMg|6O7l4t0fdv*m zjNpC%14s^}6-0xyf@qLd21XDIYz9~>10z%`Be)v^(g*62fM~FG21c;WAU;?-10&dG z1_nlmb_PbM&5Y0<3L`{210zH`10&dWnCnLA(GVC70ayqyLl^>}`rnm-0ayPYqH2^J z4S~@R7?vTx$l?<0;smagvHK5H*MjQP1gM$=P-TpudKn@HQUa>l!PPMnWKdB8sthC! c(hj1*)iEOj1GqLHZ4AIdXp|le0s4jj013Ak_y7O^ literal 0 HcmV?d00001 diff --git a/src/bilib/src/bilib/optimization/levenbergmarquardt/Cholesky.java b/src/bilib/src/bilib/optimization/levenbergmarquardt/Cholesky.java new file mode 100644 index 0000000..49fe829 --- /dev/null +++ b/src/bilib/src/bilib/optimization/levenbergmarquardt/Cholesky.java @@ -0,0 +1,111 @@ +package bilib.optimization.levenbergmarquardt; + +/** + * + * <p> + * Title: Cholesky Decomposition + * </p> + * <p> + * Description: Performs a Cholesky decomposition of a matrix and solve a linear + * system using this decomposition. Ported to Java from the Numerical Recipes in + * C. Press, Teukolsky, Vetterling,and Flannery. 2nd edition. Cambridge + * University Press, 1992. + * </p> + */ + +public class Cholesky { + + /** + * Given a positive-definite symmetric matrix A[1..n][1..n], this method + * constructs its Cholesky decomposition, A = L � L' . On input, only the + * upper triangle of a need be given; it is not modified. The Cholesky + * factor L is returned in the lower triangle of a, except for its diagonal + * elements which are returned in p[1..n]. + * + * @param A + * double[][] Input matrix. + * @param p + * double[] + * @return boolean Returns false if the decomposition is not possible. + */ + public static boolean decomp(double[][] A, double[] p) { + int n = A.length; + int i, j, k; + double sum; + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + sum = A[i][j]; + for (k = i - 1; k >= 0; k--) { + sum -= (A[i][k] * A[j][k]); + } + if (i == j) { + if (sum <= 0.) { + return false; // not positive definite + } + p[i] = Math.sqrt(sum); + } + else { + A[j][i] = sum / p[i]; + } + } + } + return true; + } // decomp + + /** + * Solves a the linear system Ax=b. + * + * @param A + * double[][] Is the result of decomp(A) + * @param p + * double[] The resulting diagonal vector. + * @param b + * double[] + * @param x + * double[] + */ + private static void solve(double[][] A, double[] p, double[] b, double[] x) { + int n = A.length; + int i, k; + double sum; + // Solve L � y = b, storing y in x. + for (i = 0; i < n; i++) { + sum = b[i]; + for (k = i - 1; k >= 0; k--) { + sum -= (A[i][k] * x[k]); + } + x[i] = sum / p[i]; + } + + // Solve L' � x = y. + for (i = n - 1; i >= 0; i--) { + sum = x[i]; + for (k = i + 1; k < n; k++) { + sum -= (A[k][i] * x[k]); + } + x[i] = sum / p[i]; + } + } // solve + + /** + * Solves the linear system Ax=b. + * + * @param A + * double[][] + * @param x + * double[] + * @param b + * double[] + * @return boolean returns false if the system can not be solved using + * Cholesky decomposition. + */ + public static boolean solve(double[][] A, double[] x, double[] b) { + double[] p = new double[A.length]; + if (!decomp(A, p)) { + return false; + } + solve(A, p, b, x); + return true; + } // solve + +} // Cholesky diff --git a/src/bilib/src/bilib/optimization/levenbergmarquardt/Function.java b/src/bilib/src/bilib/optimization/levenbergmarquardt/Function.java new file mode 100644 index 0000000..8adc8be --- /dev/null +++ b/src/bilib/src/bilib/optimization/levenbergmarquardt/Function.java @@ -0,0 +1,32 @@ +package bilib.optimization.levenbergmarquardt; + +/** + */ +public interface Function { + + /** + * Evaluates the model at point x (may be mulidimensional). + * + * @param x + * double[] Point where we evaluate the model function. + * @param a + * double[] Model estimators. + * @return double + */ + public abstract double eval(double[] x, double[] a); + + /** + * Returns the kth component of the gradient df(x,a)/da_k + * + * @param x + * double[] + * @param a + * double[] + * @param ak + * int + * @return double + */ + public abstract double grad(double[] x, double[] a, int ak); + + public abstract void setDebug(boolean debug); +} diff --git a/src/bilib/src/bilib/optimization/levenbergmarquardt/LevenbergMarquardt.java b/src/bilib/src/bilib/optimization/levenbergmarquardt/LevenbergMarquardt.java new file mode 100644 index 0000000..3ef29f8 --- /dev/null +++ b/src/bilib/src/bilib/optimization/levenbergmarquardt/LevenbergMarquardt.java @@ -0,0 +1,297 @@ +package bilib.optimization.levenbergmarquardt; + +/** + * + * <p> + * Title: Levenberg-Marquardt + * </p> + * <p> + * Description: Perfoms data fitting to a non linear model using the + * Levenberg-Marquadrt method. Ported to Java from the Numerical Recipes in C. + * Press, Teukolsky, Vetterling,and Flannery. 2nd edition. Cambridge University + * Press, 1992. + * </p> + */ + +public class LevenbergMarquardt { + + private Function f; + private double lambdaInitial = 0.0001; + private int itmax = 1000; + private boolean print = false; + private int iter; + private double tolerance = 0.001; + + /** + * Levenberg-Marquardt constructor. Supply a Function object, f, that + * evaluates the fitting function y, and its derivatives dyda[1..ma] with + * respect to the fitting parameters a at x. On the first call provide an + * initial guess for the parameters a, and set alamda to some small value, + * e.g. alambda=0.001. If a step succeeds chisq becomes smaller and alamda + * decreases by a factor of 10. If a step fails alamda grows by a factor of + * 10. + * + * @param f + * Function + * @param lambdaInitial + * double + * @param itmax + * int + */ + public LevenbergMarquardt(Function f, double lambdaInitial, int itmax, boolean print) { + this.f = f; + this.lambdaInitial = lambdaInitial; + this.itmax = itmax; + this.print = print; + if (print) { + System.out.print("CONSTRUCTOR \tlambda:" + lambdaInitial + " max iterations: " + itmax); + } + } + + public LevenbergMarquardt(Function f, int itmax, double tolerance) { + this.f = f; + this.itmax = itmax; + this.tolerance = tolerance; + } + + public LevenbergMarquardt(Function f, int itmax) { + this.f = f; + this.itmax = itmax; + } + + public LevenbergMarquardt(Function f, boolean print) { + this.f = f; + this.print = print; + } + + public LevenbergMarquardt(Function f) { + this.f = f; + } + + public void setPrint(boolean print) { + this.print = print; + } + + /** + * Levenberg-Marquardt method, attempting to reduce the value chi2 of a fit + * between a set of data points x[1..ndata], y[1..ndata] with individual + * standard deviations sig[1..ndata], and a nonlinear function dependent on + * ma coefficients a[1..ma]. The input array ia[1..ma] indicates by true, + * entries those components of a that should be fitted for, and by false, + * entries those components that should be held fixed at their input values. + * The program returns current best-fit values for the parameters a[1..ma], + * and chi2 = chisq. + * + * @param x + * double[] + * @param y + * double[] + * @param sig + * double[] + * @param a + * double[] + * @return double + * + */ + public double minimize(double x[], double y[], double sig[], double a[]) { + iter = 0; + double lambda = lambdaInitial; + + boolean ia[] = new boolean[a.length]; + for (int i = 0; i < a.length; i++) + ia[i] = true; + + int rep = 0; + boolean done = false; + double eps = 0; + int mfit = 0; + int j, k, l; + int ma = a.length; + double ochisq = 0, chisq; + + double[][] covar = new double[ma][ma]; + double[][] alpha = new double[ma][ma]; + double[] beta = new double[ma]; + double[] atry = new double[ma]; + double[] da = new double[ma]; + + double[] oneda; + + // initialization + for (mfit = 0, j = 0; j < ma; j++) { + if (ia[j]) { + mfit++; + } + } + oneda = new double[mfit]; + + chisq = mrqcof(x, y, sig, a, ia, alpha, beta); + ochisq = chisq; + for (j = 0; j < ma; j++) { + atry[j] = a[j]; + } + + do { + // Alter linearized fitting matrix, by augmenting diagonal elements. + for (j = 0; j < mfit; j++) { + for (k = 0; k < mfit; k++) { + covar[j][k] = alpha[j][k]; + } + covar[j][j] = alpha[j][j] * (1.0 + lambda); + oneda[j] = beta[j]; + } + + Cholesky.solve(covar, oneda, oneda); // Matrix solution. + + for (j = 0; j < mfit; j++) { + da[j] = oneda[j]; + } + + for (j = 0, l = 0; l < ma; l++) { + if (ia[l]) { + atry[l] = a[l] + da[j++]; + } + } + chisq = mrqcof(x, y, sig, atry, ia, covar, da); + eps = Math.abs(chisq - ochisq); + if (print) { + System.out.print("#" + iter + "\t chi:" + Math.round(Math.sqrt(chisq) * 1000) / 1000.0 + " \tlambda:" + lambda + " eps:" + eps); + for (int i = 0; i < a.length; i++) + System.out.print("\t a[" + i + "]=" + atry[i]); + System.out.println(";"); + } + if (chisq < ochisq) { + // Success, accept the new solution. + lambda *= 0.1; + ochisq = chisq; + for (j = 0; j < mfit; j++) { + for (k = 0; k < mfit; k++) { + alpha[j][k] = covar[j][k]; + } + beta[j] = da[j]; + } + for (l = 0; l < ma; l++) { + a[l] = atry[l]; + } + } + else { + // Failure, increase alamda and return. + lambda *= 10.0; + chisq = ochisq; + } + iter++; + if (eps > tolerance) { + rep = 0; + } + else { + rep++; + if (rep == 4) { + done = true; + } + } + + } + while (iter < itmax && !done); + if (print) + System.out.println("Final iter" + iter + "\t rep:" + rep + " \tdone:" + done + " eps:" + eps + " tolerance:" + tolerance); + + return Math.sqrt(chisq); + } + + /** + * Return the number of iterations after minimization. + * + * @return number of iteration + */ + public int getIteration() { + return iter; + } + + /** + * Used by mrqmin to evaluate the linearized fitting matrix alpha, and + * vector beta as in "NR in C"(15.5.8), and calculate chi2. + * + * @param x + * double[] + * @param y + * double[] + * @param sig + * double[] + * @param a + * double[] + * @param ia + * boolean[] + * @param alpha + * double[][] + * @param beta + * double[] + * @param f + * LMfunc + * @return double + */ + private double mrqcof(double x[], double y[], double sig[], double a[], boolean ia[], double alpha[][], double beta[]) { + + int ndata = x.length; + int ma = a.length; + double chisq; + int i, j, k, l, m, mfit = 0; + double ymod, wt, sig2i, dy; + double[] dyda = new double[ma]; + + for (j = 0; j < ma; j++) { + if (ia[j]) { + mfit++; + } + } + + // Initialize(symmetric) alpha, beta. + for (j = 0; j < mfit; j++) { + for (k = 0; k <= j; k++) { + alpha[j][k] = 0; + } + beta[j] = 0; + } + + chisq = 0; + + // Summation loop over all data. + for (i = 0; i < ndata; i++) { + double[] xi = new double[1]; + xi[0] = x[i]; + ymod = f.eval(xi, a); + for (k = 0; k < a.length; k++) { + dyda[k] = f.grad(xi, a, k); + } + + /* + * if (print) { System.out.print("D" + iter); for(int p=0; + * p<dyda.length; p++) System.out.print("\t da["+p+"]=" + dyda[p]); + * System.out.println(";"); } + */ + sig2i = 1.0 / (sig[i] * sig[i]); + dy = y[i] - ymod; + for (j = 0, l = 0; l < ma; l++) { + if (ia[l]) { + wt = dyda[l] * sig2i; + for (k = 0, m = 0; m <= l; m++) { + if (ia[m]) { + alpha[j][k++] += wt * dyda[m]; + } + } + beta[j] += dy * wt; + j++; + } + } + chisq += dy * dy * sig2i; // And find chi2. + } + + // Fill in the symmetric side of alpha + for (j = 1; j < mfit; j++) { + for (k = 0; k < j; k++) { + alpha[k][j] = alpha[j][k]; + } + } + return chisq; + } + +} diff --git a/src/bilib/src/ijtools/Convolver1D.java b/src/bilib/src/ijtools/Convolver1D.java new file mode 100644 index 0000000..c8a6b34 --- /dev/null +++ b/src/bilib/src/ijtools/Convolver1D.java @@ -0,0 +1,118 @@ +package ijtools; + +/** + * Fast 1-D convolution routines for symmetric kernels. All methods require only + * the causal half of the kernel, i.e., elements [0..n], assuming a support of + * [-n..n]. Only kernels with odd support are implemented. Mirror border + * conditions are applied. + * + * @author Francois Aguet + * @version 1.0 + */ +public class Convolver1D { + + /** + * Convolution with a symmetric kernel. + */ + public static double[] convolveEven(double[] input, double[] kernel) { + + int nx = input.length; + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * nx - 2; + + double[] output = new double[nx]; + + for (int x = 0; x < k_1; x++) { + output[x] = kernel[0] * input[x]; + for (int i = 1; i <= x; i++) { + output[x] += kernel[i] * (input[x - i] + input[x + i]); + } + for (int i = x + 1; i < k; i++) { + output[x] += kernel[i] * (input[i - x] + input[x + i]); + } + } + for (int x = k_1; x <= nx - k; x++) { + output[x] = kernel[0] * input[x]; + for (int i = 1; i < k; i++) { + output[x] += kernel[i] * (input[x - i] + input[x + i]); + } + } + for (int x = nx - k_1; x < nx; x++) { + output[x] = kernel[0] * input[x]; + for (int i = 1; i < nx - x; i++) { + output[x] += kernel[i] * (input[x - i] + input[x + i]); + } + for (int i = nx - x; i < k; i++) { + output[x] += kernel[i] * (input[b - i - x] + input[x - i]); + } + } + return output; + } + + /** + * Convolution with an anti-symmetric kernel. + */ + public static double[] convolveOdd(double[] input, double[] kernel) { + + int nx = input.length; + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * nx - 2; + + double[] output = new double[nx]; + + for (int x = 0; x < k_1; x++) { + output[x] = 0.0; + for (int i = 1; i <= x; i++) { + output[x] += kernel[i] * (input[x + i] - input[x - i]); + } + for (int i = x + 1; i < k; i++) { + output[x] += kernel[i] * (input[x + i] - input[i - x]); + } + } + for (int x = k_1; x <= nx - k; x++) { + output[x] = 0.0; + for (int i = 1; i < k; i++) { + output[x] += kernel[i] * (input[x + i] - input[x - i]); + } + } + for (int x = nx - k_1; x < nx; x++) { + output[x] = 0.0; + for (int i = 1; i < nx - x; i++) { + output[x] += kernel[i] * (input[x + i] - input[x - i]); + } + for (int i = nx - x; i < k; i++) { + output[x] += kernel[i] * (input[b - i - x] - input[x - i]); + } + } + return output; + } + + /** + * Moving sum filter. The length of the output is extended by 'length-1'. + */ + public static double[] movingSum(double[] input, int length) { + + int nx = input.length; + double[] output = new double[nx + length - 1]; + output[0] = input[0]; + for (int i = 1; i < length; i++) { + output[0] += input[i]; + } + for (int i = 1; i < length; i++) { + output[i] = output[i - 1] - input[length - i] + input[i]; // 0 - + // (idx-w) + } + for (int i = length; i < nx; i++) { + output[i] = output[i - 1] - input[i - length] + input[i]; + } + for (int i = nx; i < nx + length - 1; i++) { + output[i] = output[i - 1] - input[i - length] + input[2 * nx - i - 2]; // nx-1 + // - + // (i-(nx-1)) + } + return output; + } + +} diff --git a/src/bilib/src/ijtools/Convolver2D.java b/src/bilib/src/ijtools/Convolver2D.java new file mode 100644 index 0000000..43bec62 --- /dev/null +++ b/src/bilib/src/ijtools/Convolver2D.java @@ -0,0 +1,1133 @@ +package ijtools; + +/** + * Fast 2-D convolution routines for symmetric kernels. All methods require only + * the causal half of the kernel, i.e., elements [0..n], assuming a support of + * [-n..n]. Only kernels with odd support are implemented. Mirror border + * conditions are applied. + * + * @author Francois Aguet + * @version 1.0 + */ +public class Convolver2D { + + /** + * Moving sum filter, applied to both dimensions. Each dimension of the + * output is extended by 'length-1'. + */ + public static double[][] movingSum(double[][] input, int length) { + + int nx = input.length; + int ny = input[0].length; + + double[][] output = new double[nx + length - 1][ny + length - 1]; + + for (int y = 0; y < ny; y++) { + output[0][y] = input[0][y]; + for (int x = 1; x < length; x++) { + output[0][y] += input[x][y]; + } + for (int x = 1; x < length; x++) { + output[x][y] = output[x - 1][y] - input[length - x][y] + input[x][y]; + } + for (int x = length; x < nx; x++) { + output[x][y] = output[x - 1][y] - input[x - length][y] + input[x][y]; + } + for (int x = nx; x < nx + length; x++) { + output[x][y] = output[x - 1][y] - input[x - length][y] + input[2 * nx - x - 2][y]; + } + } + + for (int x = 0; x < nx + length - 1; x++) { + output[x][0] = output[x][0]; + for (int y = 1; y < length; y++) { + output[x][0] += output[x][y]; + } + for (int y = 1; y < length; y++) { + output[x][y] = output[x][y - 1] - output[x][length - y] + output[x][y]; + } + for (int y = length; y < ny; y++) { + output[x][y] = output[x][y - 1] - output[x][y - length] + output[x][y]; + } + for (int y = ny; y < ny + length - y; y++) { + output[x][y] = output[x][y - 1] - output[x][y - length] + output[x][2 * ny - y - 2]; + } + } + + return output; + } + + /** + * Moving sum filter, applied to the x-dimension. The x-dimension of the + * output is extended by 'length-1'. + */ + public static double[][] movingSumX(double[][] input, int length) { + + int nx = input.length; + int ny = input[0].length; + + double[][] output = new double[nx + length - 1][ny + length - 1]; + + for (int y = 0; y < ny; y++) { + output[0][y] = input[0][y]; + for (int x = 1; x < length; x++) { + output[0][y] += input[x][y]; + } + for (int x = 1; x < length; x++) { + output[x][y] = output[x - 1][y] - input[length - x][y] + input[x][y]; + } + for (int x = length; x < nx; x++) { + output[x][y] = output[x - 1][y] - input[x - length][y] + input[x][y]; + } + for (int x = nx; x < nx + length; x++) { + output[x][y] = output[x - 1][y] - input[x - length][y] + input[2 * nx - x - 2][y]; + } + } + return output; + } + + /** + * Moving sum filter, applied to the y-dimension. The y-dimension of the + * output is extended by 'length-1'. + */ + public static double[][] movingSumY(double[][] input, int length) { + + int nx = input.length; + int ny = input[0].length; + + double[][] output = new double[nx + length - 1][ny + length - 1]; + + for (int x = 0; x < nx + length - 1; x++) { + output[x][0] = output[x][0]; + for (int y = 1; y < length; y++) { + output[x][0] += output[x][y]; + } + for (int y = 1; y < length; y++) { + output[x][y] = output[x][y - 1] - output[x][length - y] + output[x][y]; + } + for (int y = length; y < ny; y++) { + output[x][y] = output[x][y - 1] - output[x][y - length] + output[x][y]; + } + for (int y = ny; y < ny + length - y; y++) { + output[x][y] = output[x][y - 1] - output[x][y - length] + output[x][2 * ny - y - 2]; + } + } + + return output; + } + + /** + * Convolution with a symmetric kernel along x. + */ + public static double[][] convolveEvenX(double[][] input, double[] kernel) { + + int nx = input.length; + int ny = input[0].length; + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * nx - 2; + + double[][] output = new double[nx][ny]; + + int idx = 0; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < k_1; x++) { + output[x][y] = kernel[0] * input[x][y]; + for (int i = 1; i <= x; i++) { + output[x][y] += kernel[i] * (input[x - i][y] + input[x + i][y]); + } + for (int i = x + 1; i < k; i++) { + output[x][y] += kernel[i] * (input[i - x][y] + input[x + i][y]); + } + } + for (int x = k_1; x <= nx - k; x++) { + output[x][y] = kernel[0] * input[x][y]; + for (int i = 1; i < k; i++) { + output[x][y] += kernel[i] * (input[x - i][y] + input[x + i][y]); + } + } + for (int x = nx - k_1; x < nx; x++) { + output[x][y] = kernel[0] * input[x][y]; + for (int i = 1; i < nx - x; i++) { + output[x][y] += kernel[i] * (input[x - i][y] + input[x + i][y]); + } + for (int i = nx - x; i < k; i++) { + output[x][y] += kernel[i] * (input[b - i - x][y] + input[x - i][y]); + } + } + } + return output; + } + + /** + * Convolution with a symmetric kernel along x, for an image defined as a + * 1-D array. + */ + public static double[] convolveEvenX(double[] input, double[] kernel, int nx, int ny) { + + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * nx - 2; + + double[] output = new double[nx * ny]; + + int idx = 0; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < k_1; x++) { + output[idx] = kernel[0] * input[idx]; + for (int i = 1; i <= x; i++) { + output[idx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + for (int i = x + 1; i < k; i++) { + output[idx] += kernel[i] * (input[i - x + y * nx] + input[idx + i]); + } + idx++; + } + for (int x = k_1; x <= nx - k; x++) { + output[idx] = kernel[0] * input[idx]; + for (int i = 1; i < k; i++) { + output[idx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + idx++; + } + for (int x = nx - k_1; x < nx; x++) { + output[idx] = kernel[0] * input[idx]; + for (int i = 1; i < nx - x; i++) { + output[idx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + for (int i = nx - x; i < k; i++) { + output[idx] += kernel[i] * (input[b - i - x + y * nx] + input[idx - i]); + } + idx++; + } + } + return output; + } + + /** + * Convolution with a symmetric kernel along x, for an image defined as a + * 1-D array. + */ + public static float[] convolveEvenX(float[] input, float[] kernel, int nx, int ny) { + + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * nx - 2; + + float[] output = new float[nx * ny]; + + int idx = 0; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < k_1; x++) { + output[idx] = kernel[0] * input[idx]; + for (int i = 1; i <= x; i++) { + output[idx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + for (int i = x + 1; i < k; i++) { + output[idx] += kernel[i] * (input[i - x + y * nx] + input[idx + i]); + } + idx++; + } + for (int x = k_1; x <= nx - k; x++) { + output[idx] = kernel[0] * input[idx]; + for (int i = 1; i < k; i++) { + output[idx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + idx++; + } + for (int x = nx - k_1; x < nx; x++) { + output[idx] = kernel[0] * input[idx]; + for (int i = 1; i < nx - x; i++) { + output[idx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + for (int i = nx - x; i < k; i++) { + output[idx] += kernel[i] * (input[b - i - x + y * nx] + input[idx - i]); + } + idx++; + } + } + return output; + } + + /** + * Convolution with a symmetric kernel along y. + */ + public static double[][] convolveEvenY(double[][] input, double[] kernel) { + + int nx = input.length; + int ny = input[0].length; + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * ny - 2; + + double[][] output = new double[nx][ny]; + + for (int x = 0; x < nx; x++) { + for (int y = 0; y < k_1; y++) { + output[x][y] = kernel[0] * input[x][y]; + for (int i = 1; i <= y; i++) { + output[x][y] += kernel[i] * (input[x][y - i] + input[x][y + i]); + } + for (int i = y + 1; i < k; i++) { + output[x][y] += kernel[i] * (input[x][i - y] + input[x][y + i]); + } + } + for (int y = k_1; y <= ny - k; y++) { + output[x][y] = kernel[0] * input[x][y]; + for (int i = 1; i < k; i++) { + output[x][y] += kernel[i] * (input[x][y - i] + input[x][y + i]); + } + } + for (int y = ny - k_1; y < ny; y++) { + output[x][y] = kernel[0] * input[x][y]; + for (int i = 1; i < ny - y; i++) { + output[x][y] += kernel[i] * (input[x][y - i] + input[x][y + i]); + } + for (int i = ny - y; i < k; i++) { + output[x][y] += kernel[i] * (input[x][b - i - y] + input[x][y - i]); + } + } + } + return output; + } + + /** + * Convolution with a symmetric kernel along y, for an image defined as a + * 1-D array. + */ + public static double[] convolveEvenY(double[] input, double[] kernel, int nx, int ny) { + + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * ny - 2; + + double[] output = new double[nx * ny]; + + int idx, inx; + for (int x = 0; x < nx; x++) { + for (int y = 0; y < k_1; y++) { + idx = x + y * nx; + output[idx] = kernel[0] * input[idx]; + for (int i = 1; i <= y; i++) { + inx = i * nx; + output[idx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + for (int i = y + 1; i < k; i++) { + output[idx] += kernel[i] * (input[(i - y) * nx + x] + input[idx + i * nx]); + } + } + for (int y = k_1; y <= ny - k; y++) { + idx = x + y * nx; + output[idx] = kernel[0] * input[idx]; + for (int i = 1; i < k; i++) { + inx = i * nx; + output[idx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + } + for (int y = ny - k_1; y < ny; y++) { + idx = x + y * nx; + output[idx] = kernel[0] * input[idx]; + for (int i = 1; i < ny - y; i++) { + inx = i * nx; + output[idx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + for (int i = ny - y; i < k; i++) { + output[idx] += kernel[i] * (input[(b - i - y) * nx + x] + input[idx - i * nx]); + } + } + } + return output; + } + + /** + * Convolution with a symmetric kernel along y, for an image defined as a + * 1-D array. + */ + public static float[] convolveEvenY(float[] input, float[] kernel, int nx, int ny) { + + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * ny - 2; + + float[] output = new float[nx * ny]; + + int idx, inx; + for (int x = 0; x < nx; x++) { + for (int y = 0; y < k_1; y++) { + idx = x + y * nx; + output[idx] = kernel[0] * input[idx]; + for (int i = 1; i <= y; i++) { + inx = i * nx; + output[idx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + for (int i = y + 1; i < k; i++) { + output[idx] += kernel[i] * (input[(i - y) * nx + x] + input[idx + i * nx]); + } + } + for (int y = k_1; y <= ny - k; y++) { + idx = x + y * nx; + output[idx] = kernel[0] * input[idx]; + for (int i = 1; i < k; i++) { + inx = i * nx; + output[idx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + } + for (int y = ny - k_1; y < ny; y++) { + idx = x + y * nx; + output[idx] = kernel[0] * input[idx]; + for (int i = 1; i < ny - y; i++) { + inx = i * nx; + output[idx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + for (int i = ny - y; i < k; i++) { + output[idx] += kernel[i] * (input[(b - i - y) * nx + x] + input[idx - i * nx]); + } + } + } + return output; + } + + /** + * Convolution with an anti-symmetric kernel along x. + */ + public static double[][] convolveOddX(double[][] input, double[] kernel) { + + int nx = input.length; + int ny = input[0].length; + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * nx - 2; + + double[][] output = new double[nx][ny]; + + for (int y = 0; y < ny; y++) { + for (int x = 0; x < k_1; x++) { + output[x][y] = 0.0; + for (int i = 1; i <= x; i++) { + output[x][y] += kernel[i] * (input[x + i][y] - input[x - i][y]); + } + for (int i = x + 1; i < k; i++) { + output[x][y] += kernel[i] * (input[x + i][y] - input[i - x][y]); + } + } + for (int x = k_1; x <= nx - k; x++) { + output[x][y] = 0.0; + for (int i = 1; i < k; i++) { + output[x][y] += kernel[i] * (input[x + i][y] - input[x - i][y]); + } + } + for (int x = nx - k_1; x < nx; x++) { + output[x][y] = 0.0; + for (int i = 1; i < nx - x; i++) { + output[x][y] += kernel[i] * (input[x + i][y] - input[x - i][y]); + } + for (int i = nx - x; i < k; i++) { + output[x][y] += kernel[i] * (input[b - i - x][y] - input[x - i][y]); + } + } + } + return output; + } + + /** + * Convolution with an anti-symmetric kernel along x, for an image defined + * as a 1-D array. + */ + public static double[] convolveOddX(double[] input, double[] kernel, int nx, int ny) { + + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * nx - 2; + + double[] output = new double[nx * ny]; + + int idx = 0; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < k_1; x++) { + output[idx] = 0.0; + for (int i = 1; i <= x; i++) { + output[idx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + for (int i = x + 1; i < k; i++) { + output[idx] += kernel[i] * (input[idx + i] - input[i - x + y * nx]); + } + idx++; + } + for (int x = k_1; x <= nx - k; x++) { + output[idx] = 0.0; + for (int i = 1; i < k; i++) { + output[idx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + idx++; + } + for (int x = nx - k_1; x < nx; x++) { + output[idx] = 0.0; + for (int i = 1; i < nx - x; i++) { + output[idx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + for (int i = nx - x; i < k; i++) { + output[idx] += kernel[i] * (input[b - i - x + y * nx] - input[idx - i]); + } + idx++; + } + } + return output; + } + + /** + * Convolution with an anti-symmetric kernel along x, for an image defined + * as a 1-D array. + */ + public static float[] convolveOddX(float[] input, float[] kernel, int nx, int ny) { + + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * nx - 2; + + float[] output = new float[nx * ny]; + + int idx = 0; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < k_1; x++) { + output[idx] = 0.0f; + for (int i = 1; i <= x; i++) { + output[idx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + for (int i = x + 1; i < k; i++) { + output[idx] += kernel[i] * (input[idx + i] - input[i - x + y * nx]); + } + idx++; + } + for (int x = k_1; x <= nx - k; x++) { + output[idx] = 0.0f; + for (int i = 1; i < k; i++) { + output[idx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + idx++; + } + for (int x = nx - k_1; x < nx; x++) { + output[idx] = 0.0f; + for (int i = 1; i < nx - x; i++) { + output[idx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + for (int i = nx - x; i < k; i++) { + output[idx] += kernel[i] * (input[b - i - x + y * nx] - input[idx - i]); + } + idx++; + } + } + return output; + } + + /** + * Convolution with an anti-symmetric kernel along y. + */ + public static double[][] convolveOddY(double[][] input, double[] kernel) { + + int nx = input.length; + int ny = input.length; + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * ny - 2; + + double[][] output = new double[nx][ny]; + + for (int x = 0; x < nx; x++) { + for (int y = 0; y < k_1; y++) { + output[x][y] = 0.0; + for (int i = 1; i <= y; i++) { + output[x][y] += kernel[i] * (input[x][y + i] - input[x][y - i]); + } + for (int i = y + 1; i < k; i++) { + output[x][y] += kernel[i] * (input[x][y + i] - input[x][i - y]); + } + } + for (int y = k_1; y <= ny - k; y++) { + output[x][y] = 0.0; + for (int i = 1; i < k; i++) { + output[x][y] += kernel[i] * (input[x][y + i] - input[x][y - i]); + } + } + for (int y = ny - k_1; y < ny; y++) { + output[x][y] = 0.0; + for (int i = 1; i < ny - y; i++) { + output[x][y] += kernel[i] * (input[x][y + i] - input[x][y - i]); + } + for (int i = ny - y; i < k; i++) { + output[x][y] += kernel[i] * (input[x][b - i - y] - input[x][y - i]); + } + } + } + return output; + } + + /** + * Convolution with an anti-symmetric kernel along y, for an image defined + * as a 1-D array. + */ + public static double[] convolveOddY(double[] input, double[] kernel, int nx, int ny) { + + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * ny - 2; + + double[] output = new double[nx * ny]; + + int idx, inx; + for (int x = 0; x < nx; x++) { + for (int y = 0; y < k_1; y++) { + idx = x + y * nx; + output[idx] = 0.0; + for (int i = 1; i <= y; i++) { + inx = i * nx; + output[idx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + for (int i = y + 1; i < k; i++) { + output[idx] += kernel[i] * (input[idx + i * nx] - input[(i - y) * nx + x]); + } + } + for (int y = k_1; y <= ny - k; y++) { + idx = x + y * nx; + output[idx] = 0.0; + for (int i = 1; i < k; i++) { + inx = i * nx; + output[idx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + } + for (int y = ny - k_1; y < ny; y++) { + idx = x + y * nx; + output[idx] = 0.0; + for (int i = 1; i < ny - y; i++) { + inx = i * nx; + output[idx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + for (int i = ny - y; i < k; i++) { + output[idx] += kernel[i] * (input[(b - i - y) * nx + x] - input[idx - i * nx]); + } + } + } + return output; + } + + /** + * Convolution with an anti-symmetric kernel along y, for an image defined + * as a 1-D array. + */ + public static float[] convolveOddY(float[] input, float[] kernel, int nx, int ny) { + + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * ny - 2; + + float[] output = new float[nx * ny]; + + int idx, inx; + for (int x = 0; x < nx; x++) { + for (int y = 0; y < k_1; y++) { + idx = x + y * nx; + output[idx] = 0.0f; + for (int i = 1; i <= y; i++) { + inx = i * nx; + output[idx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + for (int i = y + 1; i < k; i++) { + output[idx] += kernel[i] * (input[idx + i * nx] - input[(i - y) * nx + x]); + } + } + for (int y = k_1; y <= ny - k; y++) { + idx = x + y * nx; + output[idx] = 0.0f; + for (int i = 1; i < k; i++) { + inx = i * nx; + output[idx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + } + for (int y = ny - k_1; y < ny; y++) { + idx = x + y * nx; + output[idx] = 0.0f; + for (int i = 1; i < ny - y; i++) { + inx = i * nx; + output[idx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + for (int i = ny - y; i < k; i++) { + output[idx] += kernel[i] * (input[(b - i - y) * nx + x] - input[idx - i * nx]); + } + } + } + return output; + } + + /** + * Convolution with a symmetric kernel along x, for an image defined as a + * 1-D array, in a region bounded by [x1..x2] and [y1..y2]. Usage: + * convolve{Even,Odd}X followed by convolve{Even,Odd}Y is mandatory. + */ + public static double[] convolveEvenX(double[] input, double[] kernel, int[] dims, int x1, int x2, int y1, int y2) { + + int nx = dims[0]; + int ny = dims[1]; + int nxy = nx * ny; + + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * nx - 2; + + double[] output = new double[(x2 - x1 + 1) * (y2 - y1 + 1)]; + int idx; + int odx = 0; + + if (x1 < k_1 && x2 > nx - k) { // 2 border conditions + for (int y = y1; y <= y2; y++) { + for (int x = x1; x < k_1; x++) { + idx = x + y * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i <= x; i++) { + output[odx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + for (int i = x + 1; i < k; i++) { + output[odx] += kernel[i] * (input[i - x + y * nx] + input[idx + i]); + } + odx++; + } + for (int x = k_1; x < x2 - k_1; x++) { + idx = x + y * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i < k; i++) { + output[odx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + odx++; + } + for (int x = x2 - k_1; x <= x2; x++) { + idx = x + y * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i < nx - x; i++) { + output[odx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + for (int i = nx - x; i < k; i++) { + output[odx] += kernel[i] * (input[b - i - x + y * nx] + input[idx - i]); + } + odx++; + } + } + } + else if (x1 < k_1 && x2 <= nx - k) { // left border conditions + for (int y = y1; y <= y2; y++) { + for (int x = x1; x < k_1; x++) { + idx = x + y * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i <= x; i++) { + output[odx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + for (int i = x + 1; i < k; i++) { + output[odx] += kernel[i] * (input[i - x + y * nx] + input[idx + i]); + } + odx++; + } + for (int x = k_1; x <= x2; x++) { + idx = x + y * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i < k; i++) { + output[odx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + odx++; + } + } + } + else if (x1 >= k_1 && x2 > nx - k) { // right border conditions + for (int y = y1; y <= y2; y++) { + for (int x = x1; x < x2 - k_1; x++) { + idx = x + y * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i < k; i++) { + output[odx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + odx++; + } + for (int x = x2 - k_1; x <= x2; x++) { + idx = x + y * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i < nx - x; i++) { + output[odx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + for (int i = nx - x; i < k; i++) { + output[odx] += kernel[i] * (input[b - i - x + y * nx] + input[idx - i]); + } + odx++; + } + } + } + else { // no border conditions + for (int y = y1; y <= y2; y++) { + for (int x = x1; x <= x2; x++) { + idx = x + y * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i < k; i++) { + output[odx] += kernel[i] * (input[idx - i] + input[idx + i]); + } + odx++; + } + } + } + return output; + } + + /** + * Convolution with an anti-symmetric kernel along x, for an image defined + * as a 1-D array, in a region bounded by [x1..x2] and [y1..y2]. Usage: + * convolve{Even,Odd}X followed by convolve{Even,Odd}Y is mandatory. + */ + public static double[] convolveOddX(double[] input, double[] kernel, int[] dims, int x1, int x2, int y1, int y2) { + + int nx = dims[0]; + int ny = dims[1]; + int nxy = nx * ny; + + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * nx - 2; + + double[] output = new double[(x2 - x1 + 1) * (y2 - y1 + 1)]; + + int idx; + int odx = 0; + + if (x1 < k_1 && x2 > nx - k) { // 2 border conditions + for (int y = y1; y <= y2; y++) { + for (int x = x1; x < k_1; x++) { + idx = x + y * nx; + output[odx] = 0.0; + for (int i = 1; i <= x; i++) { + output[odx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + for (int i = x + 1; i < k; i++) { + output[odx] += kernel[i] * (input[idx + i] - input[i - x + y * nx]); + } + odx++; + } + for (int x = k_1; x < x2 - k_1; x++) { + idx = x + y * nx; + output[odx] = 0.0; + for (int i = 1; i < k; i++) { + output[odx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + odx++; + } + for (int x = x2 - k_1; x <= x2; x++) { + idx = x + y * nx; + output[odx] = 0.0; + for (int i = 1; i < nx - x; i++) { + output[odx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + for (int i = nx - x; i < k; i++) { + output[odx] += kernel[i] * (input[b - i - x + y * nx] - input[idx - i]); + } + odx++; + } + } + } + else if (x1 < k_1 && x2 <= nx - k) { // left border conditions + for (int y = y1; y <= y2; y++) { + for (int x = x1; x < k_1; x++) { + idx = x + y * nx; + output[odx] = 0.0; + for (int i = 1; i <= x; i++) { + output[odx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + for (int i = x + 1; i < k; i++) { + output[odx] += kernel[i] * (input[idx + i] - input[i - x + y * nx]); + } + odx++; + } + for (int x = k_1; x <= x2; x++) { + idx = x + y * nx; + output[odx] = 0.0; + for (int i = 1; i < k; i++) { + output[odx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + odx++; + } + } + } + else if (x1 >= k_1 && x2 > nx - k) { // right border conditions + for (int y = y1; y <= y2; y++) { + for (int x = x1; x < x2 - k_1; x++) { + idx = x + y * nx; + output[odx] = 0.0; + for (int i = 1; i < k; i++) { + output[odx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + odx++; + } + for (int x = x2 - k_1; x <= x2; x++) { + idx = x + y * nx; + output[odx] = 0.0; + for (int i = 1; i < nx - x; i++) { + output[odx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + for (int i = nx - x; i < k; i++) { + output[odx] += kernel[i] * (input[b - i - x + y * nx] - input[idx - i]); + } + odx++; + } + } + } + else { // no border conditions + for (int y = y1; y <= y2; y++) { + for (int x = x1; x <= x2; x++) { + idx = x + y * nx; + output[odx] = 0.0; + for (int i = 1; i < k; i++) { + output[odx] += kernel[i] * (input[idx + i] - input[idx - i]); + } + odx++; + } + } + } + return output; + } + + /** + * Convolution with a symmetric kernel along x, for an image defined as a + * 1-D array, in a region bounded by [x1..x2] and [y1..y2]. Usage: + * convolve{Even,Odd}X followed by convolve{Even,Odd}Y is mandatory. + */ + public static double[] convolveEvenY(double[] input, double[] kernel, int[] dims, int y1, int y2) { + + int nx = dims[0]; + int ny = dims[1]; + int nxy = nx * ny; + + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * ny - 2; + double[] output = new double[nx * (y2 - y1 + 1)]; + + int idx, inx; + int odx = 0; + + if (y1 < k_1 && y2 > ny - k) { // 2 border conditions + for (int x = 0; x < nx; x++) { + for (int y = y1; y < k_1; y++) { + idx = x + y * nx; + odx = idx - y1 * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i <= y; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + for (int i = y + 1; i < k; i++) { + output[odx] += kernel[i] * (input[(i - y) * nx + x] + input[idx + i * nx]); + } + } + for (int y = k_1; y < y2 - k_1; y++) { + idx = x + y * nx; + odx = idx - y1 * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i < k; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + } + for (int y = y2 - k_1; y <= y2; y++) { + idx = x + y * nx; + odx = idx - y1 * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i < ny - y; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + for (int i = ny - y; i < k; i++) { + output[odx] += kernel[i] * (input[(b - i - y) * nx + x] + input[idx - i * nx]); + } + } + } + } + else if (y1 < k_1 && y2 <= ny - k) { // top border conditions + for (int x = 0; x < nx; x++) { + for (int y = y1; y < k_1; y++) { + idx = x + y * nx; + odx = idx - y1 * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i <= y; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + for (int i = y + 1; i < k; i++) { + output[odx] += kernel[i] * (input[(i - y) * nx + x] + input[idx + i * nx]); + } + } + for (int y = k_1; y <= y2; y++) { + idx = x + y * nx; + odx = idx - y1 * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i < k; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + } + } + } + else if (y1 >= k_1 && y2 > ny - k) { // bottom border conditions + for (int x = 0; x < nx; x++) { + for (int y = y1; y < ny - k_1; y++) { + idx = x + y * nx; + odx = idx - y1 * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i < k; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + } + for (int y = ny - k_1; y <= y2; y++) { + idx = x + y * nx; + odx = idx - y1 * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i < ny - y; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + for (int i = ny - y; i < k; i++) { + output[odx] += kernel[i] * (input[(b - i - y) * nx + x] + input[idx - i * nx]); + } + } + } + } + else { // no border conditions + for (int x = 0; x < nx; x++) { + for (int y = y1; y <= y2; y++) { + idx = x + y * nx; + odx = idx - y1 * nx; + output[odx] = kernel[0] * input[idx]; + for (int i = 1; i < k; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx - inx] + input[idx + inx]); + } + } + } + } + return output; + } + + /** + * Convolution with an anti-symmetric kernel along y, for an image defined + * as a 1-D array, in a region bounded by [x1..x2] and [y1..y2]. Usage: + * convolve{Even,Odd}X followed by convolve{Even,Odd}Y is mandatory. + */ + public static double[] convolveOddY(double[] input, double[] kernel, int[] dims, int y1, int y2) { + + int nx = dims[0]; + int ny = dims[1]; + int nxy = nx * ny; + + int k = kernel.length; + int k_1 = k - 1; + int b = 2 * ny - 2; + + double[] output = new double[nx * (y2 - y1 + 1)]; + + int idx, inx; + int odx = 0; + + if (y1 < k_1 && y2 > ny - k) { // 2 border conditions + for (int x = 0; x < nx; x++) { + for (int y = y1; y < k_1; y++) { + idx = x + y * nx; + odx = x + (y - y1) * nx; + output[odx] = 0.0; + for (int i = 1; i <= y; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + for (int i = y + 1; i < k; i++) { + output[odx] += kernel[i] * (input[idx + i * nx] - input[(i - y) * nx + x]); + } + } + for (int y = k_1; y <= ny - k; y++) { + idx = x + y * nx; + odx = x + (y - y1) * nx; + output[odx] = 0.0; + for (int i = 1; i < k; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + } + for (int y = ny - k_1; y <= y2; y++) { + idx = x + y * nx; + odx = x + (y - y1) * nx; + output[odx] = 0.0; + for (int i = 1; i < ny - y; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + for (int i = ny - y; i < k; i++) { + output[odx] += kernel[i] * (input[(b - i - y) * nx + x] - input[idx - i * nx]); + } + } + } + } + else if (y1 < k_1 && y2 <= ny - k) { // left border conditions + for (int x = 0; x < nx; x++) { + for (int y = y1; y < k_1; y++) { + idx = x + y * nx; + odx = x + (y - y1) * nx; + output[odx] = 0.0; + for (int i = 1; i <= y; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + for (int i = y + 1; i < k; i++) { + output[odx] += kernel[i] * (input[idx + i * nx] - input[(i - y) * nx + x]); + } + } + for (int y = k_1; y <= y2; y++) { + idx = x + y * nx; + odx = x + (y - y1) * nx; + output[odx] = 0.0; + for (int i = 1; i < k; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + } + } + } + else if (y1 >= k_1 && y2 > ny - k) { // right border conditions + for (int x = 0; x < nx; x++) { + for (int y = y1; y <= ny - k; y++) { + idx = x + y * nx; + odx = x + (y - y1) * nx; + output[odx] = 0.0; + for (int i = 1; i < k; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + } + for (int y = ny - k_1; y < y2; y++) { + idx = x + y * nx; + odx = x + (y - y1) * nx; + output[odx] = 0.0; + for (int i = 1; i < ny - y; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + for (int i = ny - y; i < k; i++) { + output[odx] += kernel[i] * (input[(b - i - y) * nx + x] - input[idx - i * nx]); + } + } + } + } + else { // no border conditions + for (int x = 0; x < nx; x++) { + for (int y = y1; y <= y2; y++) { + idx = x + y * nx; + odx = x + (y - y1) * nx; + output[odx] = 0.0; + for (int i = 1; i < k; i++) { + inx = i * nx; + output[odx] += kernel[i] * (input[idx + inx] - input[idx - inx]); + } + } + } + } + return output; + } +} \ No newline at end of file diff --git a/src/bilib/src/ijtools/IJmath.java b/src/bilib/src/ijtools/IJmath.java new file mode 100644 index 0000000..89eebb9 --- /dev/null +++ b/src/bilib/src/ijtools/IJmath.java @@ -0,0 +1,549 @@ +package ijtools; + +/** + * Various methods for polynomial manipulation and basic image arithmetic. + * Polynomial are defined by the set of coefficients a[]: f(x) = a[0] + a[1]*x + + * a[2]*x^2 + ... + * + * @author Francois Aguet + * @version 1.0 + */ +public class IJmath { + + /** + * Returns the real roots of a quadratic polynomial. The input is of the + * form: x^2 + a1*x + a0. The size of the returned array corresponds to the + * number of real roots. + */ + public static double[] quadraticRoots(double a1, double a0) { + + double[] roots; + double delta = a1 * a1 - 4.0 * a0; + if (delta < 0.0) { + roots = new double[0]; + } + else { + roots = new double[2]; + delta = Math.sqrt(delta); + roots[0] = (-a1 + delta) / 2.0; + roots[1] = (-a1 - delta) / 2.0; + } + return roots; + } + + /** + * Returns the real roots of a cubic polynomial. The input is of the form: + * x^3 + a2*x^2 + a1*x + a0. References: Numerical Recipes in C, pp. + * 184-185, http://mathworld.wolfram.com/CubicFormula.html The size of the + * returned array corresponds to the number of real roots. + */ + public static double[] cubicRoots(double a2, double a1, double a0) { + + double theta, A, B; + double q = a2 * a2 - 3.0 * a1; + double Q = q / 9.0; + double R = (2.0 * a2 * a2 * a2 - 9.0 * a2 * a1 + 27.0 * a0) / 54.0; + double roots[]; + double signR, delta; + + if (a0 == 0.0) { + delta = a2 * a2 - 4.0 * a1; + if (delta >= 0.0) { + roots = new double[3]; + roots[0] = 0.0; + roots[1] = (-a2 + Math.sqrt(delta)) / 2.0; + roots[2] = (-a2 - Math.sqrt(delta)) / 2.0; + } + else { + roots = new double[1]; + roots[0] = 0.0; + } + } + else { + if (R >= 0.0) { + signR = 1.0; + } + else { + signR = -1.0; + } + if (R * R <= Q * Q * Q) { // REF: R2 < Q3 + roots = new double[3]; + theta = Math.acos(R / Math.sqrt(Q * Q * Q)); + roots[0] = -2.0 * Math.sqrt(Q) * Math.cos(theta / 3.0) - (a2 / 3.0); + roots[1] = -2.0 * Math.sqrt(Q) * Math.cos((theta + 2.0 * Math.PI) / 3.0) - (a2 / 3.0); + roots[2] = -2.0 * Math.sqrt(Q) * Math.cos((theta - 2.0 * Math.PI) / 3.0) - (a2 / 3.0); + } + else { + A = -signR * Math.pow(Math.abs(R) + Math.sqrt(R * R - Q * Q * Q), 1.0 / 3.0); + if (A == 0.0) { + B = 0.0; + } + else { + B = Q / A; + } + roots = new double[1]; + roots[0] = A + B - (a2 / 3.0); + } + } + return roots; + } + + /** + * Returns the real roots of a quartic polynomial. The input is of the form: + * x^4 + a3*x^3 + a2*x^2 + a1*x + a0. Reference: + * http://mathworld.wolfram.com/QuarticEquation.html The size of the + * returned array corresponds to the number of real roots. + */ + public static double[] quarticRoots(double a3, double a2, double a1, double a0) { + + double[] roots; + + if (a3 == 0.0 && a1 == 0.0) { // quadratic equation in x^2 + double[] x2roots = quadraticRoots(a2, a0); + if (x2roots.length != 0) { + if ((x2roots[0] >= 0.0) && (x2roots[1] >= 0.0)) { + roots = new double[4]; + roots[0] = Math.sqrt(x2roots[0]); + roots[1] = -roots[0]; + roots[2] = Math.sqrt(x2roots[1]); + roots[3] = roots[2]; + } + else if ((x2roots[0] < 0.0) && (x2roots[1] < 0.0)) { + roots = new double[0]; + } + else { + roots = new double[2]; + if (x2roots[0] >= 0.0) { + roots[0] = Math.sqrt(x2roots[0]); + roots[1] = -roots[0]; + } + else { + roots[0] = Math.sqrt(x2roots[1]); + roots[1] = -roots[0]; + } + } + } + else { + roots = new double[0]; + } + } + else { // solve quartic + + double[] crr = cubicRoots(-a2, a1 * a3 - 4.0 * a0, 4.0 * a2 * a0 - a1 * a1 - a3 * a3 * a0); + double y1 = crr[0]; + + double deltaR = 0.25 * a3 * a3 - a2 + y1; + double R; + + if (deltaR < 0.0) { // 4 complex roots + roots = new double[0]; + } + else { + R = Math.sqrt(deltaR); + double D, E; + double deltaD, deltaE; + if (R == 0.0) { + deltaD = 0.75 * a3 * a3 - 2.0 * a2 + 2.0 * Math.sqrt(y1 * y1 - 4.0 * a0); + deltaE = 0.75 * a3 * a3 - 2.0 * a2 - 2.0 * Math.sqrt(y1 * y1 - 4.0 * a0); + if (deltaD >= 0.0) { + D = Math.sqrt(deltaD); + if (deltaE >= 0.0) { + E = Math.sqrt(deltaE); + roots = new double[4]; + roots[0] = -0.25 * a3 + 0.5 * D; + roots[1] = -0.25 * a3 - 0.5 * D; + roots[2] = -0.25 * a3 + 0.5 * E; + roots[3] = -0.25 * a3 - 0.5 * E; + } + else { + roots = new double[2]; + roots[0] = -0.25 * a3 + 0.5 * D; + roots[1] = -0.25 * a3 - 0.5 * D; + } + } + else { + if (deltaE >= 0.0) { + E = Math.sqrt(deltaE); + roots = new double[2]; + roots[0] = -0.25 * a3 + 0.5 * E; + roots[1] = -0.25 * a3 - 0.5 * E; + } + else { + roots = new double[0]; + } + } + } + else { + deltaD = 0.75 * a3 * a3 - R * R - 2.0 * a2 + (a3 * a2 - 2.0 * a1 - 0.25 * a3 * a3 * a3) / R; + deltaE = 0.75 * a3 * a3 - R * R - 2.0 * a2 - (a3 * a2 - 2.0 * a1 - 0.25 * a3 * a3 * a3) / R; + if (deltaD >= 0.0) { + D = Math.sqrt(deltaD); + if (deltaE >= 0.0) { + E = Math.sqrt(deltaE); + roots = new double[4]; + roots[0] = -0.25 * a3 + 0.5 * R + 0.5 * D; + roots[1] = -0.25 * a3 + 0.5 * R - 0.5 * D; + roots[2] = -0.25 * a3 - 0.5 * R + 0.5 * E; + roots[3] = -0.25 * a3 - 0.5 * R - 0.5 * E; + } + else { + roots = new double[2]; + roots[0] = -0.25 * a3 + 0.5 * R + 0.5 * D; + roots[1] = -0.25 * a3 + 0.5 * R - 0.5 * D; + } + } + else { + if (deltaE >= 0.0) { + E = Math.sqrt(deltaE); + roots = new double[2]; + roots[0] = -0.25 * a3 - 0.5 * R + 0.5 * E; + roots[1] = -0.25 * a3 - 0.5 * R - 0.5 * E; + } + else { + roots = new double[0]; + } + } + } + } + } + return roots; + } + + /** + * Divides a polynomial by a known root. + */ + public static double[] divPolyByRoot(double[] coeffs, double root) { + int nx = coeffs.length; + double[] out = new double[nx - 1]; + double rem = coeffs[nx - 1]; + for (int i = nx - 2; i >= 0; i--) { + out[i] = rem; + rem = coeffs[i] + rem * root; + } + return out; + } + + // divide by (x - (a + bi))(x - (a - bi)) = x^2 - a^2 x + a^2 + b^2 + /** + * Divides a polynomial by a pair of known conjugate roots, i.e., (x - (a + + * bi))(x - (a - bi)). + */ + public static double[] divPolyByConjRoots(double[] coeffs, double a, double b) { + double t1 = 2.0 * a; + double t2 = a * a + b * b; + int n = coeffs.length - 2; + double[] out = new double[n]; + out[n - 1] = coeffs[n + 1]; + out[n - 2] = coeffs[n] + t1 * out[n - 1]; + for (int i = n - 1; i >= 2; i--) { + out[i - 2] = coeffs[i] + t1 * out[i - 1] - t2 * out[i]; + } + return out; + } + + /** + * Evaluates a polynomial in x0. f(x) = a[0] + a[1]*x + a[2]*x^2 + ... + */ + public static double evalPoly(double[] a, double x0) { + int n = a.length; + double f = a[n - 1]; + for (int i = n - 2; i >= 0; i--) { + f = f * x0 + a[i]; + } + return f; + } + + /** + * Evaluates a polynomial and its first derivative in x0. f(x) = a[0] + + * a[1]*x + a[2]*x^2 + ... + * + * @return A length two array containing f(x0) and f'(x0), respectively. + */ + public static double[] evalPolyD(double[] a, double x0) { + int n = a.length; + double[] f = new double[2]; + f[0] = a[n - 1]; + f[1] = 0.0; + for (int i = n - 2; i >= 0; i--) { + f[1] = f[1] * x0 + f[0]; + f[0] = f[0] * x0 + a[i]; + } + return f; + } + + // Laguerre's method, adapted from 'Numerical Recipes' for real + // coefficients, complex roots + + /** + * Laguerre's root finding method for real-valued polynomials. f(x) = a[0] + + * a[1]*x + a[2]*x^2 + ... The returned root can be complex. + * + * @return An array containing the real and imaginary part of the root a + + * i*b, i.e., {a,b}. + */ + public static double[] laguerre(double[] a, double x0) { + // public static double[] laguerre(double[] a, double x0, int[] + // converged) { + + int N = a.length - 1; // degree of the polynomial + double[] x = complex(x0, 0.0); // a (complex) root + double[] f, df, d2f; + double[] G, G2, H, sq, Gplus, Gminus, dx; + double[] delta; + double absx, error, abs_plus, abs_minus; + final double[] fracl = { 0.0, 0.5, 0.25, 0.75, 0.125, 0.375, 0.625, 0.875, 1.0 }; // fractions + // used + // to + // break + // limit + // cycles + int MT = 10; + // int maxiter = MT*fracl.length; + int maxiter = 30; + double tol = 1.0e-15; + int[] converged = new int[1]; + converged[0] = -2; + + for (int iter = 1; iter <= maxiter; iter++) { + + f = complex(a[N], 0.0); + df = complex(0.0, 0.0); + d2f = complex(0.0, 0.0); + absx = cabs(x); + error = cabs(f); + for (int k = N - 1; k >= 0; k--) { + d2f = cadd(cmul(d2f, x), df); + df = cadd(cmul(df, x), f); + f = cadd(cmul(f, x), complex(a[k], 0.0)); + error = cabs(f) + absx * error; + } + if (cabs(f) <= error * tol) { // x is a root + converged[0] = 2; + break; + } + G = cdiv(df, f); + G2 = cmul(G, G); + H = csub(G2, cdiv(d2f, f)); + delta = rcmul(N - 1, csub(rcmul(N, H), G2)); + sq = csqrt(delta); + Gplus = cadd(G, sq); + Gminus = csub(G, sq); + abs_plus = cabs(Gplus); + abs_minus = cabs(Gminus); + if (abs_minus > abs_plus) { + Gplus = Gminus; + abs_plus = abs_minus; + } // Gplus is largest absolute denominator + if (abs_plus > 0.0) { + dx = cdiv(complex(N, 0.0), Gplus); + } + else { // G = 0, H = 0, freak case + dx = rcmul(1 + absx, complex(Math.cos(iter), Math.sin(iter))); + dx = complex(1.0, 0.0); + } + /* + * if (cabs(dx) < tol) { // converged //IJ.write("test"); break; } + */ + if (iter % MT != 0) { + x = csub(x, dx); + } + else { // fractional step to break limit cycles + x = csub(x, rcmul(fracl[iter / MT], dx)); + } + } + return x; + } + + private static double[] complex(double a, double b) { + double[] c = { a, b }; + return c; + } + + private static double[] cadd(double[] c1, double[] c2) { + double[] c = { c1[0] + c2[0], c1[1] + c2[1] }; + return c; + } + + private static double[] csub(double[] c1, double[] c2) { + double[] c = { c1[0] - c2[0], c1[1] - c2[1] }; + return c; + } + + private static double cabs(double[] c) { + return Math.sqrt(c[0] * c[0] + c[1] * c[1]); + } + + private static double[] rcmul(double a, double[] c) { + double[] s = { a * c[0], a * c[1] }; + return s; + } + + private static double[] cmul(double[] c1, double[] c2) { + double[] c = { c1[0] * c2[0] - c1[1] * c2[1], c1[0] * c2[1] + c2[0] * c1[1] }; + return c; + } + + private static double[] cdiv(double[] c1, double[] c2) { + double d = c2[0] * c2[0] + c2[1] * c2[1]; + double[] c = { (c1[0] * c2[0] + c1[1] * c2[1]) / d, (c2[0] * c1[1] - c1[0] * c2[1]) / d }; + return c; + } + + private static double[] csqrt(double[] c) { + double t = cabs(c); + double d = Math.sqrt(2); + double[] s = { Math.sqrt(t + c[0]) / d, csign(c[1]) * Math.sqrt(t - c[0]) / d }; + return s; + } + + private static double csign(double x) { + if (x >= 0.0) { + return 1.0; + } + else { + return -1.0; + } + } + + /** + * Computes the mean. + */ + public static double mean(double[] input) { + return sum(input) / input.length; + } + + /** + * Computes the mean. + */ + public static double mean(double[][] input) { + return sum(input) / (input.length * input[0].length); + } + + /** + * Computes the sum. + */ + public static double sum(double[] input) { + int nxy = input.length; + double s = 0.0; + for (int k = 0; k < nxy; k++) { + s += input[k]; + } + return s; + } + + /** + * Computes the sum. + */ + public static double sum(double[][] input) { + int nx = input.length; + int ny = input[0].length; + double s = 0.0; + for (int x = 0; x < nx; x++) { + for (int y = 0; y < ny; y++) { + s += input[x][y]; + } + } + return s; + } + + /** + * Adds the two input images. + */ + public static double[] add(double[] a, double[] b) { + int nxy = a.length; + double[] out = new double[nxy]; + for (int k = 0; k < nxy; k++) { + out[k] = a[k] + b[k]; + } + return out; + } + + /** + * Adds the two input images. + */ + public static double[][] add(double[][] a, double[][] b) { + int nx = a.length; + int ny = a[0].length; + double[][] out = new double[nx][ny]; + for (int x = 0; x < nx; x++) { + for (int y = 0; y < ny; y++) { + out[x][y] = a[x][y] + b[x][y]; + } + } + return out; + } + + /** + * Subtracts the two input images. + */ + public static double[] subtract(double[] a, double[] b) { + int nxy = a.length; + double[] out = new double[nxy]; + for (int k = 0; k < nxy; k++) { + out[k] = a[k] - b[k]; + } + return out; + } + + /** + * Subtracts the two input images. + */ + public static double[][] subtract(double[][] a, double[][] b) { + int nx = a.length; + int ny = a[0].length; + double[][] out = new double[nx][ny]; + for (int x = 0; x < nx; x++) { + for (int y = 0; y < ny; y++) { + out[x][y] = a[x][y] - b[x][y]; + } + } + return out; + } + + /** + * Multiplies the two input images. + */ + public static double[] mutiply(double[] a, double[] b) { + int nxy = a.length; + double[] out = new double[nxy]; + for (int k = 0; k < nxy; k++) { + out[k] = a[k] * b[k]; + } + return out; + } + + /** + * Multiplies the two input images. + */ + public static double[][] multiply(double[][] a, double[][] b) { + int nx = a.length; + int ny = a[0].length; + double[][] out = new double[nx][ny]; + for (int x = 0; x < nx; x++) { + for (int y = 0; y < ny; y++) { + out[x][y] = a[x][y] * b[x][y]; + } + } + return out; + } + + /* + * public static double newton(double[] coeffs, double x1, double x2) { + * + * if (Math.signum(SteerableUtilities.evalPoly(coeffs, + * x1)*SteerableUtilities.evalPoly(coeffs, x2)) != -1.0) { + * //IJ.write("error newton"+(x1*x2)); } + * + * double root = (x1+x2)/2.0; int maxIter = 40; double dx; double[] f; + * double acc = 1e-30; + * + * for (int i=0;i<maxIter;i++) { f = evalPolyD(coeffs, root); dx = + * f[0]/f[1]; root -= dx; + * + * //if ((x1-root)*(root-x2) < 0.0) { //IJ.write("Newton out of bounds"); // + * root = 111.13; //} + * + * if (Math.abs(dx) < acc) { break; } } return root; } + */ + +} diff --git a/src/bilib/src/ijtools/IJtools.java b/src/bilib/src/ijtools/IJtools.java new file mode 100644 index 0000000..610c8c0 --- /dev/null +++ b/src/bilib/src/ijtools/IJtools.java @@ -0,0 +1,847 @@ +package ijtools; + +import ij.*; +import ij.process.*; + +/** + * Utility class for displaying, loading and performing basic arithmetic + * operations on images. Where applicable, arrays are in lexicographical order. + * + * @author Francois Aguet + * @version 1.0 + */ +public class IJtools { + + /** + * Displays the image in a new window in in ImageJ. + */ + public static void show(String name, double[] pixels, int nx, int ny) { + (new ImagePlus(name, new FloatProcessor(nx, ny, pixels))).show(); + } + + /** + * Displays the image in a new window in in ImageJ. + */ + public static void show(String name, float[] pixels, int nx, int ny) { + (new ImagePlus(name, new FloatProcessor(nx, ny, pixels, null))).show(); + } + + /** + * Displays the image in a new window in in ImageJ. + */ + public static void show(String name, int[] pixels, int nx, int ny) { + (new ImagePlus(name, new FloatProcessor(nx, ny, pixels))).show(); + } + + /** + * Displays the image in a new window in in ImageJ. + */ + public static void show(String name, short[] pixels, int nx, int ny) { + (new ImagePlus(name, new ShortProcessor(nx, ny, pixels, null))).show(); + } + + /** + * Displays the image in a new window in in ImageJ. + */ + public static void show(String name, byte[] pixels, int nx, int ny) { + (new ImagePlus(name, new ByteProcessor(nx, ny, pixels, null))).show(); + } + + /** + * Displays the image in a new window in in ImageJ. + */ + public static void showRGB(String name, int[] pixels, int nx, int ny) { + (new ImagePlus(name, new ColorProcessor(nx, ny, pixels))).show(); + } + + /** + * Displays the image in a new window in in ImageJ. + */ + public static void show(String name, double[][] input) { + int nx = input.length; + int ny = input[0].length; + double[] pixels = new double[nx * ny]; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < nx; x++) { + pixels[x + y * nx] = input[x][y]; + } + } + show(name, pixels, nx, ny); + } + + /** + * Displays the image in a new window in in ImageJ. + */ + public static void show(String name, float[][] input) { + int nx = input.length; + int ny = input[0].length; + float[] pixels = new float[nx * ny]; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < nx; x++) { + pixels[x + y * nx] = input[x][y]; + } + } + show(name, pixels, nx, ny); + } + + /** + * Displays the image in a new window in in ImageJ. + */ + public static void show(String name, int[][] input) { + int nx = input.length; + int ny = input[0].length; + int[] pixels = new int[nx * ny]; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < nx; x++) { + pixels[x + y * nx] = input[x][y]; + } + } + show(name, pixels, nx, ny); + } + + /** + * Displays the image in a new window in in ImageJ. + */ + public static void show(String name, short[][] input) { + int nx = input.length; + int ny = input[0].length; + short[] pixels = new short[nx * ny]; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < nx; x++) { + pixels[x + y * nx] = input[x][y]; + } + } + show(name, pixels, nx, ny); + } + + /** + * Displays the image in a new window in in ImageJ. + */ + public static void show(String name, byte[][] input) { + int nx = input.length; + int ny = input[0].length; + byte[] pixels = new byte[nx * ny]; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < nx; x++) { + pixels[x + y * nx] = input[x][y]; + } + } + show(name, pixels, nx, ny); + } + + /** + * Displays the image in a new window in in ImageJ. + */ + public static void showRGB(String name, int[][] input) { + int nx = input.length; + int ny = input[0].length; + int[] pixels = new int[nx * ny]; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < nx; x++) { + pixels[x + y * nx] = input[x][y]; + } + } + showRGB(name, pixels, nx, ny); + } + + /** + * Displays the stack in a new window in in ImageJ. + */ + public static void showStack(String name, double[] input, int nx, int ny, int nz) { + ImageStack istack = new ImageStack(nx, ny); + for (int z = 0; z < nz; z++) { + double[] pixels = new double[nx * ny]; + for (int i = 0; i < nx * ny; i++) { + pixels[i] = input[i + z * nx * ny]; + } + istack.addSlice("", new FloatProcessor(nx, ny, pixels)); + } + (new ImagePlus(name, istack)).show(); + } + + /** + * Displays the stack in a new window in in ImageJ. + */ + public static void showStack(String name, float[] input, int nx, int ny, int nz) { + ImageStack istack = new ImageStack(nx, ny); + for (int z = 0; z < nz; z++) { + float[] pixels = new float[nx * ny]; + for (int i = 0; i < nx * ny; i++) { + pixels[i] = input[i + z * nx * ny]; + } + istack.addSlice("", pixels); + } + (new ImagePlus(name, istack)).show(); + } + + /** + * Displays the stack in a new window in in ImageJ. + */ + public static void showStack(String name, int[] input, int nx, int ny, int nz) { + ImageStack istack = new ImageStack(nx, ny); + int[] pixels = new int[nx * ny]; + for (int z = 0; z < nz; z++) { + for (int i = 0; i < nx * ny; i++) { + pixels[i] = input[i + z * nx * ny]; + } + istack.addSlice("", new FloatProcessor(nx, ny, pixels)); + } + (new ImagePlus(name, istack)).show(); + } + + /** + * Displays the stack in a new window in in ImageJ. + */ + public static void showStack(String name, short[] input, int nx, int ny, int nz) { + ImageStack istack = new ImageStack(nx, ny); + for (int z = 0; z < nz; z++) { + short[] pixels = new short[nx * ny]; + for (int i = 0; i < nx * ny; i++) { + pixels[i] = input[i + z * nx * ny]; + } + istack.addSlice("", pixels); + } + (new ImagePlus(name, istack)).show(); + } + + /** + * Displays the stack in a new window in in ImageJ. + */ + public static void showStack(String name, byte[] input, int nx, int ny, int nz) { + ImageStack istack = new ImageStack(nx, ny); + for (int z = 0; z < nz; z++) { + byte[] pixels = new byte[nx * ny]; + for (int i = 0; i < nx * ny; i++) { + pixels[i] = input[i + z * nx * ny]; + } + istack.addSlice("", pixels); + } + (new ImagePlus(name, istack)).show(); + } + + /** + * Displays the stack in a new window in in ImageJ. + */ + public static void showStackRGB(String name, int[] input, int nx, int ny, int nz) { + ImageStack istack = new ImageStack(nx, ny); + int[] pixels = new int[nx * ny]; + for (int z = 0; z < nz; z++) { + for (int i = 0; i < nx * ny; i++) { + pixels[i] = input[i + z * nx * ny]; + } + istack.addSlice("", new ColorProcessor(nx, ny, pixels)); + } + (new ImagePlus(name, istack)).show(); + } + + /** + * Displays the stack in a new window in in ImageJ. Input structure is + * [z][xy]. + */ + public static void showStack(String name, double[][] input, int nx, int ny) { + int nz = input.length; + ImageStack istack = new ImageStack(nx, ny); + double max = -Double.MAX_VALUE; + double min = Double.MAX_VALUE; + double t; + for (int z = 0; z < nz; z++) { + double[] pixels = new double[nx * ny]; + for (int i = 0; i < nx * ny; i++) { + t = input[z][i]; + if (t > max) { + max = t; + } + if (t < min) { + min = t; + } + } + istack.addSlice("", pixels); + } + ImagePlus imp = new ImagePlus(name, istack); + imp.getProcessor().setMinAndMax(min, max); + imp.show(); + } + + /** + * Displays the stack in a new window in in ImageJ. Input structure is + * [z][xy]. + */ + public static void showStack(String name, float[][] input, int nx, int ny) { + int nz = input.length; + ImageStack istack = new ImageStack(nx, ny); + float max = -Float.MAX_VALUE; + float min = Float.MAX_VALUE; + float t; + for (int z = 0; z < nz; z++) { + for (int i = 0; i < nx * ny; i++) { + t = input[z][i]; + if (t > max) { + max = t; + } + if (t < min) { + min = t; + } + } + istack.addSlice("", input[z]); + } + ImagePlus imp = new ImagePlus(name, istack); + imp.getProcessor().setMinAndMax(min, max); + imp.show(); + } + + /** + * Displays the stack in a new window in in ImageJ. Input structure is + * [z][xy]. + */ + public static void showStack(String name, int[][] input, int nx, int ny) { + int nz = input.length; + ImageStack istack = new ImageStack(nx, ny); + int max = -Integer.MAX_VALUE; + int min = Integer.MAX_VALUE; + int t; + int[] pixels = new int[nx * ny]; + for (int z = 0; z < nz; z++) { + for (int i = 0; i < nx * ny; i++) { + t = input[z][i]; + if (t > max) { + max = t; + } + if (t < min) { + min = t; + } + } + istack.addSlice("", new FloatProcessor(nx, ny, pixels)); + } + ImagePlus imp = new ImagePlus(name, istack); + imp.getProcessor().setMinAndMax(min, max); + imp.show(); + } + + /** + * Displays the stack in a new window in in ImageJ. Input structure is + * [z][xy]. + */ + public static void showStack(String name, short[][] input, int nx, int ny) { + int nz = input.length; + ImageStack istack = new ImageStack(nx, ny); + short max = -Short.MAX_VALUE; + short min = Short.MAX_VALUE; + short t; + for (int z = 0; z < nz; z++) { + short[] pixels = new short[nx * ny]; + for (int i = 0; i < nx * ny; i++) { + t = input[z][i]; + if (t > max) { + max = t; + } + if (t < min) { + min = t; + } + } + istack.addSlice("", pixels); + } + ImagePlus imp = new ImagePlus(name, istack); + imp.getProcessor().setMinAndMax(min, max); + imp.show(); + } + + /** + * Displays the stack in a new window in in ImageJ. Input structure is + * [z][xy]. + */ + public static void showStack(String name, byte[][] input, int nx, int ny) { + int nz = input.length; + ImageStack istack = new ImageStack(nx, ny); + byte max = -Byte.MAX_VALUE; + byte min = Byte.MAX_VALUE; + byte t; + for (int z = 0; z < nz; z++) { + byte[] pixels = new byte[nx * ny]; + for (int i = 0; i < nx * ny; i++) { + t = input[z][i]; + if (t > max) { + max = t; + } + if (t < min) { + min = t; + } + } + istack.addSlice("", pixels); + } + ImagePlus imp = new ImagePlus(name, istack); + imp.getProcessor().setMinAndMax(min, max); + imp.show(); + } + + /** + * Displays the stack in a new window in in ImageJ. Input structure is + * [z][xy]. + */ + public static void showStackRGB(String name, int[][] input, int nx, int ny) { + int nz = input.length; + ImageStack istack = new ImageStack(nx, ny); + for (int z = 0; z < nz; z++) { + istack.addSlice("", new ColorProcessor(nx, ny, input[z])); + } + (new ImagePlus(name, istack)).show(); + } + + /** + * Loads the contents of an ImageProcessor into a double[] array. + */ + public static void loadImage(ImageProcessor ip, double[] pixels) { + if (ip == null) { + throw new ArrayStoreException("ImageProcessor == null."); + } + int nk = ip.getWidth() * ip.getHeight(); + if (nk != pixels.length) { + throw new IndexOutOfBoundsException("Array sizes do not match."); + } + if (ip.getPixels() instanceof byte[]) { + byte[] bsrc = (byte[]) ip.getPixels(); + for (int k = 0; k < nk; k++) { + pixels[k] = (double) (bsrc[k] & 0xFF); + } + } + else if (ip.getPixels() instanceof short[]) { + short[] ssrc = (short[]) ip.getPixels(); + for (int k = 0; k < nk; k++) { + pixels[k] = (double) (ssrc[k] & 0xFFFF); + } + } + else if (ip.getPixels() instanceof float[]) { + float[] fsrc = (float[]) ip.getPixels(); + for (int k = 0; k < nk; k++) { + pixels[k] = (double) fsrc[k]; + } + } + else { + throw new ArrayStoreException("Unexpected image type."); + } + } + + /** + * Loads the contents of an ImageProcessor into a float[] array. + */ + public static void loadImage(ImageProcessor ip, float[] pixels) { + if (ip == null) { + throw new ArrayStoreException("ImageProcessor == null."); + } + int nk = ip.getWidth() * ip.getHeight(); + if (nk != pixels.length) { + throw new IndexOutOfBoundsException("Array sizes do not match"); + } + if (ip.getPixels() instanceof byte[]) { + byte[] bsrc = (byte[]) ip.getPixels(); + for (int k = 0; k < nk; k++) { + pixels[k] = (float) (bsrc[k] & 0xFF); + } + } + else if (ip.getPixels() instanceof short[]) { + short[] ssrc = (short[]) ip.getPixels(); + for (int k = 0; k < nk; k++) { + pixels[k] = (float) (ssrc[k] & 0xFFFF); + } + } + else if (ip.getPixels() instanceof float[]) { + float[] fsrc = (float[]) ip.getPixels(); + for (int k = 0; k < nk; k++) { + pixels[k] = fsrc[k]; + } + } + else { + throw new ArrayStoreException("Unexpected image type."); + } + } + + /** + * Loads the contents of an ImageProcessor into a double[][] array. + */ + public static void loadImage(ImageProcessor ip, double[][] pixels) { + if (ip == null) { + throw new ArrayStoreException("ImageProcessor == null."); + } + int nx = ip.getWidth(); + int ny = ip.getHeight(); + if (nx != pixels.length || ny != pixels[0].length) { + throw new IndexOutOfBoundsException("Array sizes do not match."); + } + if (ip.getPixels() instanceof byte[]) { + byte[] bsrc = (byte[]) ip.getPixels(); + for (int y = 0; y < ny; y++) + for (int x = 0; x < nx; x++) + pixels[x][y] = (double) (bsrc[x + y * nx] & 0xFF); + } + else if (ip.getPixels() instanceof short[]) { + short[] ssrc = (short[]) ip.getPixels(); + for (int y = 0; y < ny; y++) + for (int x = 0; x < nx; x++) + pixels[x][y] = (double) (ssrc[x + y * nx] & 0xFFFF); + } + else if (ip.getPixels() instanceof float[]) { + float[] fsrc = (float[]) ip.getPixels(); + for (int y = 0; y < ny; y++) + for (int x = 0; x < nx; x++) + pixels[x][y] = (double) fsrc[x + y * nx]; + } + else { + throw new ArrayStoreException("Unexpected image type."); + } + } + + /** + * Loads the contents of an ImageProcessor into a float[][] array. + */ + public static void loadImage(ImageProcessor ip, float[][] pixels) { + if (ip == null) { + throw new ArrayStoreException("ImageProcessor == null."); + } + int nx = ip.getWidth(); + int ny = ip.getHeight(); + if (nx != pixels.length || ny != pixels[0].length) { + throw new IndexOutOfBoundsException("Array sizes do not match."); + } + if (ip.getPixels() instanceof byte[]) { + byte[] bsrc = (byte[]) ip.getPixels(); + for (int y = 0; y < ny; y++) + for (int x = 0; x < nx; x++) + pixels[x][y] = (float) (bsrc[x + y * nx] & 0xFF); + } + else if (ip.getPixels() instanceof short[]) { + short[] ssrc = (short[]) ip.getPixels(); + for (int y = 0; y < ny; y++) + for (int x = 0; x < nx; x++) + pixels[x][y] = (float) (ssrc[x + y * nx] & 0xFFFF); + } + else if (ip.getPixels() instanceof float[]) { + float[] fsrc = (float[]) ip.getPixels(); + for (int y = 0; y < ny; y++) + for (int x = 0; x < nx; x++) + pixels[x][y] = fsrc[x + y * nx]; + } + else { + throw new ArrayStoreException("Unexpected image type."); + } + } + + /** + * Loads the three RGB channels of an ImageProcessor into individual float[] + * arrays. + */ + public static void loadImageRGB(ImageProcessor ip, float[] rChannel, float[] gChannel, float[] bChannel) { + if (ip == null) { + throw new ArrayStoreException("ImageProcessor == null."); + } + int nk = ip.getWidth() * ip.getHeight(); + if (nk != rChannel.length || nk != gChannel.length || nk != bChannel.length) { + throw new IndexOutOfBoundsException("Array sizes do not match"); + } + if (ip.getPixels() instanceof int[]) { + int[] isrc = (int[]) ip.getPixels(); + for (int k = 0; k < nk; k++) { + rChannel[k] = (float) ((isrc[k] >> 16) & 0x0000FF); + gChannel[k] = (float) ((isrc[k] >> 8) & 0x0000FF); + bChannel[k] = (float) ((isrc[k]) & 0x0000FF); + } + } + else { + throw new ArrayStoreException("Unexpected image type"); + } + } + + /** + * Loads the contents of an ImageStack into a double[] array. + */ + public static void loadStack(ImageStack istack, double[] pixels) { + if (istack == null) { + throw new ArrayStoreException("ImageStack == null."); + } + int nx = istack.getWidth(); + int ny = istack.getHeight(); + int nz = istack.getSize(); + int offz = 0; + int nxy = nx * ny; + if (istack.getPixels(1) instanceof byte[]) { + byte[] bsrc; + for (int z = 0; z < nz; z++) { + bsrc = (byte[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[i + offz] = (double) (bsrc[i] & 0xFF); + } + offz += nxy; + } + } + else if (istack.getPixels(1) instanceof short[]) { + short[] ssrc; + for (int z = 0; z < nz; z++) { + ssrc = (short[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[i + offz] = (double) (ssrc[i] & 0xFFFF); + } + offz += nxy; + } + } + else if (istack.getPixels(1) instanceof float[]) { + float[] fsrc; + for (int z = 0; z < nz; z++) { + fsrc = (float[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[i + offz] = (double) fsrc[i]; + } + offz += nxy; + } + } + else { + throw new ArrayStoreException("Unexpected image type."); + } + } + + /** + * Loads the contents of an ImageStack into a float[] array. + */ + public static void loadStack(ImageStack istack, float[] pixels) { + if (istack == null) { + throw new ArrayStoreException("ImageStack == null."); + } + int nx = istack.getWidth(); + int ny = istack.getHeight(); + int nz = istack.getSize(); + int offz = 0; + int nxy = nx * ny; + if (istack.getPixels(1) instanceof byte[]) { + byte[] bsrc; + for (int z = 0; z < nz; z++) { + bsrc = (byte[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[i + offz] = (float) (bsrc[i] & 0xFF); + } + offz += nxy; + } + } + else if (istack.getPixels(1) instanceof short[]) { + short[] ssrc; + for (int z = 0; z < nz; z++) { + ssrc = (short[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[i + offz] = (float) (ssrc[i] & 0xFFFF); + } + offz += nxy; + } + } + else if (istack.getPixels(1) instanceof float[]) { + float[] fsrc; + for (int z = 0; z < nz; z++) { + fsrc = (float[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[i + offz] = fsrc[i]; + } + offz += nxy; + } + } + else { + throw new ArrayStoreException("Unexpected image type."); + } + } + + /** + * Loads the contents of an ImageStack into a double[][] array. Input + * structure is [z][xy]. + */ + public static void loadStack(ImageStack istack, double[][] pixels) { + if (istack == null) + throw new ArrayStoreException("ImageStack == null."); + + int nz = pixels.length; + int nxy = pixels[0].length; + + if (istack.getPixels(1) instanceof float[]) { + float[] fsrc; + for (int z = 0; z < nz; z++) { + fsrc = (float[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[z][i] = (double) fsrc[i]; + } + } + } + else if (istack.getPixels(1) instanceof short[]) { + short[] ssrc; + for (int z = 0; z < nz; z++) { + ssrc = (short[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[z][i] = (double) (ssrc[i] & 0xFFFF); + } + } + } + else if (istack.getPixels(1) instanceof byte[]) { + byte[] bsrc; + for (int z = 0; z < nz; z++) { + bsrc = (byte[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[z][i] = (double) (bsrc[i] & 0xFF); + } + } + } + else { + throw new ArrayStoreException("Unexpected image type."); + } + } + + /** + * Loads the contents of an ImageStack into a float[][] array. Input + * structure is [z][xy]. + */ + public static void loadStack(ImageStack istack, float[][] pixels) { + if (istack == null) + throw new ArrayStoreException("ImageStack == null."); + + int nz = pixels.length; + int nxy = pixels[0].length; + + if (istack.getPixels(1) instanceof float[]) { + float[] fsrc; + for (int z = 0; z < nz; z++) { + fsrc = (float[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[z][i] = fsrc[i]; + } + } + } + else if (istack.getPixels(1) instanceof short[]) { + short[] ssrc; + for (int z = 0; z < nz; z++) { + ssrc = (short[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[z][i] = (float) (ssrc[i] & 0xFFFF); + } + } + } + else if (istack.getPixels(1) instanceof byte[]) { + byte[] bsrc; + for (int z = 0; z < nz; z++) { + bsrc = (byte[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[z][i] = (float) (bsrc[i] & 0xFF); + } + } + } + else { + throw new ArrayStoreException("Unexpected image type."); + } + } + + /** + * Loads the contents of a RGB ImageStack into a int[][] array. Input + * structure is [z][xy]. + */ + public static void loadStackRGB(ImageStack istack, int[][] pixels) { + if (istack == null) { + throw new ArrayStoreException("ImageStack == null."); + } + int nz = pixels.length; + int nxy = pixels[0].length; + + if (istack.getPixels(1) instanceof int[]) { + int[] isrc; + for (int z = 0; z < nz; z++) { + isrc = (int[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[z][i] = isrc[i]; + } + } + } + else { + throw new ArrayStoreException("Input stack must be RGB."); + } + } + + /** + * Loads the contents of a RGB ImageStack into a int[] array. + */ + public static void loadStackRGB(ImageStack istack, int[] pixels) { + if (istack == null) { + throw new ArrayStoreException("ImageStack == null."); + } + int nx = istack.getWidth(); + int ny = istack.getHeight(); + int nz = istack.getSize(); + int offz = 0; + int nxy = nx * ny; + if (istack.getPixels(1) instanceof int[]) { + int[] isrc; + for (int z = 0; z < nz; z++) { + isrc = (int[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + pixels[i + offz] = isrc[i]; + } + offz += nxy; + } + } + else { + throw new ArrayStoreException("Input stack must be RGB."); + } + } + + /** + * Converts and RGB ImageProcessor to grayscale and loads the result into a + * float[][] array. Conversion: (R*R + G*G + B*B)/(R+G+B). + */ + public static void loadAndConvertRGBStack(ImageStack istack, float[][] pixels) { + if (istack == null) { + throw new ArrayStoreException("ImageStack == null."); + } + int nz = pixels.length; + int nxy = pixels[0].length; + float R, G, B; + if (istack.getPixels(1) instanceof int[]) { + int[] isrc; + for (int z = 0; z < nz; z++) { + isrc = (int[]) istack.getPixels(z + 1); + for (int i = 0; i < nxy; i++) { + R = (float) (isrc[i] >> 16 & 0x000000FF); + G = (float) (isrc[i] >> 8 & 0x000000FF); + B = (float) (isrc[i] & 0x000000FF); + pixels[z][i] = (R * R + G * G + B * B) / (R + G + B); + } + } + } + else { + throw new ArrayStoreException("Unexpected image type."); + } + } + + /* + * public static double[][] kMeans(double[][] input, int K) { + * + * int nx = input.length; int ny = input[0].length; int N = nx*ny; double[] + * r = new double[K]; double[] t = new double[K+1]; t[0] = + * -Double.MAX_VALUE; t[K] = Double.MAX_VALUE; int x, y, k; int[] count = + * new int[K]; for (int i=0;i<10;i++) { // change + * + * for (k=0;k<K;k++) { count[k] = 0; } + * + * for (k=0;k<K;k++) { for (x=0;x<nx;x++) { for (y=0;y<ny;y++) { if + * (input[x][y] < t[k+1]) { count[k]++; } } } } + * + * for (k=1;k<K;k++) { t[k] = (r[k]+r[k-1])/2.0; } + * + * } + * + * return input; } + */ + + /* + * public static double[] histogram(double[][] input, int nBins) { + * + * int nx = input.length; int ny = input[0].length; double[] hist = new + * double[nBins]; for (int i=0;i<nBins;i++) { hist[i] = 0; } int index; + * double min = Double.MAX_VALUE; double max = -Double.MAX_VALUE; double p; + * + * for (int x=0;x<nx;x++) { for (int y=0;y<ny;y++) { p = input[x][y]; if (p + * < min) { min = p; } if (p > max) { max = p; } } } for (int x=0;x<nx;x++) + * { for (int y=0;y<ny;y++) { p = input[x][y] - min; index = + * (int)Math.round((p-p%nBins)/nBins); hist[index]++; } } return hist; } + */ + + // public static double region + +} diff --git a/src/bilib/src/ijtools/Interpolator.java b/src/bilib/src/ijtools/Interpolator.java new file mode 100644 index 0000000..284786e --- /dev/null +++ b/src/bilib/src/ijtools/Interpolator.java @@ -0,0 +1,324 @@ +package ijtools; + +import java.lang.*; +import ij.*; + +/** + * Class for image interpolation with B-splines. Currently supported methods: + * linear, quadratic, cubic. Where applicable, mirror boundary conditions are + * used. + * + * @author Francois Aguet + * @version 1.0 + */ +public class Interpolator { + + private double[] input; + private double[] c; + private int nx, ny; + private String mode; + private double a, c0; + + /** + * @param input + * 1-D array storing the image. + * @param nx + * Width of the image. + * @param ny + * Height of the image. + * @param mode + * Interpolation mode. Options: "linear", "quadratic", "cubic". + */ + public Interpolator(double[] input, int nx, int ny, String mode) { + this.input = input; + this.nx = nx; + this.ny = ny; + this.mode = mode; + if (mode.equals("linear")) { + c = input; + } + else if (mode.equals("quadratic")) { + c0 = 8.0; + a = -3.0 + 2.0 * Math.sqrt(2.0); + computeCoefficients(); + } + else if (mode.equals("cubic")) { + c0 = 6.0; + a = -2.0 + Math.sqrt(3.0); + computeCoefficients(); + } + + // test reconstruction + /* + * double[][] s2 = new double[ntheta][nr]; for (int y=0;y<nr;y++) { for + * (int x=0;x<ntheta;x++) { s2[x][y] = interpolatedValue(x+1, y+1); } } + * IJtools.show(s2, "reconstruction"); + */ + } + + /** + * Return the value interpolated at (x,y). + */ + public double getValue(double x, double y) { + int xi = (int) x; + int yi = (int) y; + int x0, x1, y0, y1; + + if (mode.equals("linear")) { + double dx = x - xi; + double dy = y - yi; + if (x < 0) { + dx = -dx; + x1 = mirror(xi - 1, nx); + } + else { + x1 = mirror(xi + 1, nx); + } + if (y < 0) { + dy = -dy; + y1 = mirror(yi - 1, ny); + } + else { + y1 = mirror(yi + 1, ny); + } + x0 = mirror(xi, nx); + y0 = mirror(yi, ny); + return (1.0 - dy) * (dx * input[x1 + y0 * nx] + (1.0 - dx) * input[x0 + y0 * nx]) + dy * (dx * input[x1 + y1 * nx] + (1.0 - dx) * input[x0 + y1 * nx]); + } + else { + double dx = x - xi; + double dy = y - yi; + double[] wx, wy; + int x2, x3, y2, y3; + + if (x < 0) { + xi = xi - 1; + dx = 1.0 + dx; + } + if (y < 0) { + yi = yi - 1; + dy = 1.0 + dy; + } + + if (mode.equals("quadratic")) { + wx = getQuadraticSpline(dx); + wy = getQuadraticSpline(dy); + } + else { // if (mode.equals("cubic")) { + wx = getCubicSpline(dx); + wy = getCubicSpline(dy); + } + + x0 = xi - 1; + x1 = xi; + x2 = xi + 1; + x3 = xi + 2; + x0 = mirror(x0, nx); + x1 = mirror(x1, nx); + x2 = mirror(x2, nx); + x3 = mirror(x3, nx); + + y0 = yi - 1; + y1 = yi; + y2 = yi + 1; + y3 = yi + 2; + y0 = mirror(y0, ny); + y1 = mirror(y1, ny); + y2 = mirror(y2, ny); + y3 = mirror(y3, ny); + y0 *= nx; + y1 *= nx; + y2 *= nx; + y3 *= nx; + + double v = wx[0] * (wy[0] * c[x0 + y0] + wy[1] * c[x0 + y1] + wy[2] * c[x0 + y2] + wy[3] * c[x0 + y3]) + wx[1] + * (wy[0] * c[x1 + y0] + wy[1] * c[x1 + y1] + wy[2] * c[x1 + y2] + wy[3] * c[x1 + y3]) + wx[2] + * (wy[0] * c[x2 + y0] + wy[1] * c[x2 + y1] + wy[2] * c[x2 + y2] + wy[3] * c[x2 + y3]) + wx[3] + * (wy[0] * c[x3 + y0] + wy[1] * c[x3 + y1] + wy[2] * c[x3 + y2] + wy[3] * c[x3 + y3]); + return v; + } + + } + + private int mirror(int x, int nx) { + if (x >= 0 && x < nx) { + return x; + } + else if (x < 0) { + return -x; + } + else { + return 2 * nx - 2 - x; + } + } + + // public double[] interpolate (double[][] coordinates) {}; + + /* + * public static double interpolateLinear(double[] image, int[] dims, double + * x, double y, int z) { + * + * int nx = dims[0]; int ny = dims[1]; int i = (int)x; int j = (int)y; + * double dx = x - (double)(i); double dy = y - (double)(j); + * + * if (x < 0) { dx = -dx; i = 0; } if (i >= nx-1) { dx = 1-dx; i--; i = + * nx-2; } if (y < 0) { dy = -dy; j = 0; } if (j >= ny-1) { dy = 1-dy; j--; + * j = ny-2;} + * + * int idx1 = i+nx*(j+z*ny); int idx2 = idx1+nx; double v00 = image[idx1]; + * double v10 = image[idx1+1]; double v01 = image[idx2]; double v11 = + * image[idx2+1]; + * + * return dx*((v11-v10)*dy + v10) - (dx-1.0)*((v01-v00)*dy + v00); } + */ + + private void computeCoefficients() { + // Horizontal + double[] cp = getCausalInitHorizontal(input, a); + int i; + for (int y = 0; y < ny; y++) { + for (int x = 1; x < nx; x++) { // causal + i = x + y * nx; + cp[i] = input[i] + a * cp[i - 1]; + } + } + c = getAntiCausalInitHorizontal(cp, a); // cn + for (int y = 0; y < ny; y++) { + for (int x = nx - 2; x >= 0; x--) { // anticausal + i = x + y * nx; + c[i] = a * (c[i + 1] - cp[i]); + } + } + // Vertical + cp = getCausalInitVertical(c, a); + for (int x = 0; x < nx; x++) { + for (int y = 1; y < ny; y++) { + i = x + y * nx; + cp[i] = c[i] + a * cp[i - nx]; + } + } + c = getAntiCausalInitVertical(cp, a); + for (int x = 0; x < nx; x++) { + for (int y = ny - 2; y >= 0; y--) { + i = x + y * nx; + c[i] = a * (c[i + nx] - cp[i]); + } + } + // constant + double c02 = c0 * c0; + for (i = 0; i < ny * ny; i++) { + c[i] = c02 * c[i]; + } + } + + /** + * Computes the quadratic spline basis function at a position t. + * + * @param t + * argument between 0 and 1. + * @return 4 sampled values of the cubic B-spline (B3[t+1], B3[t], B3[t-1], + * B3[t-2]). + */ + private static double[] getQuadraticSpline(double t) { + if (t < 0.0 || t > 1.0) { + throw new ArrayStoreException("Argument t for quadratic B-spline outside of expected range [0, 1]: " + t); + } + double v[] = new double[4]; + if (t <= 0.5) { + v[0] = (t - 0.5) * (t - 0.5) / 2.0; + v[1] = 0.75 - t * t; + v[2] = 1.0 - v[1] - v[0]; // (t+0.5)*(t+0.5)/2.0; + v[3] = 0.0; + } + else { + v[0] = 0.0; + v[1] = (t - 1.5) * (t - 1.5) / 2.0; + v[3] = (t - 0.5) * (t - 0.5) / 2.0; + v[2] = 1.0 - v[3] - v[1]; + } + return v; + } + + /** + * Computes the cubic spline basis function at a position t. + * + * @param t + * argument between 0 and 1. + * @return 4 sampled values of the cubic B-spline (B3[t+1], B3[t], B3[t-1], + * B3[t-2]). + */ + private static double[] getCubicSpline(double t) { + if (t < 0.0 || t > 1.0) { + throw new ArrayStoreException("Argument t for cubic B-spline outside of expected range [0, 1]: " + t); + } + double v[] = new double[4]; + double t1 = 1.0 - t; + double t2 = t * t; + v[0] = (t1 * t1 * t1) / 6.0; + v[1] = (2.0 / 3.0) + 0.5 * t2 * (t - 2); + v[3] = (t2 * t) / 6.0; + v[2] = 1.0 - v[3] - v[1] - v[0]; + return v; + } + + private double[] getAntiCausalInitVertical(double[] s, double a) { + double[] cn = new double[nx * ny]; + int idx = (ny - 1) * nx; + double d = a * a - 1.0; + for (int x = 0; x < nx; x++) { + cn[x + idx] = a * (s[x + idx] + a * s[x + idx - nx]) / d; + } + return cn; + } + + private double[] getAntiCausalInitHorizontal(double[] s, double a) { + double[] cn = new double[nx * ny]; + double d = a * a - 1.0; + for (int y = 0; y < ny; y++) { + cn[nx - 1 + y * nx] = a * (s[nx - 1 + y * nx] + a * s[nx - 2 + y * nx]) / d; + } + return cn; + } + + // Exact method + private double[] getCausalInitVertical(double[] s, double z) { + double[] cp = new double[nx * ny]; + double zd, sum, den, za; + for (int x = 0; x < nx; x++) { + zd = Math.pow(z, ny - 1); + sum = s[x] + s[x + (ny - 1) * nx] * zd; + den = zd * zd; + zd = den / z; + za = z; + for (int y = 1; y < ny - 1; y++) { + sum += s[x + y * nx] * (za + zd); + za *= z; + zd /= z; + } + sum /= (1 - den); + cp[x] = sum; + } + return cp; + } + + private double[] getCausalInitHorizontal(double[] s, double z) { + double[] cp = new double[nx * ny]; + double zd, sum, den, za; + for (int y = 0; y < ny; y++) { + zd = Math.pow(z, nx - 1); + sum = s[y * nx] + s[nx - 1 + y * nx] * zd; + den = zd * zd; + zd = den / z; + za = z; + for (int x = 1; x < nx - 1; x++) { + sum += s[x + y * nx] * (za + zd); + za *= z; + zd /= z; + } + sum /= (1 - den); + cp[y * nx] = sum; + } + return cp; + } + +} diff --git a/src/bilib/src/ijtools/SplineConvolver.java b/src/bilib/src/ijtools/SplineConvolver.java new file mode 100644 index 0000000..d0ed394 --- /dev/null +++ b/src/bilib/src/ijtools/SplineConvolver.java @@ -0,0 +1,437 @@ +package ijtools; + +//package ijtools; +import ij.IJ; + +/** + * Convolution routines for spline kernels of arbitrary integer orders and + * scales. + * + * @author Francois Aguet + * @version 1.0 + */ +public class SplineConvolver { + + public static double[][] convolve(double[][] input, int n, int scale) { + + int nx = input.length; + int ny = input[0].length; + + double[][] output;// = new double[nx][ny]; + double[][] reference;// = new double[nx][ny]; + + double[] splineKernelFine = splineSamples(n, scale); + double[] splineKernelCoarse = splineSamples(n, 1); + // IJtools.show("spline", splineKernelFine, splineKernelFine.length, 1); + + long start1 = System.currentTimeMillis(); + reference = Convolver2D.convolveEvenX(input, splineKernelFine); + reference = Convolver2D.convolveEvenY(reference, splineKernelFine); + long end1 = System.currentTimeMillis(); + IJtools.show("Reference", reference); + + long start2 = System.currentTimeMillis(); + if (n % 2 == 1) { // odd n + output = Convolver2D.convolveEvenX(input, splineKernelCoarse); + output = Convolver2D.convolveEvenY(output, splineKernelCoarse); + for (int k = 1; k <= (n + 1) / 2; k++) { + output = movingSum(output, scale); + output = movingSumSansFrontieres(output, scale); + } + } + else { // even n + if (scale % 2 == 1) { // odd scale + output = Convolver2D.convolveEvenX(input, splineKernelCoarse); + output = Convolver2D.convolveEvenY(output, splineKernelCoarse); + for (int k = 0; k <= n; k++) { + output = movingSumOdd(output, scale); + } + } + else { // even scale + output = movingSum(input, scale); + output = movingSumSansFrontieres(output, scale); + for (int k = 2; k <= n / 2; k++) { + output = movingSum(output, scale); + output = movingSumSansFrontieres(output, scale); + } + output = movingSum(output, scale); + + splineKernelCoarse = splineSamplesHalfStep(n); + for (int k = 0; k < splineKernelCoarse.length; k++) { + IJ.write("" + splineKernelCoarse[k]); + } + output = convEvenKernelX(output, splineKernelCoarse); + output = convEvenKernelY(output, splineKernelCoarse); + output = crop(output, (scale + splineKernelCoarse.length) / 2 - 1); + } + } + double norm = Math.pow(scale, n + 1); + for (int x = 0; x < nx; x++) { + for (int y = 0; y < ny; y++) { + output[x][y] /= (norm * norm); + } + } + long end2 = System.currentTimeMillis(); + IJ.write("ref time: " + (end1 - start1)); + IJ.write("sum time: " + (end2 - start2)); + + IJtools.show("Moving sums", output); + + return output; + + } + + private static double[][] movingSum(double[][] input, int length) { + int nx = input.length; + int ny = input[0].length; + double[][] temp = new double[nx + length - 1][ny]; + double[][] output = new double[nx + length - 1][ny + length - 1]; + int x, y; + + for (y = 0; y < ny; y++) { + temp[0][y] = input[0][y]; + for (x = 1; x < length; x++) { + temp[0][y] += input[x][y]; + } + for (x = 1; x < length; x++) { // left border: mirror + temp[x][y] = temp[x - 1][y] - input[length - x][y] + input[x][y]; // remove + // 0-(x-length) + } + for (x = length; x < nx; x++) { + temp[x][y] = temp[x - 1][y] - input[x - length][y] + input[x][y]; + } + for (x = nx; x < nx + length - 1; x++) { // right border: mirror + temp[x][y] = temp[x - 1][y] - input[x - length][y] + input[2 * nx - x - 2][y]; // (ns-1) + // - + // (x + // - + // (ns-1)) + } + } + for (x = 0; x < nx + length - 1; x++) { + output[x][0] = temp[x][0]; + for (y = 1; y < length; y++) { + output[x][0] += temp[x][y]; + } + for (y = 1; y < length; y++) { // left border: mirror + output[x][y] = output[x][y - 1] - temp[x][length - y] + temp[x][y]; + } + for (y = length; y < ny; y++) { + output[x][y] = output[x][y - 1] - temp[x][y - length] + temp[x][y]; + } + for (y = ny; y < ny + length - 1; y++) { // right border: mirror + output[x][y] = output[x][y - 1] - temp[x][y - length] + temp[x][2 * ny - y - 2]; + } + } + return output; + } + + private static double[][] movingSumSansFrontieres(double[][] input, int length) { + int nx = input.length; + int ny = input[0].length; + double[][] temp = new double[nx - length + 1][ny]; + double[][] output = new double[nx - length + 1][ny - length + 1]; + int x, y; + + for (y = 0; y < ny; y++) { + temp[0][y] = input[0][y]; + for (x = 1; x < length; x++) { + temp[0][y] += input[x][y]; + } + for (x = 1; x < nx - length + 1; x++) { + temp[x][y] = temp[x - 1][y] - input[x - 1][y] + input[x + length - 1][y]; + } + } + for (x = 0; x < nx - length + 1; x++) { // ///// + 1 ?? + output[x][0] = temp[x][0]; + for (y = 1; y < length; y++) { + output[x][0] += temp[x][y]; + } + for (y = 1; y < ny - length + 1; y++) { + output[x][y] = output[x][y - 1] - temp[x][y - 1] + temp[x][y + length - 1]; + } + } + return output; + } + + private static double[][] movingSumOdd(double[][] input, int length) { + int nx = input.length; + int ny = input[0].length; + double[][] temp = new double[nx][ny]; + double[][] output = new double[nx][ny]; + int c = (length - 1) / 2; + int x, y; + for (y = 0; y < ny; y++) { + temp[0][y] = input[0][y]; + for (x = 1; x <= c; x++) { + temp[0][y] += 2.0 * input[x][y]; + } + for (x = 1; x <= c; x++) { // left border + temp[x][y] = temp[x - 1][y] - input[1 + c - x][y] + input[x + c][y]; + } + for (x = c + 1; x < nx - c; x++) { + temp[x][y] = temp[x - 1][y] - input[x - c - 1][y] + input[x + c][y]; + } + for (x = nx - c; x < nx; x++) { // right border + temp[x][y] = temp[x - 1][y] - input[x - c - 1][y] + input[2 * nx - 2 - x - c][y]; + } + } + for (x = 0; x < nx; x++) { + output[x][0] = temp[x][0]; + for (y = 1; y <= c; y++) { + output[x][0] += 2.0 * temp[x][y]; + } + for (y = 1; y <= c; y++) { // top border + output[x][y] = output[x][y - 1] - temp[x][1 + c - y] + temp[x][y + c]; + } + for (y = c + 1; y < ny - c; y++) { + output[x][y] = output[x][y - 1] - temp[x][y - c - 1] + temp[x][y + c]; + } + for (y = ny - c; y < ny; y++) { // bottom border + output[x][y] = output[x][y - 1] - temp[x][y - c - 1] + temp[x][2 * ny - 2 - y - c]; + } + } + return output; + } + + private static double[][] convEvenKernelX(double[][] input, double[] kernel) { + int nx = input.length; + int ny = input[0].length; + int w = kernel.length; + double[][] output = new double[nx + w - 1][ny]; + int x, y, k; + + for (y = 0; y < ny; y++) { + for (x = 0; x <= w - 2; x++) { // left border + output[x][y] = 0.0; + for (k = 0; k <= w - x - 2; k++) { // (w-1) - (x+1) + output[x][y] += kernel[k] * input[w - x - 1 - k][y]; // 0 - + // (0 + // - + // (x-w+1+k)) + } + for (k = w - x - 1; k < w; k++) { + output[x][y] += kernel[k] * input[x - w + 1 + k][y]; + } + } + for (x = w - 1; x < nx; x++) { + output[x][y] = 0.0; + for (k = 0; k < w; k++) { + output[x][y] += kernel[k] * input[x - w + 1 + k][y]; + } + } + for (x = nx; x <= nx + w - 2; x++) { // right border + output[x][y] = 0.0; + for (k = 0; k <= nx - 2 - x + w; k++) { + output[x][y] += kernel[k] * input[x - w + 1 + k][y]; + } + for (k = nx - x - 1 + w; k < w; k++) { + output[x][y] += kernel[k] * input[2 * nx - 3 - x + w - k][y]; + } + } + } + return output; + } + + private static double[][] convEvenKernelY(double[][] input, double[] kernel) { + int nx = input.length; + int ny = input[0].length; + int w = kernel.length; + double[][] output = new double[nx][ny + w - 1]; + int x, y, k; + + for (x = 0; x < nx; x++) { + for (y = 0; y <= w - 2; y++) { // top border + output[x][y] = 0.0; + for (k = 0; k <= w - y - 2; k++) { // outside of signal -> + // mirror: + output[x][y] += kernel[k] * input[x][w - y - 1 - k]; + } + for (k = w - y - 1; k < w; k++) { + output[x][y] += kernel[k] * input[x][y - w + 1 + k]; + } + } + for (y = w - 1; y < ny; y++) { + output[x][y] = 0.0; + for (k = 0; k < w; k++) { + output[x][y] += kernel[k] * input[x][y - w + 1 + k]; + } + } + for (y = ny; y <= ny + w - 2; y++) { // bottom border + output[x][y] = 0.0; + for (k = 0; k <= ny - 2 - y + w; k++) { + output[x][y] += kernel[k] * input[x][y - w + 1 + k]; + } + for (k = ny - 1 - y + w; k < w; k++) { + output[x][y] += kernel[k] * input[x][2 * ny - 3 - y + w - k]; + } + } + } + return output; + } + + private static double[][] crop(double[][] input, int t) { + int nx = input.length - 2 * t; + int ny = input[0].length - 2 * t; + double[][] output = new double[nx][ny]; + for (int y = 0; y < ny; y++) { + for (int x = 0; x < nx; x++) { + output[x][y] = input[x + t][y + t]; + } + } + return output; + } + + /* + * private static double[] mavg(double[] signal, int scale) { int ns = + * signal.length; double[] out = new double[ns+scale-1]; out[0] = + * sum(signal, 0, scale-1); for (int i=1;i<scale;i++) { out[i] = out[i-1] - + * signal[scale-i+1] + signal[i]; } for (int i=scale;i<ns;i++) { out[i] = + * out[i-1] - signal[i-scale] + signal[i]; } for (int + * i=ns;i<=ns+scale-2;i++) { out[i] = out[i-1] - signal[i-scale] + + * signal[2*ns-i]; } return out; } + */ + + private static double[] mavg_odd(double[] signal, int scale) { + int ns = signal.length; + int c = (scale - 1) / 2; + double[] out = new double[ns]; + out[0] = signal[0] + 2.0 * sum(signal, 1, c); + for (int i = 1; i <= c; i++) { + out[i] = out[i - 1] - signal[2 - i + c] + signal[i + c]; + } + for (int i = c + 1; i < ns - c; i++) { + out[i] = out[i - 1] - signal[i - c - 1] + signal[i + c]; + } + for (int i = ns - c; i < ns; i++) { + out[i] = out[i - 1] - signal[i - c - 1] + signal[2 * ns - c - i]; + } + return out; + } + + private static double[] convEvenKernel(double[] signal, double[] kernel) { + int ns = signal.length; + int w = kernel.length; + double[] out = new double[ns + w - 1]; + for (int i = 0; i <= w - 2; i++) { + out[i] = 0.0; + for (int k = 0; k <= w - i - 1; k++) { + out[i] += kernel[k] * signal[2 - i + w - k]; + } + for (int k = w - i; k < w; k++) { + out[i] += kernel[k] * signal[i - w + k]; + } + } + for (int i = w - 1; i < ns; i++) { + out[i] = 0.0; + for (int k = 0; k < w; k++) { + out[i] += kernel[k] * signal[i - w + k]; + } + } + for (int i = ns; i <= ns + w - 2; i++) { + for (int k = 0; k < ns - i + w; k++) { + out[i] += kernel[k] * signal[i - w + k]; + } + for (int k = ns - i + w; k < w; k++) { + out[i] += kernel[k] * signal[ns - 1 - w + k]; + } + } + return out; + } + + private static double[] mavg_nob(double[] signal, int scale) { + int ns = signal.length; + double[] out = new double[ns - scale + 1]; + out[0] = sum(signal, 0, scale - 1); + for (int i = 1; i <= ns - scale; i++) { + out[i] = out[i - 1] + signal[i + scale - 1] - signal[i - 1]; + } + return out; + } + + private static double sum(double[] s, int a, int b) { + double t = 0; + for (int i = a; i <= b; i++) { + t += s[i]; + } + return t; + } + + /** + * returns the samples of a degree n, scale s B-spline samples returned for + * [0 b+1/2] + */ + private static double[] splineSamples(int n, int s) { + + double b = ((double) n + 1.0) / 2.0; + double normalization = (double) (s * fact(n)); + int nx = (int) (b * s); // +1 unnecessary, =0 + if (nx != b * s) { + nx++; + } + double[] samples = new double[nx]; + double ksign; + double x, klimit; + int[] coeffs = binomialCoefficients(n + 1); + for (int i = 0; i < nx; i++) { + x = (double) i / (double) s; + klimit = x + b; + samples[i] = Math.pow(x + b, n); + ksign = 1.0; + for (int k = 1; k <= klimit; k++) { + ksign *= -1.0; + samples[i] += (double) coeffs[k] * ksign * Math.pow(x - k + b, n); + } + samples[i] /= normalization; + } + return samples; + } + + /** + * returns the samples of a degree n at half-integer positions samples + * returned for [-(b+1)/2 (b+1)/2] + */ + private static double[] splineSamplesHalfStep(int n) { + double b = ((double) n + 1.0) / 2.0; + double normalization = (double) fact(n); + double[] samples = new double[n]; + double ksign; + double x, klimit; + int[] coeffs = binomialCoefficients(n + 1); + for (int i = 0; i < n; i++) { + x = (double) i - ((double) n - 1.0) / 2.0; + klimit = x + b; + ksign = 1.0; + samples[i] = Math.pow(x + b, n); + for (int k = 1; k <= klimit; k++) { + ksign *= -1.0; + samples[i] += (double) coeffs[k] * ksign * Math.pow(x - k + b, n); + } + samples[i] /= normalization; + } + return samples; + } + + private static int[] binomialCoefficients(int n) { + int[] coeffs = new int[n + 1]; + for (int k = 0; k <= n; k++) { + coeffs[k] = (int) (fact(n) / (fact(k) * fact(n - k))); + } + return coeffs; + } + + public static long fact(int n) { + + if (n <= 1) { + return 1; + } + else { + long k = n; + for (int i = n - 1; i > 1; i--) { + k *= i; + } + return k; + } + } + +} diff --git a/src/bilib/src/imageware.zip b/src/bilib/src/imageware.zip new file mode 100644 index 0000000000000000000000000000000000000000..4685044fa0b9ca7d6a7683c0a320929b0051f837 GIT binary patch literal 88737 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	++RvDkq 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 literal 0 HcmV?d00001 diff --git a/src/bilib/src/imageware/Access.java b/src/bilib/src/imageware/Access.java new file mode 100644 index 0000000..d345d9b --- /dev/null +++ b/src/bilib/src/imageware/Access.java @@ -0,0 +1,256 @@ +package imageware; + +/** + * Class Access. + * + * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de + * Lausanne, Lausanne, Switzerland + */ + +public interface Access extends Buffer { + + // getPixel section + public double getPixel(int x, int y, int z); + + public double getPixel(int x, int y, int z, byte boundaryConditions); + + public double getInterpolatedPixel(double x, double y, double z); + + public double getInterpolatedPixel(double x, double y, double z, byte boundaryConditions); + + // putPixel section + public void putPixel(int x, int y, int z, double value); + + // getBounded section + + public void getBoundedX(int x, int y, int z, byte[] buffer); + + public void getBoundedY(int x, int y, int z, byte[] buffer); + + public void getBoundedZ(int x, int y, int z, byte[] buffer); + + public void getBoundedXY(int x, int y, int z, byte[][] buffer); + + public void getBoundedXZ(int x, int y, int z, byte[][] buffer); + + public void getBoundedYZ(int x, int y, int z, byte[][] buffer); + + public void getBoundedXYZ(int x, int y, int z, byte[][][] buffer); + + public void getBoundedX(int x, int y, int z, short[] buffer); + + public void getBoundedY(int x, int y, int z, short[] buffer); + + public void getBoundedZ(int x, int y, int z, short[] buffer); + + public void getBoundedXY(int x, int y, int z, short[][] buffer); + + public void getBoundedXZ(int x, int y, int z, short[][] buffer); + + public void getBoundedYZ(int x, int y, int z, short[][] buffer); + + public void getBoundedXYZ(int x, int y, int z, short[][][] buffer); + + public void getBoundedX(int x, int y, int z, float[] buffer); + + public void getBoundedY(int x, int y, int z, float[] buffer); + + public void getBoundedZ(int x, int y, int z, float[] buffer); + + public void getBoundedXY(int x, int y, int z, float[][] buffer); + + public void getBoundedXZ(int x, int y, int z, float[][] buffer); + + public void getBoundedYZ(int x, int y, int z, float[][] buffer); + + public void getBoundedXYZ(int x, int y, int z, float[][][] buffer); + + public void getBoundedX(int x, int y, int z, double[] buffer); + + public void getBoundedY(int x, int y, int z, double[] buffer); + + public void getBoundedZ(int x, int y, int z, double[] buffer); + + public void getBoundedXY(int x, int y, int z, double[][] buffer); + + public void getBoundedXZ(int x, int y, int z, double[][] buffer); + + public void getBoundedYZ(int x, int y, int z, double[][] buffer); + + public void getBoundedXYZ(int x, int y, int z, double[][][] buffer); + + // getBlock with boundary conditions section + + public void getBlockX(int x, int y, int z, byte[] buffer, byte boundaryConditions); + + public void getBlockY(int x, int y, int z, byte[] buffer, byte boundaryConditions); + + public void getBlockZ(int x, int y, int z, byte[] buffer, byte boundaryConditions); + + public void getBlockXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions); + + public void getBlockXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions); + + public void getBlockYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions); + + public void getBlockXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions); + + public void getBlockX(int x, int y, int z, short[] buffer, byte boundaryConditions); + + public void getBlockY(int x, int y, int z, short[] buffer, byte boundaryConditions); + + public void getBlockZ(int x, int y, int z, short[] buffer, byte boundaryConditions); + + public void getBlockXY(int x, int y, int z, short[][] buffer, byte boundaryConditions); + + public void getBlockXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions); + + public void getBlockYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions); + + public void getBlockXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions); + + public void getBlockX(int x, int y, int z, float[] buffer, byte boundaryConditions); + + public void getBlockY(int x, int y, int z, float[] buffer, byte boundaryConditions); + + public void getBlockZ(int x, int y, int z, float[] buffer, byte boundaryConditions); + + public void getBlockXY(int x, int y, int z, float[][] buffer, byte boundaryConditions); + + public void getBlockXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions); + + public void getBlockYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions); + + public void getBlockXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions); + + public void getBlockX(int x, int y, int z, double[] buffer, byte boundaryConditions); + + public void getBlockY(int x, int y, int z, double[] buffer, byte boundaryConditions); + + public void getBlockZ(int x, int y, int z, double[] buffer, byte boundaryConditions); + + public void getBlockXY(int x, int y, int z, double[][] buffer, byte boundaryConditions); + + public void getBlockXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions); + + public void getBlockYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions); + + public void getBlockXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions); + + // getNeighborhood with boundary conditions section + + public void getNeighborhoodX(int x, int y, int z, byte[] buffer, byte boundaryConditions); + + public void getNeighborhoodY(int x, int y, int z, byte[] buffer, byte boundaryConditions); + + public void getNeighborhoodZ(int x, int y, int z, byte[] buffer, byte boundaryConditions); + + public void getNeighborhoodXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions); + + public void getNeighborhoodXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions); + + public void getNeighborhoodYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions); + + public void getNeighborhoodXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions); + + public void getNeighborhoodX(int x, int y, int z, short[] buffer, byte boundaryConditions); + + public void getNeighborhoodY(int x, int y, int z, short[] buffer, byte boundaryConditions); + + public void getNeighborhoodZ(int x, int y, int z, short[] buffer, byte boundaryConditions); + + public void getNeighborhoodXY(int x, int y, int z, short[][] buffer, byte boundaryConditions); + + public void getNeighborhoodXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions); + + public void getNeighborhoodYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions); + + public void getNeighborhoodXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions); + + public void getNeighborhoodX(int x, int y, int z, float[] buffer, byte boundaryConditions); + + public void getNeighborhoodY(int x, int y, int z, float[] buffer, byte boundaryConditions); + + public void getNeighborhoodZ(int x, int y, int z, float[] buffer, byte boundaryConditions); + + public void getNeighborhoodXY(int x, int y, int z, float[][] buffer, byte boundaryConditions); + + public void getNeighborhoodXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions); + + public void getNeighborhoodYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions); + + public void getNeighborhoodXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions); + + public void getNeighborhoodX(int x, int y, int z, double[] buffer, byte boundaryConditions); + + public void getNeighborhoodY(int x, int y, int z, double[] buffer, byte boundaryConditions); + + public void getNeighborhoodZ(int x, int y, int z, double[] buffer, byte boundaryConditions); + + public void getNeighborhoodXY(int x, int y, int z, double[][] buffer, byte boundaryConditions); + + public void getNeighborhoodXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions); + + public void getNeighborhoodYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions); + + public void getNeighborhoodXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions); + + // putBounded section + + public void putBoundedX(int x, int y, int z, byte[] buffer); + + public void putBoundedY(int x, int y, int z, byte[] buffer); + + public void putBoundedZ(int x, int y, int z, byte[] buffer); + + public void putBoundedXY(int x, int y, int z, byte[][] buffer); + + public void putBoundedXZ(int x, int y, int z, byte[][] buffer); + + public void putBoundedYZ(int x, int y, int z, byte[][] buffer); + + public void putBoundedXYZ(int x, int y, int z, byte[][][] buffer); + + public void putBoundedX(int x, int y, int z, short[] buffer); + + public void putBoundedY(int x, int y, int z, short[] buffer); + + public void putBoundedZ(int x, int y, int z, short[] buffer); + + public void putBoundedXY(int x, int y, int z, short[][] buffer); + + public void putBoundedXZ(int x, int y, int z, short[][] buffer); + + public void putBoundedYZ(int x, int y, int z, short[][] buffer); + + public void putBoundedXYZ(int x, int y, int z, short[][][] buffer); + + public void putBoundedX(int x, int y, int z, float[] buffer); + + public void putBoundedY(int x, int y, int z, float[] buffer); + + public void putBoundedZ(int x, int y, int z, float[] buffer); + + public void putBoundedXY(int x, int y, int z, float[][] buffer); + + public void putBoundedXZ(int x, int y, int z, float[][] buffer); + + public void putBoundedYZ(int x, int y, int z, float[][] buffer); + + public void putBoundedXYZ(int x, int y, int z, float[][][] buffer); + + public void putBoundedX(int x, int y, int z, double[] buffer); + + public void putBoundedY(int x, int y, int z, double[] buffer); + + public void putBoundedZ(int x, int y, int z, double[] buffer); + + public void putBoundedXY(int x, int y, int z, double[][] buffer); + + public void putBoundedXZ(int x, int y, int z, double[][] buffer); + + public void putBoundedYZ(int x, int y, int z, double[][] buffer); + + public void putBoundedXYZ(int x, int y, int z, double[][][] buffer); + +} diff --git a/src/bilib/src/imageware/Buffer.java b/src/bilib/src/imageware/Buffer.java new file mode 100644 index 0000000..29206a3 --- /dev/null +++ b/src/bilib/src/imageware/Buffer.java @@ -0,0 +1,188 @@ +package imageware; + +/** + * Class Buffer. + * + * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de + * Lausanne, Lausanne, Switzerland + */ + +public interface Buffer { + public int getType(); + + public String getTypeToString(); + + public int getDimension(); + + public int[] getSize(); + + public int getSizeX(); + + public int getSizeY(); + + public int getSizeZ(); + + public int getWidth(); + + public int getHeight(); + + public int getDepth(); + + public int getTotalSize(); + + public boolean isSameSize(ImageWare imageware); + + // get section + public void getX(int x, int y, int z, ImageWare buffer); + + public void getY(int x, int y, int z, ImageWare buffer); + + public void getZ(int x, int y, int z, ImageWare buffer); + + public void getXY(int x, int y, int z, ImageWare buffer); + + public void getXZ(int x, int y, int z, ImageWare buffer); + + public void getYZ(int x, int y, int z, ImageWare buffer); + + public void getXYZ(int x, int y, int z, ImageWare buffer); + + public void getX(int x, int y, int z, byte[] buffer); + + public void getY(int x, int y, int z, byte[] buffer); + + public void getZ(int x, int y, int z, byte[] buffer); + + public void getXY(int x, int y, int z, byte[][] buffer); + + public void getXZ(int x, int y, int z, byte[][] buffer); + + public void getYZ(int x, int y, int z, byte[][] buffer); + + public void getXYZ(int x, int y, int z, byte[][][] buffer); + + public void getX(int x, int y, int z, short[] buffer); + + public void getY(int x, int y, int z, short[] buffer); + + public void getZ(int x, int y, int z, short[] buffer); + + public void getXY(int x, int y, int z, short[][] buffer); + + public void getXZ(int x, int y, int z, short[][] buffer); + + public void getYZ(int x, int y, int z, short[][] buffer); + + public void getXYZ(int x, int y, int z, short[][][] buffer); + + public void getX(int x, int y, int z, float[] buffer); + + public void getY(int x, int y, int z, float[] buffer); + + public void getZ(int x, int y, int z, float[] buffer); + + public void getXY(int x, int y, int z, float[][] buffer); + + public void getXZ(int x, int y, int z, float[][] buffer); + + public void getYZ(int x, int y, int z, float[][] buffer); + + public void getXYZ(int x, int y, int z, float[][][] buffer); + + public void getX(int x, int y, int z, double[] buffer); + + public void getY(int x, int y, int z, double[] buffer); + + public void getZ(int x, int y, int z, double[] buffer); + + public void getXY(int x, int y, int z, double[][] buffer); + + public void getXZ(int x, int y, int z, double[][] buffer); + + public void getYZ(int x, int y, int z, double[][] buffer); + + public void getXYZ(int x, int y, int z, double[][][] buffer); + + // put section + public void putX(int x, int y, int z, ImageWare buffer); + + public void putY(int x, int y, int z, ImageWare buffer); + + public void putZ(int x, int y, int z, ImageWare buffer); + + public void putXY(int x, int y, int z, ImageWare buffer); + + public void putXZ(int x, int y, int z, ImageWare buffer); + + public void putYZ(int x, int y, int z, ImageWare buffer); + + public void putXYZ(int x, int y, int z, ImageWare buffer); + + public void putX(int x, int y, int z, byte[] buffer); + + public void putY(int x, int y, int z, byte[] buffer); + + public void putZ(int x, int y, int z, byte[] buffer); + + public void putXY(int x, int y, int z, byte[][] buffer); + + public void putXZ(int x, int y, int z, byte[][] buffer); + + public void putYZ(int x, int y, int z, byte[][] buffer); + + public void putXYZ(int x, int y, int z, byte[][][] buffer); + + public void putX(int x, int y, int z, short[] buffer); + + public void putY(int x, int y, int z, short[] buffer); + + public void putZ(int x, int y, int z, short[] buffer); + + public void putXY(int x, int y, int z, short[][] buffer); + + public void putXZ(int x, int y, int z, short[][] buffer); + + public void putYZ(int x, int y, int z, short[][] buffer); + + public void putXYZ(int x, int y, int z, short[][][] buffer); + + public void putX(int x, int y, int z, float[] buffer); + + public void putY(int x, int y, int z, float[] buffer); + + public void putZ(int x, int y, int z, float[] buffer); + + public void putXY(int x, int y, int z, float[][] buffer); + + public void putXZ(int x, int y, int z, float[][] buffer); + + public void putYZ(int x, int y, int z, float[][] buffer); + + public void putXYZ(int x, int y, int z, float[][][] buffer); + + public void putX(int x, int y, int z, double[] buffer); + + public void putY(int x, int y, int z, double[] buffer); + + public void putZ(int x, int y, int z, double[] buffer); + + public void putXY(int x, int y, int z, double[][] buffer); + + public void putXZ(int x, int y, int z, double[][] buffer); + + public void putYZ(int x, int y, int z, double[][] buffer); + + public void putXYZ(int x, int y, int z, double[][][] buffer); + + // get Slice in a specific type for a fast and direct access + public Object[] getVolume(); + + public byte[] getSliceByte(int z); + + public short[] getSliceShort(int z); + + public float[] getSliceFloat(int z); + + public double[] getSliceDouble(int z); + +} diff --git a/src/bilib/src/imageware/Builder.java b/src/bilib/src/imageware/Builder.java new file mode 100644 index 0000000..6bf68a3 --- /dev/null +++ b/src/bilib/src/imageware/Builder.java @@ -0,0 +1,732 @@ +package imageware; + +import ij.ImagePlus; +import ij.ImageStack; +import ij.WindowManager; +import ij.process.ByteProcessor; +import ij.process.FloatProcessor; +import ij.process.ImageProcessor; +import ij.process.ShortProcessor; + +import java.awt.Image; + +/** + * Class Builder. + * + * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de + * Lausanne, Lausanne, Switzerland + */ + +public class Builder { + + /** + * Wrap a imageware of from the focused image of ImageJ. + */ + public static ImageWare wrapOnFocus() { + ImagePlus imp = WindowManager.getCurrentImage(); + return wrap(imp.getStack()); + } + + /** + * Wrap a imageware around a ImagePlus object. + * + * @param imp + * an ImagePlus object to wrap + */ + public static ImageWare wrap(ImagePlus imp) { + return wrap(imp.getStack()); + } + + /** + * Wrap a imageware around a ImageStack object. + * + * @param stack + * an ImageStack object to wrap + */ + public static ImageWare wrap(ImageStack stack) { + if (stack == null) + throw_null(); + ImageProcessor ip = stack.getProcessor(1); + if (ip instanceof ByteProcessor) { + return new ByteSet(stack, ImageWare.WRAP); + } + else if (ip instanceof ShortProcessor) { + return new ShortSet(stack, ImageWare.WRAP); + } + else if (ip instanceof FloatProcessor) { + return new FloatSet(stack, ImageWare.WRAP); + } + else { + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to wrap this ImageStack object.\n" + + "Support only the 8-bit, 16-bit and 32-bits type.\n" + "-------------------------------------------------------\n"); + } + } + + /** + * Create an empty imageware of a specified type. + * + * @param nx + * size in X axis + * @param ny + * size in Y axis + * @param nz + * size in Z axis + * @param type + * type of the imageware + */ + public static ImageWare create(int nx, int ny, int nz, int type) { + switch (type) { + case ImageWare.BYTE: + return new ByteSet(nx, ny, nz); + case ImageWare.SHORT: + return new ShortSet(nx, ny, nz); + case ImageWare.FLOAT: + return new FloatSet(nx, ny, nz); + case ImageWare.DOUBLE: + return new DoubleSet(nx, ny, nz); + default: + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + ".\n" + + "-------------------------------------------------------\n"); + } + } + + /** + * Create a imageware of from an Java AWT Image. + * + * @param image + * an Java AWT Image object + */ + public static ImageWare create(Image image) { + if (image == null) + throw_null(); + return new ByteSet(image, ImageWare.CREATE); + } + + /** + * Create a imageware of from the focussed image of ImageJ. + */ + public static ImageWare createOnFocus() { + return create(WindowManager.getCurrentImage()); + } + + /** + * Create a imageware of from an ImageStack. + * + * @param stack + * an ImageStack object + */ + public static ImageWare create(ImageStack stack) { + if (stack == null) + throw_null(); + ImageWare wrapped = wrap(stack); + return wrapped.duplicate(); + } + + /** + * Create a imageware of from an ImagePlus. + * + * @param imp + * an ImagePlus object + */ + public static ImageWare create(ImagePlus imp) { + if (imp == null) + throw_null(); + ImageWare wrapped = wrap(imp); + return wrapped.duplicate(); + } + + /** + * Create an array of 3 datasets from an ImagePlus. + * + * @param imp + * an ImagePlus object + */ + public static ImageWare[] createColors(ImagePlus imp) { + if (imp == null) + throw_null(); + return createColors(imp.getStack()); + } + + /** + * Create an array of 3 imageware from an ImageStack. + * + * @param stack + * an ImageStack object + */ + public static ImageWare[] createColors(ImageStack stack) { + if (stack == null) + throw_null(); + ImageWare color[] = new ImageWare[3]; + color[0] = new ByteSet(stack, ImageWare.RED); + color[1] = new ByteSet(stack, ImageWare.GREEN); + color[2] = new ByteSet(stack, ImageWare.BLUE); + return color; + } + + /** + * Create a imageware of a specific channel if the ImagePlus is a color + * image. + * + * @param imp + * an ImagePlus object + */ + public static ImageWare createColorChannel(ImagePlus imp, byte channel) { + if (imp == null) + throw_null(); + return createColorChannel(imp.getStack(), channel); + } + + /** + * Create a imageware of a specific channel if the ImageStack is a color + * image. + * + * @param stack + * an ImageStack object + */ + public static ImageWare createColorChannel(ImageStack stack, byte channel) { + if (stack == null) + throw_null(); + return new ByteSet(stack, channel); + } + + /** + * Create a imageware of from an array. + * + * @param object + * byte 1D array + */ + public static ImageWare create(byte[] object) { + if (object == null) + throw_null(); + return new ByteSet(object, ImageWare.CREATE); + } + + /** + * Create a imageware of from an array. + * + * @param object + * short 1D array + */ + public static ImageWare create(short[] object) { + if (object == null) + throw_null(); + return new ShortSet(object, ImageWare.CREATE); + } + + /** + * Create a imageware of from an array. + * + * @param object + * float 1D array + */ + public static ImageWare create(float[] object) { + if (object == null) + throw_null(); + return new FloatSet(object, ImageWare.CREATE); + } + + /** + * Create a imageware of from an array. + * + * @param object + * double 1D array + */ + public static ImageWare create(double[] object) { + if (object == null) + throw_null(); + return new DoubleSet(object, ImageWare.CREATE); + } + + /** + * Create a imageware of from an array. + * + * @param object + * byte 2D array + */ + public static ImageWare create(byte[][] object) { + if (object == null) + throw_null(); + return new ByteSet(object, ImageWare.CREATE); + } + + /** + * Create a imageware of from an array. + * + * @param object + * short 2D array + */ + public static ImageWare create(short[][] object) { + if (object == null) + throw_null(); + return new ByteSet(object, ImageWare.CREATE); + } + + /** + * Create a imageware of from an array. + * + * @param object + * float 2D array + */ + public static ImageWare create(float[][] object) { + if (object == null) + throw_null(); + return new FloatSet(object, ImageWare.CREATE); + } + + /** + * Create a imageware of from an array. + * + * @param object + * double 2D array + */ + public static ImageWare create(double[][] object) { + if (object == null) + throw_null(); + return new DoubleSet(object, ImageWare.CREATE); + } + + /** + * Create a imageware of from an array. + * + * @param object + * byte 3D array + */ + public static ImageWare create(byte[][][] object) { + if (object == null) + throw_null(); + return new ByteSet(object, ImageWare.CREATE); + } + + /** + * Create a imageware of from an array. + * + * @param object + * short 3D array + */ + public static ImageWare create(short[][][] object) { + if (object == null) + throw_null(); + return new ShortSet(object, ImageWare.CREATE); + } + + /** + * Create a imageware of from an array. + * + * @param object + * float 3D array + */ + public static ImageWare create(float[][][] object) { + if (object == null) + throw_null(); + return new FloatSet(object, ImageWare.CREATE); + } + + /** + * Create a imageware of from an array. + * + * @param object + * double 3D array + */ + public static ImageWare create(double[][][] object) { + if (object == null) + throw_null(); + return new DoubleSet(object, ImageWare.CREATE); + } + + /** + * Create a imageware of a specified type from an Java AWT Image object. + * + * @param image + * an Java AWT Image object + * @param type + * type of the imageware + */ + public static ImageWare create(Image image, int type) { + if (image == null) + throw_null(); + switch (type) { + case ImageWare.BYTE: + return new ByteSet(image, ImageWare.CREATE); + case ImageWare.SHORT: + return new ShortSet(image, ImageWare.CREATE); + case ImageWare.FLOAT: + return new FloatSet(image, ImageWare.CREATE); + case ImageWare.DOUBLE: + return new DoubleSet(image, ImageWare.CREATE); + default: + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + ".\n" + + "-------------------------------------------------------\n"); + } + } + + /** + * Create a imageware of from the focused image of ImageJ. + * + * @param type + * type of the imageware + */ + public static ImageWare createOnFocus(int type) { + return create(WindowManager.getCurrentImage(), type); + } + + /** + * Create a imageware of a specified type from an ImagePlus object. + * + * @param imp + * an ImagePlus object + * @param type + * type of the imageware + */ + public static ImageWare create(ImagePlus imp, int type) { + if (imp == null) + throw_null(); + switch (type) { + case ImageWare.BYTE: + return new ByteSet(imp.getStack(), ImageWare.CREATE); + case ImageWare.SHORT: + return new ShortSet(imp.getStack(), ImageWare.CREATE); + case ImageWare.FLOAT: + return new FloatSet(imp.getStack(), ImageWare.CREATE); + case ImageWare.DOUBLE: + return new DoubleSet(imp.getStack(), ImageWare.CREATE); + default: + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + ".\n" + + "-------------------------------------------------------\n"); + } + } + + /** + * Create a imageware of a specified type from an ImageStack object. + * + * @param object + * an ImageStack object + * @param type + * type of the imageware + */ + public static ImageWare create(ImageStack object, int type) { + if (object == null) + throw_null(); + ImageWare wrapped = wrap(object); + return wrapped.convert(type); + } + + /** + * Create an array of 3 datasets from an ImagePlus. + * + * @param imp + * an ImagePlus object + * @param type + * type of the imageware + */ + public static ImageWare[] createColor(ImagePlus imp, int type) { + if (imp == null) + throw_null(); + return createColor(imp.getStack(), type); + } + + /** + * Create an array of 3 imageware from an ColorProcessor. + * + * @param stack + * an ColorProcessor object + * @param type + * type of the imageware + */ + public static ImageWare[] createColor(ImageStack stack, int type) { + if (stack == null) + throw_null(); + ImageWare color[] = new ImageWare[3]; + switch (type) { + case ImageWare.BYTE: + color[0] = new ByteSet(stack, ImageWare.RED); + color[1] = new ByteSet(stack, ImageWare.GREEN); + color[2] = new ByteSet(stack, ImageWare.BLUE); + break; + case ImageWare.SHORT: + color[0] = new ShortSet(stack, ImageWare.RED); + color[1] = new ShortSet(stack, ImageWare.GREEN); + color[2] = new ShortSet(stack, ImageWare.BLUE); + break; + case ImageWare.FLOAT: + color[0] = new FloatSet(stack, ImageWare.RED); + color[1] = new FloatSet(stack, ImageWare.GREEN); + color[2] = new FloatSet(stack, ImageWare.BLUE); + break; + case ImageWare.DOUBLE: + color[0] = new DoubleSet(stack, ImageWare.RED); + color[1] = new DoubleSet(stack, ImageWare.GREEN); + color[2] = new DoubleSet(stack, ImageWare.BLUE); + break; + default: + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + ".\n" + + "-------------------------------------------------------\n"); + } + return color; + } + + /** + * Create a imageware of a specific channel if the ImagePlus is a color + * image. + * + * @param imp + * an ImagePlus object + */ + public static ImageWare createColorChannel(ImagePlus imp, byte channel, int type) { + if (imp == null) + throw_null(); + return createColorChannel(imp.getStack(), channel, type); + } + + /** + * Create a imageware of a specific channel if the ImageStack is a color + * image. + * + * @param stack + * an ImageStack object + */ + public static ImageWare createColorChannel(ImageStack stack, byte channel, int type) { + if (stack == null) + throw_null(); + ImageWare out = null; + switch (type) { + case ImageWare.BYTE: + out = new ByteSet(stack, channel); + break; + case ImageWare.SHORT: + out = new ShortSet(stack, channel); + break; + case ImageWare.FLOAT: + out = new FloatSet(stack, channel); + break; + case ImageWare.DOUBLE: + out = new DoubleSet(stack, channel); + break; + default: + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + ".\n" + + "-------------------------------------------------------\n"); + } + return out; + } + + /** + * Create a imageware of a specified type from an array. + * + * @param object + * byte 1D array + * @param type + * type of the imageware + */ + public static ImageWare create(byte[] object, int type) { + if (object == null) + throw_null(); + ImageWare out = createType(object.length, 1, 1, type); + out.putX(0, 0, 0, object); + return out; + } + + /** + * Create a imageware of a specified type from an array. + * + * @param object + * short 1D array + * @param type + * type of the imageware + */ + public static ImageWare create(short[] object, int type) { + if (object == null) + throw_null(); + ImageWare out = createType(object.length, 1, 1, type); + out.putX(0, 0, 0, object); + return out; + } + + /** + * Create a imageware of a specified type from an array. + * + * @param object + * float 1D array + * @param type + * type of the imageware + */ + public static ImageWare create(float[] object, int type) { + if (object == null) + throw_null(); + ImageWare out = createType(object.length, 1, 1, type); + out.putX(0, 0, 0, object); + return out; + } + + /** + * Create a imageware of a specified type from an array. + * + * @param object + * double 1D array + * @param type + * type of the imageware + */ + public static ImageWare create(double[] object, int type) { + if (object == null) + throw_null(); + ImageWare out = createType(object.length, 1, 1, type); + out.putX(0, 0, 0, object); + return out; + } + + /** + * Create a imageware of a specified type from an array. + * + * @param object + * byte 2D array + * @param type + * type of the imageware + */ + public static ImageWare create(byte[][] object, int type) { + if (object == null) + throw_null(); + ImageWare out = createType(object.length, object[0].length, 1, type); + out.putXY(0, 0, 0, object); + return out; + } + + /** + * Create a imageware of a specified type from an array. + * + * @param object + * short 2D array + * @param type + * type of the imageware + */ + public static ImageWare create(short[][] object, int type) { + if (object == null) + throw_null(); + ImageWare out = createType(object.length, object[0].length, 1, type); + out.putXY(0, 0, 0, object); + return out; + } + + /** + * Create a imageware of a specified type from an array. + * + * @param object + * float 2D array + * @param type + * type of the imageware + */ + public static ImageWare create(float[][] object, int type) { + if (object == null) + throw_null(); + ImageWare out = createType(object.length, object[0].length, 1, type); + out.putXY(0, 0, 0, object); + return out; + } + + /** + * Create a imageware of a specified type from an array. + * + * @param object + * double 2D array + * @param type + * type of the imageware + */ + public static ImageWare create(double[][] object, int type) { + if (object == null) + throw_null(); + ImageWare out = createType(object.length, object[0].length, 1, type); + out.putXY(0, 0, 0, object); + return out; + } + + /** + * Create a imageware of a specified type from an array. + * + * @param object + * byte 3D array + * @param type + * type of the imageware + */ + public static ImageWare create(byte[][][] object, int type) { + if (object == null) + throw_null(); + ImageWare out = createType(object.length, object[0].length, object[0][0].length, type); + out.putXYZ(0, 0, 0, object); + return out; + } + + /** + * Create a imageware of a specified type from an array. + * + * @param object + * short 3D array + * @param type + * type of the imageware + */ + public static ImageWare create(short[][][] object, int type) { + if (object == null) + throw_null(); + ImageWare out = createType(object.length, object[0].length, object[0][0].length, type); + out.putXYZ(0, 0, 0, object); + return out; + } + + /** + * Create a imageware of a specified type from an array. + * + * @param object + * float 3D array + * @param type + * type of the imageware + */ + public static ImageWare create(float[][][] object, int type) { + if (object == null) + throw_null(); + ImageWare out = createType(object.length, object[0].length, object[0][0].length, type); + out.putXYZ(0, 0, 0, object); + return out; + } + + /** + * Create a imageware of a specified type from an array. + * + * @param object + * double 3D array + * @param type + * type of the imageware + */ + public static ImageWare create(double[][][] object, int type) { + if (object == null) + throw_null(); + ImageWare out = createType(object.length, object[0].length, object[0][0].length, type); + out.putXYZ(0, 0, 0, object); + return out; + } + + /** + */ + private static ImageWare createType(int nx, int ny, int nz, int type) { + ImageWare out = null; + switch (type) { + case ImageWare.BYTE: + out = new ByteSet(nx, ny, nz); + break; + case ImageWare.SHORT: + out = new ShortSet(nx, ny, nz); + break; + case ImageWare.FLOAT: + out = new FloatSet(nx, ny, nz); + break; + case ImageWare.DOUBLE: + out = new DoubleSet(nx, ny, nz); + break; + default: + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create this object.\n" + + "-------------------------------------------------------\n"); + } + return out; + } + + private static void throw_null() { + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to wrap the ImagePlus.\n" + + "The object parameter is null.\n" + "-------------------------------------------------------\n"); + } + +} diff --git a/src/bilib/src/imageware/ByteAccess.java b/src/bilib/src/imageware/ByteAccess.java new file mode 100644 index 0000000..888b0d0 --- /dev/null +++ b/src/bilib/src/imageware/ByteAccess.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import java.awt.Image; /** * Class ByteAccess. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class ByteAccess extends ByteBuffer implements Access { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected ByteAccess(int nx, int ny, int nz) { super(nx, ny, nz); } protected ByteAccess(Image image, int mode) { super(image, mode); } protected ByteAccess(ImageStack stack, int mode) { super(stack, mode); } protected ByteAccess(ImageStack stack, byte chan) { super(stack, chan); } protected ByteAccess(byte[] array, int mode) { super(array, mode); } protected ByteAccess(byte[][] array, int mode) { super(array, mode); } protected ByteAccess(byte[][][] array, int mode) { super(array, mode); } protected ByteAccess(short[] array, int mode) { super(array, mode); } protected ByteAccess(short[][] array, int mode) { super(array, mode); } protected ByteAccess(short[][][] array, int mode) { super(array, mode); } protected ByteAccess(float[] array, int mode) { super(array, mode); } protected ByteAccess(float[][] array, int mode) { super(array, mode); } protected ByteAccess(float[][][] array, int mode) { super(array, mode); } protected ByteAccess(double[] array, int mode) { super(array, mode); } protected ByteAccess(double[][] array, int mode) { super(array, mode); } protected ByteAccess(double[][][] array, int mode) { super(array, mode); } // ------------------------------------------------------------------ // // getPixel section // // ------------------------------------------------------------------ /** * Get a pixel at specific position without specific boundary conditions * * If the positions is outside of this imageware, the method return 0.0. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @return a pixel value */ public double getPixel(int x, int y, int z) { if (x >= nx) return 0.0; if (y >= ny) return 0.0; if (z >= nz) return 0.0; if (x < 0) return 0.0; if (y < 0) return 0.0; if (z < 0) return 0.0; return ((byte[]) data[z])[x + y * nx] & 0xFF; } /** * Get a pixel at specific position with specific boundary conditions * * If the positions is outside of this imageware, the method apply the * boundary conditions to return a value. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @return a pixel value */ public double getPixel(int x, int y, int z, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a pixel \n" + "at the position (" + x + "," + y + "," + z + ".\n" + "-------------------------------------------------------\n"); } int xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } return ((byte[]) data[zp])[xp + yp * nx] & 0xFF; } /** * Get a interpolated pixel value at specific position without specific * boundary conditions. * * If the positions is not on the pixel grid, the method return a * interpolated value of the pixel (linear interpolation). If the positions * is outside of this imageware, the method return 0.0. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @return a interpolated value */ public double getInterpolatedPixel(double x, double y, double z) { if (x > nx - 1) return 0.0; if (y > ny - 1) return 0.0; if (z > nz - 1) return 0.0; if (x < 0) return 0.0; if (y < 0) return 0.0; if (z < 0) return 0.0; double output = 0.0; /* * int i = (x >= 0.0 ? ((int)x) : ((int)x - 1)); int j = (y >= 0.0 ? * ((int)y) : ((int)y - 1)); int k = (z >= 0.0 ? ((int)z) : ((int)z - * 1)); */ int i = (x >= 0.0 ? ((int) x) : ((int) x - 1)); int j = (y >= 0.0 ? ((int) y) : ((int) y - 1)); int k = (z >= 0.0 ? ((int) z) : ((int) z - 1)); boolean fi = (i == nx - 1); boolean fj = (j == ny - 1); boolean fk = (k == nz - 1); int index = i + j * nx; switch (getDimension()) { case 1: double v1_0 = (double) (((byte[]) data[k])[index] & 0xFF); double v1_1 = (fi ? v1_0 : (double) (((byte[]) data[k])[index + 1] & 0xFF)); double dx1 = x - (double) i; return v1_1 * dx1 - v1_0 * (dx1 - 1.0); case 2: double v2_00 = (double) (((byte[]) data[k])[index] & 0xFF); double v2_10 = (fi ? v2_00 : (double) (((byte[]) data[k])[index + 1] & 0xFF)); double v2_01 = (fj ? v2_00 : (double) (((byte[]) data[k])[index + nx] & 0xFF)); double v2_11 = (fi ? (fj ? v2_00 : v2_01) : (double) (((byte[]) data[k])[index + 1 + nx] & 0xFF)); double dx2 = x - (double) i; double dy2 = y - (double) j; return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0))); case 3: double v3_000 = (double) (((byte[]) data[k])[index] & 0xFF); double v3_100 = (fi ? v3_000 : (double) (((byte[]) data[k])[index + 1] & 0xFF)); double v3_010 = (fj ? v3_000 : (double) (((byte[]) data[k])[index + nx] & 0xFF)); double v3_110 = (fi ? (fj ? v3_000 : v3_010) : (double) (((byte[]) data[k])[index + 1 + nx] & 0xFF)); double v3_001 = (fk ? v3_000 : (double) (((byte[]) data[k + 1])[index] & 0xFF)); double v3_011 = (fk ? (fj ? v3_000 : v3_010) : (double) (((byte[]) data[k + 1])[index + 1] & 0xFF)); double v3_101 = (fk ? (fi ? v3_000 : v3_100) : (double) (((byte[]) data[k + 1])[index + nx] & 0xFF)); double v3_111 = (fk ? (fj ? (fi ? v3_000 : v3_100) : v3_110) : (double) (((byte[]) data[k + 1])[index + 1 + nx] & 0xFF)); double dx3 = x - (double) i; double dy3 = y - (double) j; double dz3 = z - (double) k; double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0))); double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0))); return z2 * dz3 - z1 * (dz3 - 1.0); } return output; } /** * Get a interpolated pixel value at specific position with specific * boundary conditions. * * If the positions is not on the pixel grid, the method return a * interpolated value of the pixel (linear interpolation). If the positions * is outside of this imageware, the method apply the boundary conditions to * return a value. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @param boundaryConditions * MIRROR or PERIODIC boundary conditions * @return a interpolated value */ public double getInterpolatedPixel(double x, double y, double z, byte boundaryConditions) { double output = 0.0; int i = (x >= 0.0 ? ((int) x) : ((int) x - 1)); int j = (y >= 0.0 ? ((int) y) : ((int) y - 1)); int k = (z >= 0.0 ? ((int) z) : ((int) z - 1)); switch (getDimension()) { case 1: double v1_0 = getPixel(i, j, k, boundaryConditions); double v1_1 = getPixel(i + 1, j, k, boundaryConditions); double dx1 = x - (double) i; return v1_1 * dx1 - v1_0 * (dx1 - 1.0); case 2: double v2_00 = getPixel(i, j, k, boundaryConditions); double v2_10 = getPixel(i + 1, j, k, boundaryConditions); double v2_01 = getPixel(i, j + 1, k, boundaryConditions); double v2_11 = getPixel(i + 1, j + 1, k, boundaryConditions); double dx2 = x - (double) i; double dy2 = y - (double) j; return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0))); case 3: double v3_000 = getPixel(i, j, k, boundaryConditions); double v3_100 = getPixel(i + 1, j, k, boundaryConditions); double v3_010 = getPixel(i, j + 1, k, boundaryConditions); double v3_110 = getPixel(i + 1, j + 1, k, boundaryConditions); double v3_001 = getPixel(i, j, k + 1, boundaryConditions); double v3_011 = getPixel(i + 1, j, k + 1, boundaryConditions); double v3_101 = getPixel(i, j + 1, k + 1, boundaryConditions); double v3_111 = getPixel(i + 1, j + 1, k + 1, boundaryConditions); double dx3 = x - (double) i; double dy3 = y - (double) j; double dz3 = z - (double) k; double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0))); double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0))); return z2 * dz3 - z1 * (dz3 - 1.0); } return output; } // ------------------------------------------------------------------ // // putPixel section // // ------------------------------------------------------------------ /** * Put a pixel at specific position * * If the positions is outside of this imageware, the method does nothing. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis */ public void putPixel(int x, int y, int z, double value) { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; if (x < 0) return; if (y < 0) return; if (z < 0) return; ((byte[]) data[z])[x + y * nx] = (byte) value; } // ------------------------------------------------------------------ // // getBounded section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (byte) (tmp[offset] & 0xFF); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (short) (tmp[offset] & 0xFF); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (float) (tmp[offset] & 0xFF); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (double) (tmp[offset] & 0xFF); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (byte) (tmp[offset] & 0xFF); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (short) (tmp[offset] & 0xFF); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (float) (tmp[offset] & 0xFF); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (double) (tmp[offset] & 0xFF); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (byte) (((byte[]) data[k])[offset] & 0xFF); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (short) (((byte[]) data[k])[offset] & 0xFF); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (float) (((byte[]) data[k])[offset] & 0xFF); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (double) (((byte[]) data[k])[offset] & 0xFF); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); byte[] tmp = (byte[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (byte) (tmp[offset] & 0xFF); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); byte[] tmp = (byte[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (short) (tmp[offset] & 0xFF); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); byte[] tmp = (byte[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (float) (tmp[offset] & 0xFF); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); byte[] tmp = (byte[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (double) (tmp[offset] & 0xFF); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (byte) (((byte[]) data[z])[offset] & 0xFF); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (short) (((byte[]) data[z])[offset] & 0xFF); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (float) (((byte[]) data[z])[offset] & 0xFF); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (double) (((byte[]) data[z])[offset] & 0xFF); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (byte) (((byte[]) data[z])[offset] & 0xFF); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (short) (((byte[]) data[z])[offset] & 0xFF); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (float) (((byte[]) data[z])[offset] & 0xFF); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (double) (((byte[]) data[z])[offset] & 0xFF); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, byte[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { byte[] tmp = (byte[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (byte) (tmp[offset] & 0xFF); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, short[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { byte[] tmp = (byte[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (short) (tmp[offset] & 0xFF); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, float[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { byte[] tmp = (byte[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (float) (tmp[offset] & 0xFF); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, double[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { byte[] tmp = (byte[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (double) (tmp[offset] & 0xFF); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } // ------------------------------------------------------------------ // // getBlock section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; byte[] tmp = (byte[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (byte) (tmp[xp + yp] & 0xFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; byte[] tmp = (byte[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (short) (tmp[xp + yp] & 0xFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; byte[] tmp = (byte[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (float) (tmp[xp + yp] & 0xFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; byte[] tmp = (byte[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (double) (tmp[xp + yp] & 0xFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } byte[] tmp = (byte[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (byte) (tmp[xp + yp * nx] & 0xFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } byte[] tmp = (byte[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (short) (tmp[xp + yp * nx] & 0xFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } byte[] tmp = (byte[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (float) (tmp[xp + yp * nx] & 0xFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } byte[] tmp = (byte[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (double) (tmp[xp + yp * nx] & 0xFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (byte) (((byte[]) data[zp])[xyp] & 0xFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (short) (((byte[]) data[zp])[xyp] & 0xFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (float) (((byte[]) data[zp])[xyp] & 0xFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (double) (((byte[]) data[zp])[xyp] & 0xFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } byte[] tmp = (byte[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (tmp[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } byte[] tmp = (byte[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (tmp[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } byte[] tmp = (byte[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (tmp[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } byte[] tmp = (byte[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (tmp[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (((byte[]) data[zp])[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (((byte[]) data[zp])[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (((byte[]) data[zp])[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (((byte[]) data[zp])[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (byte) (((byte[]) data[zp])[xp + yp * nx] & 0xFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (short) (((byte[]) data[zp])[xp + yp * nx] & 0xFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (float) (((byte[]) data[zp])[xp + yp * nx] & 0xFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (double) (((byte[]) data[zp])[xp + yp * nx] & 0xFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } byte[] tmp = (byte[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (byte) (tmp[xp + yp] & 0xFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } byte[] tmp = (byte[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (short) (tmp[xp + yp] & 0xFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } byte[] tmp = (byte[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (float) (tmp[xp + yp] & 0xFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } byte[] tmp = (byte[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (double) (tmp[xp + yp] & 0xFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } // ------------------------------------------------------------------ // // getBlock section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; byte[] tmp = ((byte[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (byte) (tmp[xp + yp] & 0xFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; byte[] tmp = ((byte[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (short) (tmp[xp + yp] & 0xFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; byte[] tmp = ((byte[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (float) (tmp[xp + yp] & 0xFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; byte[] tmp = ((byte[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (double) (tmp[xp + yp] & 0xFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; byte[] tmp = ((byte[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (byte) (tmp[xp + yp * nx] & 0xFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; byte[] tmp = ((byte[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (short) (tmp[xp + yp * nx] & 0xFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; byte[] tmp = ((byte[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (float) (tmp[xp + yp * nx] & 0xFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; byte[] tmp = ((byte[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (double) (tmp[xp + yp * nx] & 0xFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (byte) (((byte[]) data[zp])[xyp] & 0xFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (short) (((byte[]) data[zp])[xyp] & 0xFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (float) (((byte[]) data[zp])[xyp] & 0xFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (double) (((byte[]) data[zp])[xyp] & 0xFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; byte[] tmp = ((byte[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (tmp[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; byte[] tmp = ((byte[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (tmp[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; byte[] tmp = ((byte[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (tmp[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; byte[] tmp = ((byte[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (tmp[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (((byte[]) data[zp])[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (((byte[]) data[zp])[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (((byte[]) data[zp])[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (((byte[]) data[zp])[xp + yp] & 0xFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (byte) (((byte[]) data[zp])[xp + yp * nx] & 0xFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (short) (((byte[]) data[zp])[xp + yp * nx] & 0xFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (float) (((byte[]) data[zp])[xp + yp * nx] & 0xFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (double) (((byte[]) data[zp])[xp + yp * nx] & 0xFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } byte[] tmp = ((byte[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (byte) (tmp[xp + yp] & 0xFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } byte[] tmp = ((byte[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (short) (tmp[xp + yp] & 0xFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } byte[] tmp = ((byte[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (float) (tmp[xp + yp] & 0xFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } byte[] tmp = ((byte[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (double) (tmp[xp + yp] & 0xFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } // ------------------------------------------------------------------ // // putBounded section // // ------------------------------------------------------------------ /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i] & 0xFF); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i] & 0xFFFF); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i]); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i]); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i] & 0xFF); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i] & 0xFFFF); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i]); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); byte[] tmp = (byte[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i]); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((byte[]) data[k])[offset] = (byte) (buffer[i] & 0xFF); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((byte[]) data[k])[offset] = (byte) (buffer[i] & 0xFFFF); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((byte[]) data[k])[offset] = (byte) (buffer[i]); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((byte[]) data[k])[offset] = (byte) (buffer[i]); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); byte[] tmp = (byte[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i][j] & 0xFF); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); byte[] tmp = (byte[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i][j] & 0xFFFF); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); byte[] tmp = (byte[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i][j]); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); byte[] tmp = (byte[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i][j]); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((byte[]) data[k])[offset] = (byte) (buffer[i][j] & 0xFF); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((byte[]) data[k])[offset] = (byte) (buffer[i][j] & 0xFFFF); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((byte[]) data[k])[offset] = (byte) (buffer[i][j]); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((byte[]) data[k])[offset] = (byte) (buffer[i][j]); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((byte[]) data[k])[offset] = (byte) (buffer[i][j] & 0xFF); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((byte[]) data[k])[offset] = (byte) (buffer[i][j] & 0xFFFF); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((byte[]) data[k])[offset] = (byte) (buffer[i][j]); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((byte[]) data[k])[offset] = (byte) (buffer[i][j]); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, byte[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { byte[] tmp = (byte[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i][j][k] & 0xFF); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, short[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { byte[] tmp = (byte[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i][j][k] & 0xFFFF); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, float[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { byte[] tmp = (byte[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i][j][k]); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, double[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { byte[] tmp = (byte[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (byte) (buffer[i][j][k]); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/ByteBuffer.java b/src/bilib/src/imageware/ByteBuffer.java new file mode 100644 index 0000000..e9d7a5e --- /dev/null +++ b/src/bilib/src/imageware/ByteBuffer.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import ij.process.ByteProcessor; import ij.process.ColorProcessor; import ij.process.FloatProcessor; import ij.process.ImageProcessor; import ij.process.ShortProcessor; import java.awt.Image; import java.awt.image.ImageObserver; import java.awt.image.PixelGrabber; /** * Class ByteBuffer. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class ByteBuffer implements Buffer { protected Object[] data = null; protected int nx = 0; protected int ny = 0; protected int nz = 0; protected int nxy = 0; /** * Constructor of a empty 3D byte buffer. * * @param nx * size of the 3D buffer in the X axis * @param ny * size of the 3D buffer in the Y axis * @param nz * size of the 3D buffer in the Z axis */ protected ByteBuffer(int nx, int ny, int nz) { this.nx = nx; this.ny = ny; this.nz = nz; if (nx <= 0 || ny <= 0 || nz <= 0) throw_constructor(nx, ny, nz); allocate(); } /** * Constructor of a byte buffer from a object Image of Java. * * @param image * source to build a new imageware */ protected ByteBuffer(Image image, int mode) { if (image == null) { throw_constructor(); } ImageObserver observer = null; this.nx = image.getWidth(observer); this.ny = image.getHeight(observer); this.nz = 1; this.nxy = nx * ny; byte[] pixels = new byte[nxy]; PixelGrabber pg = new PixelGrabber(image, 0, 0, nx, ny, false); try { pg.grabPixels(); pixels = (byte[]) (pg.getPixels()); } catch (Exception e) { throw_constructor(); } allocate(); for (int k = 0; k < nxy; k++) ((byte[]) data[0])[k] = (byte) (pixels[k] & 0xFF); } /** * Constructor of a byte buffer from a ImageStack. * * New data are allocated if the mode is CREATE, the imageware use the data * of ImageJ if the mode is WRAP. * * @param stack * source to build a new imageware * @param mode * WRAP or CREATE */ protected ByteBuffer(ImageStack stack, int mode) { if (stack == null) { throw_constructor(); } this.nx = stack.getWidth(); this.ny = stack.getHeight(); this.nz = stack.getSize(); this.nxy = nx * ny; switch (mode) { case ImageWare.WRAP: this.data = stack.getImageArray(); break; case ImageWare.CREATE: allocate(); ImageProcessor ip = stack.getProcessor(1); if (ip instanceof ByteProcessor) { Object[] vol = stack.getImageArray(); for (int z = 0; z < nz; z++) { byte[] slice = (byte[]) vol[z]; for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] = (byte) (slice[k] & 0xFF); } } } else if (ip instanceof ShortProcessor) { Object[] vol = stack.getImageArray(); for (int z = 0; z < nz; z++) { short[] slice = (short[]) vol[z]; for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] = (byte) (slice[k] & 0xFFFF); } } } else if (ip instanceof FloatProcessor) { Object[] vol = stack.getImageArray(); for (int z = 0; z < nz; z++) { float[] slice = (float[]) vol[z]; for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] = (byte) slice[k]; } } } else if (ip instanceof ColorProcessor) { double r, g, b; int c; ColorProcessor cp; int[] pixels; for (int z = 0; z < nz; z++) { cp = (ColorProcessor) stack.getProcessor(z + 1); pixels = (int[]) cp.getPixels(); for (int k = 0; k < nxy; k++) { c = pixels[k]; r = (double) ((c & 0xFF0000) >> 16); g = (double) ((c & 0xFF00) >> 8); b = (double) ((c & 0xFF)); ((byte[]) data[z])[k] = (byte) ((r + g + b) / 3.0); } } } else { throw_constructor(); } break; default: throw_constructor(); break; } } /** * Constructor of a byte buffer from a specific color channel of ImageStack. * * New data are always allocated. If it is a gray image the imageware is * created and fill up with data of the source ImageStack. If it is a color * image only the selected channel is used to create this imageware. * * @param stack * source to build a new imageware * @param channel * RED, GREEN or BLUE */ protected ByteBuffer(ImageStack stack, byte channel) { if (stack == null) { throw_constructor(); } this.nx = stack.getWidth(); this.ny = stack.getHeight(); this.nz = stack.getSize(); this.nxy = nx * ny; allocate(); ImageProcessor ip = stack.getProcessor(1); if (ip instanceof ByteProcessor) { Object[] vol = stack.getImageArray(); for (int z = 0; z < nz; z++) { byte[] slice = (byte[]) vol[z]; for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] = (byte) (slice[k] & 0xFF); } } } else if (ip instanceof ShortProcessor) { Object[] vol = stack.getImageArray(); for (int z = 0; z < nz; z++) { short[] slice = (short[]) vol[z]; for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] = (byte) (slice[k] & 0xFFFF); } } } else if (ip instanceof FloatProcessor) { Object[] vol = stack.getImageArray(); for (int z = 0; z < nz; z++) { float[] slice = (float[]) vol[z]; for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] = (byte) slice[k]; } } } else if (ip instanceof ColorProcessor) { ColorProcessor cp; int[] pixels; for (int z = 0; z < nz; z++) { cp = (ColorProcessor) stack.getProcessor(z + 1); pixels = (int[]) cp.getPixels(); switch (channel) { case ImageWare.RED: for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] = (byte) ((pixels[k] & 0xFF0000) >> 16); } break; case ImageWare.GREEN: for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] = (byte) ((pixels[k] & 0xFF00) >> 8); } break; case ImageWare.BLUE: for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] = (byte) (pixels[k] & 0xFF); } break; default: throw_constructor(); } } } else { throw_constructor(); } } /** * Constructor of a byte buffer from a byte array. * * @param array * source to build this new imageware */ protected ByteBuffer(byte[] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = 1; this.nz = 1; allocate(); putX(0, 0, 0, array); } /** * Constructor of a byte buffer from a byte array. * * @param array * source to build this new imageware */ protected ByteBuffer(byte[][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = 1; allocate(); putXY(0, 0, 0, array); } /** * Constructor of a byte buffer from a byte array. * * @param array * source to build this new imageware */ protected ByteBuffer(byte[][][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = array[0][0].length; allocate(); putXYZ(0, 0, 0, array); } /** * Constructor of a byte buffer from a short array. * * @param array * source to build this new imageware */ protected ByteBuffer(short[] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = 1; this.nz = 1; allocate(); putX(0, 0, 0, array); } /** * Constructor of a byte buffer from a short array. * * @param array * source to build this new imageware */ protected ByteBuffer(short[][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = 1; allocate(); putXY(0, 0, 0, array); } /** * Constructor of a byte buffer from a short array. * * @param array * source to build this new imageware */ protected ByteBuffer(short[][][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = array[0][0].length; allocate(); putXYZ(0, 0, 0, array); } /** * Constructor of a byte buffer from a float array. * * @param array * source to build this new imageware */ protected ByteBuffer(float[] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = 1; this.nz = 1; allocate(); putX(0, 0, 0, array); } /** * Constructor of a byte buffer from a float array. * * @param array * source to build this new imageware */ protected ByteBuffer(float[][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = 1; allocate(); putXY(0, 0, 0, array); } /** * Constructor of a byte buffer from a float array. * * @param array * source to build this new imageware */ protected ByteBuffer(float[][][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = array[0][0].length; allocate(); putXYZ(0, 0, 0, array); } /** * Constructor of a byte buffer from a double array. * * @param array * source to build this new imageware */ protected ByteBuffer(double[] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = 1; this.nz = 1; allocate(); putX(0, 0, 0, array); } /** * Constructor of a byte buffer from a double array. * * @param array * source to build this new imageware */ protected ByteBuffer(double[][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = 1; allocate(); putXY(0, 0, 0, array); } /** * Constructor of a byte buffer from a double array. * * @param array * source to build this new imageware */ protected ByteBuffer(double[][][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = array[0][0].length; allocate(); putXYZ(0, 0, 0, array); } /** * Return the type of this imageware. * * @return the type of this imageware */ public int getType() { return ImageWare.BYTE; } /** * Return the type of this imageware in a string format. * * @return the type of this imageware translated in a string format */ public String getTypeToString() { return "Byte"; } /** * Return the number of dimension of this imageware (1, 2 or 3). * * @return the number of dimension of this imageware */ public int getDimension() { int dims = 0; dims += (nx > 1 ? 1 : 0); dims += (ny > 1 ? 1 : 0); dims += (nz > 1 ? 1 : 0); return dims; } /** * Return the size of the imageware int[0] : x, int[1] : y, int[2] : z. * * @return an array given the size of the imageware */ public int[] getSize() { int[] size = { nx, ny, nz }; return size; } /** * Return the size in the X axis. * * @return the size in the X axis */ public int getSizeX() { return nx; } /** * Return the size in the Y axis. * * @return the size in the Y axis */ public int getSizeY() { return ny; } /** * Return the size in the Z axis. * * @return the size in the Z axis */ public int getSizeZ() { return nz; } /** * Return the size in the X axis. * * @return the size in the X axis */ public int getWidth() { return nx; } /** * Return the size in the Y axis. * * @return the size in the Y axis */ public int getHeight() { return ny; } /** * Return the size in the Z axis. * * @return the size in the Z axis */ public int getDepth() { return nz; } /** * Return the number of pixels in the imageware. * * @return number of pixels in the imageware */ public int getTotalSize() { return nxy * nz; } /** * Return true is this imageware has the same size the imageware given as * parameter. * * @param imageware * imageware to be compared * @return true if the imageware of the same size than this imageware */ public boolean isSameSize(ImageWare imageware) { if (nx != imageware.getSizeX()) return false; if (ny != imageware.getSizeY()) return false; if (nz != imageware.getSizeZ()) return false; return true; } // ------------------------------------------------------------------ // // put Section // // ------------------------------------------------------------------ /** * Put an array into the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putX(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); double buf[] = new double[bnx]; buffer.getX(0, 0, 0, buf); putX(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putY(int x, int y, int z, ImageWare buffer) { int bny = buffer.getSizeY(); double buf[] = new double[bny]; buffer.getY(0, 0, 0, buf); putY(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putZ(int x, int y, int z, ImageWare buffer) { int bnz = buffer.getSizeZ(); double buf[] = new double[bnz]; buffer.getZ(0, 0, 0, buf); putZ(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putXY(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); int bny = buffer.getSizeY(); double buf[][] = new double[bnx][bny]; buffer.getXY(0, 0, 0, buf); putXY(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putXZ(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); int bnz = buffer.getSizeZ(); double buf[][] = new double[bnx][bnz]; buffer.getXZ(0, 0, 0, buf); putXZ(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putYZ(int x, int y, int z, ImageWare buffer) { int bny = buffer.getSizeY(); int bnz = buffer.getSizeZ(); double buf[][] = new double[bny][bnz]; buffer.getYZ(0, 0, 0, buf); putYZ(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putXYZ(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); int bny = buffer.getSizeY(); int bnz = buffer.getSizeZ(); double buf[][][] = new double[bnx][bny][bnz]; buffer.getXYZ(0, 0, 0, buf); putXYZ(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putX(int x, int y, int z, byte[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; System.arraycopy(buffer, 0, tmp, offset, leni); } catch (Exception e) { throw_put("X", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putX(int x, int y, int z, short[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (byte) (buffer[i] & 0xFFFF); offset++; } } catch (Exception e) { throw_put("X", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putX(int x, int y, int z, float[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (byte) (buffer[i]); offset++; } } catch (Exception e) { throw_put("X", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putX(int x, int y, int z, double[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (byte) (buffer[i]); offset++; } } catch (Exception e) { throw_put("X", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putY(int x, int y, int z, byte[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (byte) (buffer[i] & 0xFF); offset += nx; } } catch (Exception e) { throw_put("Y", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putY(int x, int y, int z, short[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (byte) (buffer[i] & 0xFFFF); offset += nx; } } catch (Exception e) { throw_put("Y", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putY(int x, int y, int z, float[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (byte) (buffer[i]); offset += nx; } } catch (Exception e) { throw_put("Y", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putY(int x, int y, int z, double[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (byte) (buffer[i]); offset += nx; } } catch (Exception e) { throw_put("Y", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * bybytete 1D array to put into the imageware */ public void putZ(int x, int y, int z, byte[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { ((byte[]) data[z])[offset] = (byte) (buffer[i] & 0xFF); z++; } } catch (Exception e) { throw_put("Z", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byshortte 1D array to put into the imageware */ public void putZ(int x, int y, int z, short[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { ((byte[]) data[z])[offset] = (byte) (buffer[i] & 0xFFFF); z++; } } catch (Exception e) { throw_put("Z", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byfloatte 1D array to put into the imageware */ public void putZ(int x, int y, int z, float[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { ((byte[]) data[z])[offset] = (byte) (buffer[i]); z++; } } catch (Exception e) { throw_put("Z", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * bydoublete 1D array to put into the imageware */ public void putZ(int x, int y, int z, double[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { ((byte[]) data[z])[offset] = (byte) (buffer[i]); z++; } } catch (Exception e) { throw_put("Z", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putXY(int x, int y, int z, byte[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (byte) (buffer[i][j] & 0xFF); } } } catch (Exception e) { throw_put("XY", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putXY(int x, int y, int z, short[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (byte) (buffer[i][j] & 0xFFFF); } } } catch (Exception e) { throw_put("XY", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putXY(int x, int y, int z, float[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (byte) (buffer[i][j]); } } } catch (Exception e) { throw_put("XY", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putXY(int x, int y, int z, double[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (byte) (buffer[i][j]); } } } catch (Exception e) { throw_put("XY", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putXZ(int x, int y, int z, byte[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + j * nx; for (int i = 0; i < leni; i++, offset++) { ((byte[]) data[z])[offset] = (byte) (buffer[i][j] & 0xFF); } } } catch (Exception e) { throw_put("YZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putXZ(int x, int y, int z, short[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + j * nx; for (int i = 0; i < leni; i++, offset++) { ((byte[]) data[z])[offset] = (byte) (buffer[i][j] & 0xFFFF); } } } catch (Exception e) { throw_put("YZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putXZ(int x, int y, int z, float[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + j * nx; for (int i = 0; i < leni; i++, offset++) { ((byte[]) data[z])[offset] = (byte) (buffer[i][j]); } } } catch (Exception e) { throw_put("YZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putXZ(int x, int y, int z, double[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + j * nx; for (int i = 0; i < leni; i++, offset++) { ((byte[]) data[z])[offset] = (byte) (buffer[i][j]); } } } catch (Exception e) { throw_put("YZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putYZ(int x, int y, int z, byte[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) for (int i = 0; i < leni; i++, offset += nx) { ((byte[]) data[z])[offset] = (byte) (buffer[i][j] & 0xFF); } } catch (Exception e) { throw_put("XZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putYZ(int x, int y, int z, short[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) for (int i = 0; i < leni; i++, offset += nx) { ((byte[]) data[z])[offset] = (byte) (buffer[i][j] & 0xFFFF); } } catch (Exception e) { throw_put("XZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putYZ(int x, int y, int z, float[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) for (int i = 0; i < leni; i++, offset += nx) { ((byte[]) data[z])[offset] = (byte) (buffer[i][j]); } } catch (Exception e) { throw_put("XZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putYZ(int x, int y, int z, double[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) for (int i = 0; i < leni; i++, offset += nx) { ((byte[]) data[z])[offset] = (byte) (buffer[i][j]); } } catch (Exception e) { throw_put("XZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 3D array to put into the imageware */ public void putXYZ(int x, int y, int z, byte[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (byte) (buffer[i][j][k] & 0xFF); } } } } catch (Exception e) { throw_put("XYZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 3D array to put into the imageware */ public void putXYZ(int x, int y, int z, short[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (byte) (buffer[i][j][k] & 0xFFFF); } } } } catch (Exception e) { throw_put("XYZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 3D array to put into the imageware */ public void putXYZ(int x, int y, int z, float[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (byte) (buffer[i][j][k]); } } } } catch (Exception e) { throw_put("XYZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 3D array to put into the imageware */ public void putXYZ(int x, int y, int z, double[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (byte) (buffer[i][j][k]); } } } } catch (Exception e) { throw_put("XYZ", "No check", buffer, x, y, z); } } // ------------------------------------------------------------------ // // get Section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the imageware */ public void getX(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); double buf[] = new double[bnx]; getX(x, y, z, buf); buffer.putX(0, 0, 0, buf); } /** * Get an array from the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the imageware */ public void getY(int x, int y, int z, ImageWare buffer) { int bny = buffer.getSizeY(); double buf[] = new double[bny]; getY(x, y, z, buf); buffer.putY(0, 0, 0, buf); } /** * Get an array from the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the imageware */ public void getZ(int x, int y, int z, ImageWare buffer) { int bnz = buffer.getSizeZ(); double buf[] = new double[bnz]; getZ(x, y, z, buf); buffer.putZ(0, 0, 0, buf); } /** * get an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the imageware */ public void getXY(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); int bny = buffer.getSizeY(); double buf[][] = new double[bnx][bny]; getXY(x, y, z, buf); buffer.putXY(0, 0, 0, buf); } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the imageware */ public void getXZ(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); int bnz = buffer.getSizeZ(); double buf[][] = new double[bnx][bnz]; getXZ(x, y, z, buf); buffer.putXZ(0, 0, 0, buf); } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the datase */ public void getYZ(int x, int y, int z, ImageWare buffer) { int bny = buffer.getSizeY(); int bnz = buffer.getSizeZ(); double buf[][] = new double[bny][bnz]; getYZ(x, y, z, buf); buffer.putYZ(0, 0, 0, buf); } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the imageware */ public void getXYZ(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); int bny = buffer.getSizeY(); int bnz = buffer.getSizeZ(); double buf[][][] = new double[bnx][bny][bnz]; getXYZ(x, y, z, buf); buffer.putXYZ(0, 0, 0, buf); } /** * Get an array from the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware */ public void getX(int x, int y, int z, byte[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; System.arraycopy(tmp, offset, buffer, 0, leni); } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware */ public void getX(int x, int y, int z, short[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (short) (tmp[offset] & 0xFF); offset++; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware */ public void getX(int x, int y, int z, float[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (float) (tmp[offset] & 0xFF); offset++; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware */ public void getX(int x, int y, int z, double[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (double) (tmp[offset] & 0xFF); offset++; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware */ public void getY(int x, int y, int z, byte[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (byte) (tmp[offset] & 0xFF); offset += nx; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware */ public void getY(int x, int y, int z, short[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (short) (tmp[offset] & 0xFF); offset += nx; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware */ public void getY(int x, int y, int z, float[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (float) (tmp[offset] & 0xFF); offset += nx; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware */ public void getY(int x, int y, int z, double[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; byte[] tmp = (byte[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (double) (tmp[offset] & 0xFF); offset += nx; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware */ public void getZ(int x, int y, int z, byte[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { buffer[i] = (byte) (((byte[]) data[z])[offset] & 0xFF); z++; } } catch (Exception e) { throw_get("Y", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware */ public void getZ(int x, int y, int z, short[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { buffer[i] = (short) (((byte[]) data[z])[offset] & 0xFF); z++; } } catch (Exception e) { throw_get("Y", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware */ public void getZ(int x, int y, int z, float[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { buffer[i] = (float) (((byte[]) data[z])[offset] & 0xFF); z++; } } catch (Exception e) { throw_get("Y", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware */ public void getZ(int x, int y, int z, double[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { buffer[i] = (double) (((byte[]) data[z])[offset] & 0xFF); z++; } } catch (Exception e) { throw_get("Y", "No check", buffer, x, y, z); } } /** * get an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware */ public void getXY(int x, int y, int z, byte[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (byte) (tmp[offset] & 0xFF); } } } catch (Exception e) { throw_get("XY", "No check", buffer, x, y, z); } } /** * get an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware */ public void getXY(int x, int y, int z, short[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (short) (tmp[offset] & 0xFF); } } } catch (Exception e) { throw_get("XY", "No check", buffer, x, y, z); } } /** * get an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware */ public void getXY(int x, int y, int z, float[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (float) (tmp[offset] & 0xFF); } } } catch (Exception e) { throw_get("XY", "No check", buffer, x, y, z); } } /** * get an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware */ public void getXY(int x, int y, int z, double[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (double) (tmp[offset] & 0xFF); } } } catch (Exception e) { throw_get("XY", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware */ public void getXZ(int x, int y, int z, byte[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + y * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (byte) (((byte[]) data[z])[offset] & 0xFF); } } } catch (Exception e) { throw_get("XZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware */ public void getXZ(int x, int y, int z, short[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + y * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (short) (((byte[]) data[z])[offset] & 0xFF); } } } catch (Exception e) { throw_get("XZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware */ public void getXZ(int x, int y, int z, float[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + y * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (float) (((byte[]) data[z])[offset] & 0xFF); } } } catch (Exception e) { throw_get("XZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware */ public void getXZ(int x, int y, int z, double[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + y * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (double) (((byte[]) data[z])[offset] & 0xFF); } } } catch (Exception e) { throw_get("XZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the datase */ public void getYZ(int x, int y, int z, byte[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { for (int i = 0; i < leni; i++, offset += nx) { buffer[i][j] = (byte) (((byte[]) data[z])[offset] & 0xFF); } } } catch (Exception e) { throw_get("YZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the datase */ public void getYZ(int x, int y, int z, short[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { for (int i = 0; i < leni; i++, offset += nx) { buffer[i][j] = (short) (((byte[]) data[z])[offset] & 0xFF); } } } catch (Exception e) { throw_get("YZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the datase */ public void getYZ(int x, int y, int z, float[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { for (int i = 0; i < leni; i++, offset += nx) { buffer[i][j] = (float) (((byte[]) data[z])[offset] & 0xFF); } } } catch (Exception e) { throw_get("YZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the datase */ public void getYZ(int x, int y, int z, double[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { for (int i = 0; i < leni; i++, offset += nx) { buffer[i][j] = (double) (((byte[]) data[z])[offset] & 0xFF); } } } catch (Exception e) { throw_get("YZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 3D array to get into the imageware */ public void getXYZ(int x, int y, int z, byte[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j][k] = (byte) (tmp[offset] & 0xFF); } } } } catch (Exception e) { throw_get("XYZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 3D array to get into the imageware */ public void getXYZ(int x, int y, int z, short[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j][k] = (short) (tmp[offset] & 0xFF); } } } } catch (Exception e) { throw_get("XYZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 3D array to get into the imageware */ public void getXYZ(int x, int y, int z, float[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j][k] = (float) (tmp[offset] & 0xFF); } } } } catch (Exception e) { throw_get("XYZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 3D array to get into the imageware */ public void getXYZ(int x, int y, int z, double[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { byte[] tmp = (byte[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j][k] = (double) (tmp[offset] & 0xFF); } } } } catch (Exception e) { throw_get("XYZ", "No check", buffer, x, y, z); } } // ------------------------------------------------------------------ // // Private Section // // ------------------------------------------------------------------ /** * Prepare a complete error message from the errors coming the constructors. */ protected void throw_constructor() { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a byte imageware.\n" + "-------------------------------------------------------\n"); } /** * Prepare a complete error message from the errors coming the constructors. */ protected void throw_constructor(int nx, int ny, int nz) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a byte imageware " + nx + "," + ny + "," + nz + "].\n" + "-------------------------------------------------------\n"); } /** * Prepare a complete error message from the errors coming the get routines. */ protected void throw_get(String direction, String border, Object buffer, int x, int y, int z) { int leni = 0; int lenj = 0; int lenk = 0; String type = " unknown type"; if (buffer instanceof byte[]) { leni = ((byte[]) buffer).length; type = " 1D byte"; } else if (buffer instanceof short[]) { leni = ((short[]) buffer).length; type = " 1D short"; } else if (buffer instanceof float[]) { leni = ((float[]) buffer).length; type = " 1D float"; } else if (buffer instanceof double[]) { leni = ((double[]) buffer).length; type = " 1D double"; } else if (buffer instanceof byte[][]) { leni = ((byte[][]) buffer).length; lenj = ((byte[][]) buffer)[0].length; type = " 2D byte"; } else if (buffer instanceof short[][]) { leni = ((short[][]) buffer).length; lenj = ((short[][]) buffer)[0].length; type = " 2D short"; } else if (buffer instanceof float[][]) { leni = ((float[][]) buffer).length; lenj = ((float[][]) buffer)[0].length; type = " 2D float"; } else if (buffer instanceof double[][]) { leni = ((double[][]) buffer).length; lenj = ((double[][]) buffer)[0].length; type = " 2D double"; } else if (buffer instanceof byte[][][]) { leni = ((byte[][][]) buffer).length; lenj = ((byte[][][]) buffer)[0].length; lenk = ((byte[][][]) buffer)[0][0].length; type = " 3D byte"; } else if (buffer instanceof short[][][]) { leni = ((short[][][]) buffer).length; lenj = ((short[][][]) buffer)[0].length; lenk = ((short[][][]) buffer)[0][0].length; type = " 3D short"; } else if (buffer instanceof float[][][]) { leni = ((float[][][]) buffer).length; lenj = ((float[][][]) buffer)[0].length; lenk = ((float[][][]) buffer)[0][0].length; type = " 3D float"; } else if (buffer instanceof double[][][]) { leni = ((double[][][]) buffer).length; lenj = ((double[][][]) buffer)[0].length; lenk = ((double[][][]) buffer)[0][0].length; type = " 3D double"; } throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get a" + type + " buffer [" + (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "from the byte imageware [" + nx + "," + ny + "," + nz + "]\n" + "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + "-------------------------------------------------------\n"); } /** * Prepare a complete error message from the errors coming the put routines. */ protected void throw_put(String direction, String border, Object buffer, int x, int y, int z) { int leni = 0; int lenj = 0; int lenk = 0; String type = " unknown type"; if (buffer instanceof byte[]) { leni = ((byte[]) buffer).length; type = " 1D byte"; } else if (buffer instanceof short[]) { leni = ((short[]) buffer).length; type = " 1D short"; } else if (buffer instanceof float[]) { leni = ((float[]) buffer).length; type = " 1D float"; } else if (buffer instanceof double[]) { leni = ((double[]) buffer).length; type = " 1D double"; } else if (buffer instanceof byte[][]) { leni = ((byte[][]) buffer).length; lenj = ((byte[][]) buffer)[0].length; type = " 2D byte"; } else if (buffer instanceof short[][]) { leni = ((short[][]) buffer).length; lenj = ((short[][]) buffer)[0].length; type = " 2D short"; } else if (buffer instanceof float[][]) { leni = ((float[][]) buffer).length; lenj = ((float[][]) buffer)[0].length; type = " 2D float"; } else if (buffer instanceof double[][]) { leni = ((double[][]) buffer).length; lenj = ((double[][]) buffer)[0].length; type = " 2D double"; } else if (buffer instanceof byte[][][]) { leni = ((byte[][][]) buffer).length; lenj = ((byte[][][]) buffer)[0].length; lenk = ((byte[][][]) buffer)[0][0].length; type = " 3D byte"; } else if (buffer instanceof short[][][]) { leni = ((short[][][]) buffer).length; lenj = ((short[][][]) buffer)[0].length; lenk = ((short[][][]) buffer)[0][0].length; type = " 3D short"; } else if (buffer instanceof float[][][]) { leni = ((float[][][]) buffer).length; lenj = ((float[][][]) buffer)[0].length; lenk = ((float[][][]) buffer)[0][0].length; type = " 3D float"; } else if (buffer instanceof double[][][]) { leni = ((double[][][]) buffer).length; lenj = ((double[][][]) buffer)[0].length; lenk = ((double[][][]) buffer)[0][0].length; type = " 3D double"; } throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a" + type + " buffer [" + (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "into the byte imageware [" + nx + "," + ny + "," + nz + "]\n" + "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + "-------------------------------------------------------\n"); } // ------------------------------------------------------------------ // // Get slice fast and direct access Section // // ------------------------------------------------------------------ /** * Get a reference of the whole volume data. * * @return a reference of the data of this imageware */ public Object[] getVolume() { return data; } /** * Get a specific slice, fast and direct access, but only for byte * imageware. * * @param z * number of the requested slice * @return a reference of the data of one slice of this imageware */ public byte[] getSliceByte(int z) { return (byte[]) data[z]; } /** * Get a specific slice, fast and direct access, but only for short * imageware. * * @param z * number of the requested slice * @return a reference of the data of one slice of this imageware */ public short[] getSliceShort(int z) { return null; } /** * Get a specific slice, fast and direct access, but only for float * imageware. * * @param z * number of the requested slice * @return a reference of the data of one slice of this imageware */ public float[] getSliceFloat(int z) { return null; } /** * Get a specific slice, fast and direct access, but only for double * imageware. * * @param z * number of the requested slice * @return a reference of the data of one slice of this imageware */ public double[] getSliceDouble(int z) { return null; } /** * Allocate a buffer of size [nx,ny,nz]. */ private void allocate() { try { this.data = new Object[nz]; this.nxy = nx * ny; for (int z = 0; z < nz; z++) this.data[z] = new byte[nxy]; } catch (Exception e) { throw_constructor(nx, ny, nz); } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/BytePointwise.java b/src/bilib/src/imageware/BytePointwise.java new file mode 100644 index 0000000..2b75f04 --- /dev/null +++ b/src/bilib/src/imageware/BytePointwise.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import ij.process.ByteProcessor; import java.awt.Image; import java.util.Random; /** * Class BytePointwise. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class BytePointwise extends ByteAccess implements Pointwise { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected BytePointwise(int nx, int ny, int nz) { super(nx, ny, nz); } protected BytePointwise(Image image, int mode) { super(image, mode); } protected BytePointwise(ImageStack stack, int mode) { super(stack, mode); } protected BytePointwise(ImageStack stack, byte chan) { super(stack, chan); } protected BytePointwise(byte[] array, int mode) { super(array, mode); } protected BytePointwise(byte[][] array, int mode) { super(array, mode); } protected BytePointwise(byte[][][] array, int mode) { super(array, mode); } protected BytePointwise(short[] array, int mode) { super(array, mode); } protected BytePointwise(short[][] array, int mode) { super(array, mode); } protected BytePointwise(short[][][] array, int mode) { super(array, mode); } protected BytePointwise(float[] array, int mode) { super(array, mode); } protected BytePointwise(float[][] array, int mode) { super(array, mode); } protected BytePointwise(float[][][] array, int mode) { super(array, mode); } protected BytePointwise(double[] array, int mode) { super(array, mode); } protected BytePointwise(double[][] array, int mode) { super(array, mode); } protected BytePointwise(double[][][] array, int mode) { super(array, mode); } /** * Fill this imageware with a constant value. * * @param value * the constant value */ public void fillConstant(double value) { byte typedValue = (byte) value; byte[] slice = null; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) slice[k] = typedValue; } } /** * Fill this imageware with ramp. */ public void fillRamp() { int off = 0; byte[] slice = null; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) slice[k] = (byte) (off + k); off += nxy; } } /** * Generate a gaussian noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void fillGaussianNoise(double amplitude) { Random rnd = new Random(); byte[] slice = null; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (byte) ((rnd.nextGaussian()) * amplitude); } } } /** * Generate a uniform noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void fillUniformNoise(double amplitude) { Random rnd = new Random(); byte[] slice = null; amplitude *= 2.0; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (byte) ((rnd.nextDouble() - 0.5) * amplitude); } } } /** * Generate a salt and pepper noise. * * @param amplitudeSalt * amplitude of the salt noise * @param amplitudePepper * amplitude of the pepper noise * @param percentageSalt * percentage of the salt noise * @param percentagePepper * percentage of the pepper noise */ public void fillSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) { Random rnd = new Random(); int index, z; if (percentageSalt > 0) { double nbSalt = nxy * nz / percentageSalt; for (int k = 0; k < nbSalt; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((byte[]) data[z])[index] = (byte) (rnd.nextDouble() * amplitudeSalt); } } if (percentagePepper > 0) { double nbPepper = nxy * nz / percentagePepper; for (int k = 0; k < nbPepper; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((byte[]) data[z])[index] = (byte) (-rnd.nextDouble() * amplitudeSalt); } } } /** * Build an ImageStack of ImageJ. */ public ImageStack buildImageStack() { ImageStack imagestack = new ImageStack(nx, ny); for (int z = 0; z < nz; z++) { ByteProcessor ip = new ByteProcessor(nx, ny); byte pix[] = (byte[]) ip.getPixels(); for (int k = 0; k < nxy; k++) pix[k] = (byte) (((byte[]) data[z])[k]); imagestack.addSlice("" + z, ip); } return imagestack; } /** * Invert the pixel intensity. */ public void invert() { double max = -Double.MAX_VALUE; byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k] & 0xFF) > max) max = slice[k] & 0xFF; } } for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (byte) (max - ((double) (slice[k] & 0xFF))); } } } /** * Negate the pixel intensity. */ public void negate() { byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (byte) (-((double) (slice[k] & 0xFF))); } } } /** * Clip the pixel intensity into [0..255]. */ public void clip() { clip(0.0, 255.0); } /** * Clip the pixel intensity into [minLevel..maxLevel]. * * @param minLevel * double value given the threshold * @param maxLevel * double value given the threshold */ public void clip(double minLevel, double maxLevel) { byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; byte value; byte min = (byte) minLevel; byte max = (byte) maxLevel; for (int k = 0; k < nxy; k++) { value = (byte) (slice[k] & 0xFF); if (value < min) slice[k] = min; if (value > max) slice[k] = max; } } } /** * Rescale the pixel intensity into [0..255]. */ public void rescale() { double maxImage = -Double.MAX_VALUE; double minImage = Double.MAX_VALUE; byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k] & 0xFF) > maxImage) maxImage = slice[k] & 0xFF; if ((slice[k] & 0xFF) < minImage) minImage = slice[k] & 0xFF; } } double a; if (minImage - maxImage == 0) { a = 1.0; minImage = 128.0; } else { a = 255.0 / (maxImage - minImage); } for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (byte) (a * (((double) (slice[k] & 0xFF)) - minImage)); } } } /** * Rescale the pixel intensity into [minLevel..maxLevel]. * * @param minLevel * double value given the threshold * @param maxLevel * double value given the threshold */ public void rescale(double minLevel, double maxLevel) { double maxImage = -Double.MAX_VALUE; double minImage = Double.MAX_VALUE; byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k] & 0xFF) > maxImage) maxImage = slice[k] & 0xFF; if ((slice[k] & 0xFF) < minImage) minImage = slice[k] & 0xFF; } } double a; if (minImage - maxImage == 0) { a = 1.0; minImage = (maxLevel - minLevel) / 2.0; } else { a = (maxLevel - minLevel) / (maxImage - minImage); } for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (byte) (a * (((double) (slice[k] & 0xFF)) - minImage) + minLevel); } } } /** * Rescale the pixel intensity with a linear curve passing through * (maxLevel-minLevel)/2 at the 0 input intensity. * * @param minLevel * double value given the threshold * @param maxLevel * double value given the threshold */ public void rescaleCenter(double minLevel, double maxLevel) { double maxImage = -Double.MAX_VALUE; double minImage = Double.MAX_VALUE; byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k] & 0xFF) > maxImage) maxImage = slice[k] & 0xFF; if ((slice[k] & 0xFF) < minImage) minImage = slice[k] & 0xFF; } } double center = (maxLevel + minLevel) / 2.0; double a; if (minImage - maxImage == 0) { a = 1.0; minImage = (maxLevel - minLevel) / 2.0; } else { if (Math.abs(maxImage) > Math.abs(minImage)) a = (maxLevel - center) / Math.abs(maxImage); else a = (center - minLevel) / Math.abs(minImage); } for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (byte) (a * (((double) (slice[k] & 0xFF)) - minImage) + center); } } } /** * Compute the absolute value of this imageware. */ public void abs() { } /** * Compute the log of this imageware. */ public void log() { byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (byte) Math.log(slice[k]); } } } /** * Compute the exponential of this imageware. */ public void exp() { byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (byte) Math.exp(slice[k]); } } } /** * Compute the square root of this imageware. */ public void sqrt() { byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (byte) Math.sqrt(slice[k]); } } } /** * Compute the square of this imageware. */ public void sqr() { byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] *= slice[k]; } } } /** * Compute the power of a of this imageware. * * @param a * exponent */ public void pow(double a) { byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (byte) Math.pow(slice[k], a); } } } /** * Add a constant value to this imageware. */ public void add(double constant) { byte cst = (byte) constant; byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] += cst; } } } /** * Multiply a constant value to this imageware. * * @param constant * the constant value */ public void multiply(double constant) { byte cst = (byte) constant; byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] *= cst; } } } /** * Subtract a constant value to this imageware. * * @param constant * the constant value */ public void subtract(double constant) { byte cst = (byte) constant; byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] -= cst; } } } /** * Divide by a constant value to this imageware. * * @param constant * the constant value */ public void divide(double constant) { if (constant == 0.0) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to divide because the constant is 0.\n" + "-------------------------------------------------------\n"); byte cst = (byte) constant; byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] /= cst; } } } /** * Threshold a imageware in two levels 0 and 255. * * All the pixels values strictly greater than 'thresholdValue' and are set * to 0. The remaining values are set to 255. * * @param thresholdValue * double value given the threshold */ public void threshold(double thresholdValue) { threshold(thresholdValue, 0.0, 255.0); } /** * Threshold a imageware in two levels minLevel and maxLevel. * * All the pixels values strictly greater than 'thresholdValue' and are set * to maxLevel. The remaining values are set to minLevel. * * @param thresholdValue * double value given the threshold * @param minLevel * double value given the minimum level * @param maxLevel * double value given the maximum level */ public void threshold(double thresholdValue, double minLevel, double maxLevel) { byte low = (byte) (minLevel); byte high = (byte) (maxLevel); byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = ((double) (slice[k] & 0xFF) > thresholdValue ? high : low); } } } /** * Apply a soft thresholding. * * All the pixels values strictly greater than '-thresholdValue' and stricty * lower than 'thresholdValue' set to 0. The remaining positive values are * reduced by 'thresholdvalue'; the remaining negative values are augmented * by 'thresholdValue'. * * @param thresholdValue * double value given the threshold */ public void thresholdSoft(double thresholdValue) { byte zero = (byte) (0.0); double pixel; byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { pixel = (double) (slice[k] & 0xFF); slice[k] = (pixel <= -thresholdValue ? (byte) (pixel + thresholdValue) : (pixel > thresholdValue ? (byte) (pixel - thresholdValue) : zero)); } } } /** * Apply a hard thresholding. * * All the pixels values strictly greater than '-thresholdValue' and stricty * lower than 'thresholdValue' are set to 0. The remaining values are * unchanged. * * @param thresholdValue * double value given the threshold */ public void thresholdHard(double thresholdValue) { byte zero = (byte) (0.0); double pixel; byte[] slice; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { pixel = (double) (slice[k] & 0xFF); if (pixel > -thresholdValue && pixel < thresholdValue) slice[k] = zero; } } } /** * Add a gaussian noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void addGaussianNoise(double amplitude) { Random rnd = new Random(); byte[] slice = null; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] += (byte) ((rnd.nextGaussian()) * amplitude); } } } /** * Add a uniform noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void addUniformNoise(double amplitude) { Random rnd = new Random(); byte[] slice = null; amplitude *= 2.0; for (int z = 0; z < nz; z++) { slice = (byte[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] += (byte) ((rnd.nextDouble() - 0.5) * amplitude); } } } /** * Add a salt and pepper noise. * * @param amplitudeSalt * amplitude of the salt noise * @param amplitudePepper * amplitude of the pepper noise * @param percentageSalt * percentage of the salt noise * @param percentagePepper * percentage of the pepper noise */ public void addSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) { Random rnd = new Random(); int index, z; if (percentageSalt > 0) { double nbSalt = nxy * nz / percentageSalt; for (int k = 0; k < nbSalt; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((byte[]) data[z])[index] += (byte) (rnd.nextDouble() * amplitudeSalt); } } if (percentagePepper > 0) { double nbPepper = nxy * nz / percentagePepper; for (int k = 0; k < nbPepper; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((byte[]) data[z])[index] -= (byte) (rnd.nextDouble() * amplitudeSalt); } } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/ByteProcess.java b/src/bilib/src/imageware/ByteProcess.java new file mode 100644 index 0000000..8156fba --- /dev/null +++ b/src/bilib/src/imageware/ByteProcess.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import java.awt.Image; /** * Class ByteProcess. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class ByteProcess extends BytePointwise implements Process { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected ByteProcess(int nx, int ny, int nz) { super(nx, ny, nz); } protected ByteProcess(Image image, int mode) { super(image, mode); } protected ByteProcess(ImageStack stack, int mode) { super(stack, mode); } protected ByteProcess(ImageStack stack, byte chan) { super(stack, chan); } protected ByteProcess(byte[] array, int mode) { super(array, mode); } protected ByteProcess(byte[][] array, int mode) { super(array, mode); } protected ByteProcess(byte[][][] array, int mode) { super(array, mode); } protected ByteProcess(short[] array, int mode) { super(array, mode); } protected ByteProcess(short[][] array, int mode) { super(array, mode); } protected ByteProcess(short[][][] array, int mode) { super(array, mode); } protected ByteProcess(float[] array, int mode) { super(array, mode); } protected ByteProcess(float[][] array, int mode) { super(array, mode); } protected ByteProcess(float[][][] array, int mode) { super(array, mode); } protected ByteProcess(double[] array, int mode) { super(array, mode); } protected ByteProcess(double[][] array, int mode) { super(array, mode); } protected ByteProcess(double[][][] array, int mode) { super(array, mode); } /** * Apply a separable gaussian smoothing over the image with the same * strengthness in all directions. To have a smmothing effect the * strengthness should be strictly greater than 0 and the size in the * considered directions should be greater strictly than 1. * * @param sigma * Strengthness of the smoothing */ public void smoothGaussian(double sigma) { smoothGaussian(sigma, sigma, sigma); } /** * Apply a separablegaussian smoothing over the image with an independant * strengthness in the different directions. To have a smmothing effect the * strengthness should be strictly greater than 0 and the size in the * considered directions should be greater strictly than 1. * * @param sigmaX * Strengthness of the smoothing in X axis * @param sigmaY * Strengthness of the smoothing in X axis * @param sigmaZ * Strengthness of the smoothing in X axis */ public void smoothGaussian(double sigmaX, double sigmaY, double sigmaZ) { int n = 3; double N = (double) n; double poles[] = new double[n]; if (nx > 1 && sigmaX > 0.0) { double s2 = sigmaX * sigmaX; double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2); poles[0] = poles[1] = poles[2] = alpha; double line[] = new double[nx]; for (int z = 0; z < nz; z++) { for (int y = 0; y < ny; y++) { getX(0, y, z, line); putX(0, y, z, Convolver.convolveIIR(line, poles)); } } } if (ny > 1 && sigmaY > 0.0) { double s2 = sigmaY * sigmaY; double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2); poles[0] = poles[1] = poles[2] = alpha; double line[] = new double[ny]; for (int x = 0; x < nx; x++) { for (int z = 0; z < nz; z++) { getY(x, 0, z, line); putY(x, 0, z, Convolver.convolveIIR(line, poles)); } } } if (nz > 1 && sigmaZ > 0.0) { double s2 = sigmaZ * sigmaZ; double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2); poles[0] = poles[1] = poles[2] = alpha; double line[] = new double[nz]; for (int y = 0; y < ny; y++) { for (int x = 0; x < nx; x++) { getZ(x, y, 0, line); putZ(x, y, 0, Convolver.convolveIIR(line, poles)); } } } } /** * Get the maximum of this imageware and a imageware. * * @param imageware * imageware to max */ public void max(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get the maximum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { if (((byte[]) data[z])[k] < (byte) tmp[k]) ((byte[]) data[z])[k] = (byte) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { if (((byte[]) data[z])[k] < (byte) tmp[k]) ((byte[]) data[z])[k] = (byte) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { if (((byte[]) data[z])[k] < (byte) tmp[k]) ((byte[]) data[z])[k] = (byte) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { if (((byte[]) data[z])[k] < (byte) tmp[k]) ((byte[]) data[z])[k] = (byte) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Get the minimum of this imageware and a imageware. * * @param imageware * imageware to min */ public void min(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get the minimum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { if (((byte[]) data[z])[k] > (byte) tmp[k]) ((byte[]) data[z])[k] = (byte) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { if (((byte[]) data[z])[k] > (byte) tmp[k]) ((byte[]) data[z])[k] = (byte) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { if (((byte[]) data[z])[k] > (byte) tmp[k]) ((byte[]) data[z])[k] = (byte) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { if (((byte[]) data[z])[k] > (byte) tmp[k]) ((byte[]) data[z])[k] = (byte) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Add a imageware to the current imageware. * * @param imageware * imageware to add */ public void add(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to add because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] += (byte) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] += (byte) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] += (byte) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] += (byte) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Multiply a imageware to the current imageware. * * @param imageware * imageware to multiply */ public void multiply(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to multiply because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] *= (byte) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] *= (byte) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] *= (byte) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] *= (byte) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Subtract a imageware to the current imageware. * * @param imageware * imageware to subtract */ public void subtract(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to subtract because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] -= (byte) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] -= (byte) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] -= (byte) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] -= (byte) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Divide a imageware to the current imageware. * * @param imageware * imageware to divide */ public void divide(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to divide because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] /= (byte) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] /= (byte) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] /= (byte) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((byte[]) data[z])[k] /= (byte) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/ByteSet.java b/src/bilib/src/imageware/ByteSet.java new file mode 100644 index 0000000..d354ff4 --- /dev/null +++ b/src/bilib/src/imageware/ByteSet.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import java.awt.Image; /** * Class ByteSet. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class ByteSet extends ByteProcess implements ImageWare { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected ByteSet(int nx, int ny, int nz) { super(nx, ny, nz); } protected ByteSet(Image image, int mode) { super(image, mode); } protected ByteSet(ImageStack stack, int mode) { super(stack, mode); } protected ByteSet(ImageStack stack, byte chan) { super(stack, chan); } protected ByteSet(byte[] array, int mode) { super(array, mode); } protected ByteSet(byte[][] array, int mode) { super(array, mode); } protected ByteSet(byte[][][] array, int mode) { super(array, mode); } protected ByteSet(short[] array, int mode) { super(array, mode); } protected ByteSet(short[][] array, int mode) { super(array, mode); } protected ByteSet(short[][][] array, int mode) { super(array, mode); } protected ByteSet(float[] array, int mode) { super(array, mode); } protected ByteSet(float[][] array, int mode) { super(array, mode); } protected ByteSet(float[][][] array, int mode) { super(array, mode); } protected ByteSet(double[] array, int mode) { super(array, mode); } protected ByteSet(double[][] array, int mode) { super(array, mode); } protected ByteSet(double[][][] array, int mode) { super(array, mode); } /** * Duplicate the imageware. * * Create a new imageware with the same size, same type and same data than * the calling one. * * @return a duplicated version of this imageware */ public ImageWare duplicate() { ImageWare out = new ByteSet(nx, ny, nz); byte[] outdata; for (int z = 0; z < nz; z++) { outdata = (byte[]) (((ByteSet) out).data[z]); System.arraycopy(data[z], 0, outdata, 0, nxy); } return out; } /** * Replicate the imageware. * * Create a new imageware with the same size, same type than the calling * one. The data are not copied. * * @return a replicated version of this imageware */ public ImageWare replicate() { return new ByteSet(nx, ny, nz); } /** * Replicate the imageware. * * Create a new imageware with the same size and a specified type than the * calling one. The data are not copied. * * @param type * requested type * @return a replicated version of this imageware */ public ImageWare replicate(int type) { switch (type) { case ImageWare.BYTE: return new ByteSet(nx, ny, nz); case ImageWare.SHORT: return new ShortSet(nx, ny, nz); case ImageWare.FLOAT: return new FloatSet(nx, ny, nz); case ImageWare.DOUBLE: return new DoubleSet(nx, ny, nz); default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n" + "-------------------------------------------------------\n"); } } /** * Copy all the data of source in the current imageware. The source should * have the same size and same type than the calling one. * * @param source * a source imageware */ public void copy(ImageWare source) { if (nx != source.getSizeX()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size (" + nx + " != " + source.getSizeX() + ").\n" + "-------------------------------------------------------\n"); if (ny != source.getSizeY()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size (" + ny + " != " + source.getSizeY() + ").\n" + "-------------------------------------------------------\n"); if (nz != source.getSizeZ()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size (" + nz + " != " + source.getSizeZ() + ").\n" + "-------------------------------------------------------\n"); if (getType() != source.getType()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same type (" + getType() + " != " + source.getType() + ").\n" + "-------------------------------------------------------\n"); byte[] src; for (int z = 0; z < nz; z++) { src = (byte[]) (((ByteSet) source).data[z]); System.arraycopy(src, 0, data[z], 0, nxy); } } /** * convert the imageware in a specified type. * * Create a new imageware with the same size and converted data than the * calling one. * * @param type * indicates the type of the output * @return a converted version of this imageware */ public ImageWare convert(int type) { if (type == ImageWare.BYTE) return duplicate(); ImageWare out = null; switch (type) { case ImageWare.BYTE: { byte[] slice; out = new ByteSet(nx, ny, nz); byte[] outslice; for (int z = 0; z < nz; z++) { slice = ((byte[]) data[z]); outslice = ((byte[]) ((ByteSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (byte) (slice[k] & 0xFF); } } } break; case ImageWare.SHORT: { byte[] slice; out = new ShortSet(nx, ny, nz); short[] outslice; for (int z = 0; z < nz; z++) { slice = ((byte[]) data[z]); outslice = ((short[]) ((ShortSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (short) (slice[k] & 0xFF); } } } break; case ImageWare.FLOAT: { byte[] slice; out = new FloatSet(nx, ny, nz); float[] outslice; for (int z = 0; z < nz; z++) { slice = ((byte[]) data[z]); outslice = ((float[]) ((FloatSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (float) (slice[k] & 0xFF); } } } break; case ImageWare.DOUBLE: { byte[] slice; out = new DoubleSet(nx, ny, nz); double[] outslice; for (int z = 0; z < nz; z++) { slice = ((byte[]) data[z]); outslice = ((double[]) ((DoubleSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (double) (slice[k] & 0xFF); } } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n" + "-------------------------------------------------------\n"); } return out; } /** * Print information of this ImageWare object. */ public void printInfo() { System.out.println("ImageWare object information"); System.out.println("Dimension: " + getDimension()); System.out.println("Size: [" + nx + ", " + ny + ", " + nz + "]"); System.out.println("TotalSize: " + getTotalSize()); System.out.println("Type: " + getTypeToString()); System.out.println("Maximun: " + getMaximum()); System.out.println("Minimun: " + getMinimum()); System.out.println("Mean: " + getMean()); System.out.println("Norm1: " + getNorm1()); System.out.println("Norm2: " + getNorm2()); System.out.println("Total: " + getTotal()); System.out.println(""); } /** * Show this ImageWare object. */ public void show() { String title = getTypeToString(); switch (getDimension()) { case 1: title += " line"; break; case 2: title += " image"; break; case 3: title += " volume"; break; } Display.show(title, this); // ImagePlus imp = new ImagePlus(title, buildImageStack()); // imp.show(); } /** * Show the data in ImagePlus object with a specify title. * * @param title * a string given the title of the window */ public void show(String title) { Display.show(title, this); // ImagePlus imp = new ImagePlus(title, buildImageStack()); // imp.show(); } /** * Return the minimum value of this imageware. * * @return the min value of this imageware */ public double getMinimum() { double min = Double.MAX_VALUE; byte[] slice; for (int z = 0; z < nz; z++) { slice = ((byte[]) data[z]); for (int k = 0; k < nxy; k++) if ((slice[k] & 0xFF) < min) min = slice[k] & 0xFF; } return min; } /** * Return the maximum value of this imageware. * * @return the max value of this imageware */ public double getMaximum() { double max = -Double.MAX_VALUE; byte[] slice; for (int z = 0; z < nz; z++) { slice = ((byte[]) data[z]); for (int k = 0; k < nxy; k++) if ((slice[k] & 0xFF) > max) max = slice[k] & 0xFF; } return max; } /** * Return the mean value of this imageware. * * @return the mean value of this imageware */ public double getMean() { return getTotal() / (nz * nxy); } /** * Return the norm value of order 1. * * @return the norm value of this imageware in L1 sense */ public double getNorm1() { double norm = 0.0; double value = 0; byte[] slice; for (int z = 0; z < nz; z++) { slice = ((byte[]) data[z]); for (int k = 0; k < nxy; k++) { value = (double) (slice[k] & 0xFF); norm += (value > 0.0 ? value : -value); } } return norm; } /** * Return the norm value of order 2. * * @return the norm value of this imageware in L2 sense */ public double getNorm2() { double norm = 0.0; byte[] slice; for (int z = 0; z < nz; z++) { slice = ((byte[]) data[z]); for (int k = 0; k < nxy; k++) norm += (slice[k] & 0xFF) * (slice[k] & 0xFF); } return norm; } /** * Return the sum of all pixel in this imageware. * * @return the total sum of all pixel in this imageware */ public double getTotal() { double total = 0.0; byte[] slice; for (int z = 0; z < nz; z++) { slice = ((byte[]) data[z]); for (int k = 0; k < nxy; k++) total += slice[k] & 0xFF; } return total; } /** * Return the the minumum [0] and the maximum [1] value of this imageware. * Faster routine than call one getMinimum() and then one getMaximum(). * * @return an array of two values, the min and the max values of the images */ public double[] getMinMax() { double max = -Double.MAX_VALUE; double min = Double.MAX_VALUE; byte[] slice; for (int z = 0; z < nz; z++) { slice = ((byte[]) data[z]); for (int k = 0; k < nxy; k++) { if ((slice[k] & 0xFF) > max) max = slice[k] & 0xFF; if ((slice[k] & 0xFF) < min) min = slice[k] & 0xFF; } } double minmax[] = { min, max }; return minmax; } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/Convolver.java b/src/bilib/src/imageware/Convolver.java new file mode 100644 index 0000000..ffeffee --- /dev/null +++ b/src/bilib/src/imageware/Convolver.java @@ -0,0 +1 @@ +package imageware; /** * Class Convolver. Routines to convolve a 1D signal applying mirror boundary * conditions. * * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class Convolver extends Object { private static double tolerance = 10e-6; /** * Convolution with a Finite Impulse Response (FIR) filter. * * Note: Only with the periodic boundary conditions. * * @param input * 1D input signal * @param kernel * kernel of the filter */ public static double[] convolveFIR(double[] input, double[] kernel) { int l = input.length; if (l <= 1) throw new IllegalArgumentException("convolveFIR: input signal too short"); double[] output = new double[l]; int indexq = kernel.length - 1; int indexp = 0; int n2 = 2 * (l - 1); int origin = kernel.length / 2; int m = 1 + origin - kernel.length; m -= (m < 0L) ? (n2 * ((m + 1 - n2) / n2)) : (n2 * (m / n2)); int k; for (int i = 0; i < l; i++) { int j = -kernel.length; k = m; indexq = kernel.length - 1; double Sum = 0.0; while (j < 0) { indexp = k; int kp = ((k - l) < j) ? (j) : (k - l); if (kp < 0L) { for (int n = kp; n < 0; n++) { Sum += input[indexp] * kernel[indexq]; indexq--; indexp++; } k -= kp; j -= kp; } indexp = n2 - k; int km = ((k - n2) < j) ? (j) : (k - n2); if (km < 0L) { for (int n = km; n < 0; n++) { Sum += input[indexp] * kernel[indexq]; indexq--; indexp--; } j -= km; } k = 0; } if (++m == n2) { m = 0; } output[i] = Sum; } return output; } /** * Convolve with with a Infinite Impluse Response filter (IIR) * * @param input * 1D input signal * @param poles * 1D array containing the poles of the filter */ public static double[] convolveIIR(double[] input, double poles[]) { double lambda = 1.0; int l = input.length; double[] output = new double[l]; for (int k = 0; k < poles.length; k++) { lambda = lambda * (1.0 - poles[k]) * (1.0 - 1.0 / poles[k]); } for (int n = 0; n < l; n++) { output[n] = input[n] * lambda; } for (int k = 0; k < poles.length; k++) { output[0] = getInitialCausalCoefficientMirror(output, poles[k]); for (int n = 1; n < l; n++) { output[n] = output[n] + poles[k] * output[n - 1]; } output[l - 1] = getInitialAntiCausalCoefficientMirror(output, poles[k]); for (int n = l - 2; 0 <= n; n--) { output[n] = poles[k] * (output[n + 1] - output[n]); } } return output; } /** * Convolve a 1D signal with a Infinite Impluse Response 2nd order (IIR2) * * Note: Only with the mirror (on bounds) boundary conditions. * * Purpose: Recursive implementation of a symmetric 2nd order filter with * mirror symmetry boundary conditions : * * 1 1 H[z] = --------------- * --------------- (1-b1*z-b2*z^2) * (1-b1/z-b2/z^2) * * implemented in the following form: * * a1+a2*z a1+a2/z H[z] = --------------- + --------------- - a1 * (1-b1*z+b2*z^2) (1-b1/z+b2/z^2) * * where : a1 = -(b2 + 1.0) * (1 - b1 + b2) / ((b2 - 1.0) * (1 + b1 + b2)); * a2 = - a1 * b2 * b1 / (b2 + 1.0); * * @param input * 1D input signal * @param b1 * first pole of the filter * @param b2 * second pole of the filter */ public static double[] convolveIIR2(double input[], double b1, double b2) { int l = input.length; int n2 = 2 * l; double a1 = -(b2 + 1.0) * (1 - b1 + b2) / ((b2 - 1.0) * (1 + b1 + b2)); double a2 = -a1 * b2 * b1 / (b2 + 1.0); // cBuffer stores temporary spline coefficients double cBuffer[] = new double[n2]; // sBuffer contains a copy of s[] and a time reversed version of s[] double sBuffer[] = new double[n2]; // copy signal s[] and its time reversed version to sBuffer[] for (int n = 0; n < l; n++) { sBuffer[n] = input[n]; sBuffer[n2 - n - 1] = input[n]; } // Determine the start index n0 for the causal recursion. n0 is chosen // such // that the error of cBuffer[0] and cBuffer[1] is smaller than the // specified 'Tolerance'. int n0 = 2; if ((tolerance > 0.0) && (b2 != 1.0)) { n0 = n2 - (int) Math.ceil(2.0 * Math.log(tolerance) / Math.log(b2)); } if (n0 < 2) { n0 = 2; } cBuffer[n0 - 1] = 0.0; cBuffer[n0 - 2] = 0.0; for (int n = n0; n < n2; n++) { cBuffer[n] = a1 * sBuffer[n] + a2 * sBuffer[n - 1] + b1 * cBuffer[n - 1] - b2 * cBuffer[n - 2]; } cBuffer[0] = a1 * sBuffer[0] + a2 * sBuffer[n2 - 1] + b1 * cBuffer[n2 - 1] - b2 * cBuffer[n2 - 2]; cBuffer[1] = a1 * sBuffer[1] + a2 * sBuffer[0] + b1 * cBuffer[0] - b2 * cBuffer[n2 - 1]; // compute the remaining spline coefficients cBuffer(z) = H_{+}(z) * // sBuffer(z) by // recursive filtering for (int n = 2; n < n2; n++) { cBuffer[n] = a1 * sBuffer[n] + a2 * sBuffer[n - 1] + b1 * cBuffer[n - 1] - b2 * cBuffer[n - 2]; } // add together the temporary filter outputs to obtain the final spline // coefficients double[] output = new double[l]; for (int n = 0; n < l; n++) { output[n] = cBuffer[n] + cBuffer[n2 - n - 1] - a1 * input[n]; } return output; } /** */ private static double getInitialAntiCausalCoefficientMirror(double[] c, double z) { return ((z * c[c.length - 2] + c[c.length - 1]) * z / (z * z - 1.0)); } /** */ private static double getInitialCausalCoefficientMirror(double[] c, double z) { double z1 = z, zn = Math.pow(z, c.length - 1); double sum = c[0] + zn * c[c.length - 1]; int horizon = c.length; if (0.0 < tolerance) { horizon = 2 + (int) (Math.log(tolerance) / Math.log(Math.abs(z))); horizon = (horizon < c.length) ? (horizon) : (c.length); } zn = zn * zn; for (int n = 1; (n < (horizon - 1)); n++) { zn = zn / z; sum = sum + (z1 + zn) * c[n]; z1 = z1 * z; } return (sum / (1.0 - Math.pow(z, 2 * c.length - 2))); } } // end of classe \ No newline at end of file diff --git a/src/bilib/src/imageware/Display.java b/src/bilib/src/imageware/Display.java new file mode 100644 index 0000000..4a85ab4 --- /dev/null +++ b/src/bilib/src/imageware/Display.java @@ -0,0 +1 @@ +package imageware; import ij.ImagePlus; import ij.ImageStack; import ij.gui.ImageCanvas; import ij.gui.ImageWindow; import ij.process.ColorProcessor; import javax.swing.SwingUtilities; /** * Class Display. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class Display { /** * Class Show. Display an imageware in the swing thread. */ static class Show implements Runnable { private String title; private ImageStack stack; public Show(String title, ImageStack stack) { this.title = title; this.stack = stack; } public void run() { (new ImagePlus(title, stack)).show(); } } /** * Class ShowZoom. Display an imageware in the swing thread. */ static class ShowZoom implements Runnable { private String title; private ImageStack stack; private double magnification; public ShowZoom(String title, ImageStack stack, double magnification) { this.title = title; this.stack = stack; } public void run() { ImagePlus imp = new ImagePlus(title, stack); imp.show(); ImageWindow win = imp.getWindow(); ImageCanvas canvas = win.getCanvas(); canvas.setMagnification(magnification); canvas.setDrawingSize((int) Math.ceil(stack.getWidth() * magnification), (int) Math.ceil(stack.getHeight() * magnification)); win.pack(); imp.updateAndRepaintWindow(); } } /** * Shows a imageware with a specifc title. * * @param title * a specific title * @param ds * the imageware to be shown */ static public void show(String title, ImageWare ds) { ImageStack stack = ds.buildImageStack(); SwingUtilities.invokeLater(new Show(title, stack)); } /** * Shows color image composed by three datasets with a specific title. * * @param title * a specific title * @param red * the imageware to be shown in the red channel * @param green * the imageware to be shown in the green channel * @param blue * the imageware to be shown in the blue channel */ static public void showColor(String title, ImageWare red, ImageWare green, ImageWare blue) { ImageStack stack = buildColor(red, green, blue); (new ImagePlus(title, stack)).show(); } /** * Shows a imageware with a specific title and with a specific * magnification. * * @param title * a specific title * @param ds * the imageware to be shown * @param magnification * zoom factor */ static public void show(String title, ImageWare ds, double magnification) { ImageStack stack = ds.buildImageStack(); SwingUtilities.invokeLater(new ShowZoom(title, stack, magnification)); } /** * Shows color image composed by three datasets with a specifc title and * with a specific magnification. * * @param title * a specific title * @param red * the imageware to be shown in the red channel * @param green * the imageware to be shown in the green channel * @param blue * the imageware to be shown in the blue channel * @param magnification * zoom factor */ static public void showColor(String title, ImageWare red, ImageWare green, ImageWare blue, double magnification) { ImageStack stack = buildColor(red, green, blue); SwingUtilities.invokeLater(new ShowZoom(title, stack, magnification)); } /** */ static private ImageStack buildColor(ImageWare red, ImageWare green, ImageWare blue) { if (!red.isSameSize(green)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a ImageStack the channel are not the same size.\n" + "[" + red.getSizeX() + "," + red.getSizeY() + "," + red.getSizeZ() + "] != " + "[" + green.getSizeX() + "," + green.getSizeY() + "," + green.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } if (!red.isSameSize(blue)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a ImageStack the channel are not the same size.\n" + "[" + red.getSizeX() + "," + red.getSizeY() + "," + red.getSizeZ() + "] != " + "[" + blue.getSizeX() + "," + blue.getSizeY() + "," + blue.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } int nx = red.getSizeX(); int ny = red.getSizeY(); int nz = red.getSizeZ(); int nxy = nx * ny; ImageStack imagestack = new ImageStack(nx, ny); ColorProcessor cp; byte[] r = new byte[nxy]; byte[] g = new byte[nxy]; byte[] b = new byte[nxy]; for (int z = 0; z < nz; z++) { cp = new ColorProcessor(nx, ny); switch (red.getType()) { case ImageWare.DOUBLE: double[] dpixred = red.getSliceDouble(z); for (int k = 0; k < nxy; k++) r[k] = (byte) (dpixred[k]); break; case ImageWare.FLOAT: float[] fpixred = red.getSliceFloat(z); for (int k = 0; k < nxy; k++) r[k] = (byte) (fpixred[k]); break; case ImageWare.SHORT: short[] spixred = red.getSliceShort(z); for (int k = 0; k < nxy; k++) r[k] = (byte) (spixred[k]); break; case ImageWare.BYTE: r = red.getSliceByte(z); break; } switch (green.getType()) { case ImageWare.DOUBLE: double[] dpixgreen = green.getSliceDouble(z); for (int k = 0; k < nxy; k++) g[k] = (byte) (dpixgreen[k]); break; case ImageWare.FLOAT: float[] fpixgreen = green.getSliceFloat(z); for (int k = 0; k < nxy; k++) g[k] = (byte) (fpixgreen[k]); break; case ImageWare.SHORT: short[] spixgreen = green.getSliceShort(z); for (int k = 0; k < nxy; k++) g[k] = (byte) (spixgreen[k]); break; case ImageWare.BYTE: g = green.getSliceByte(z); break; } switch (blue.getType()) { case ImageWare.DOUBLE: double[] dpixblue = blue.getSliceDouble(z); for (int k = 0; k < nxy; k++) b[k] = (byte) (dpixblue[k]); break; case ImageWare.FLOAT: float[] fpixblue = blue.getSliceFloat(z); for (int k = 0; k < nxy; k++) b[k] = (byte) (fpixblue[k]); break; case ImageWare.SHORT: short[] spixblue = blue.getSliceShort(z); for (int k = 0; k < nxy; k++) b[k] = (byte) (spixblue[k]); break; case ImageWare.BYTE: b = blue.getSliceByte(z); break; } cp.setRGB(r, g, b); imagestack.addSlice("" + z, cp); } return imagestack; } } \ No newline at end of file diff --git a/src/bilib/src/imageware/DoubleAccess.java b/src/bilib/src/imageware/DoubleAccess.java new file mode 100644 index 0000000..0662b87 --- /dev/null +++ b/src/bilib/src/imageware/DoubleAccess.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import java.awt.Image; /** * Class DoubleAccess. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class DoubleAccess extends DoubleBuffer implements Access { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected DoubleAccess(int nx, int ny, int nz) { super(nx, ny, nz); } protected DoubleAccess(Image image, int mode) { super(image, mode); } protected DoubleAccess(ImageStack stack, int mode) { super(stack, mode); } protected DoubleAccess(ImageStack stack, byte chan) { super(stack, chan); } protected DoubleAccess(byte[] array, int mode) { super(array, mode); } protected DoubleAccess(byte[][] array, int mode) { super(array, mode); } protected DoubleAccess(byte[][][] array, int mode) { super(array, mode); } protected DoubleAccess(short[] array, int mode) { super(array, mode); } protected DoubleAccess(short[][] array, int mode) { super(array, mode); } protected DoubleAccess(short[][][] array, int mode) { super(array, mode); } protected DoubleAccess(float[] array, int mode) { super(array, mode); } protected DoubleAccess(float[][] array, int mode) { super(array, mode); } protected DoubleAccess(float[][][] array, int mode) { super(array, mode); } protected DoubleAccess(double[] array, int mode) { super(array, mode); } protected DoubleAccess(double[][] array, int mode) { super(array, mode); } protected DoubleAccess(double[][][] array, int mode) { super(array, mode); } // ------------------------------------------------------------------ // // getPixel section // // ------------------------------------------------------------------ /** * Get a pixel at specific position without specific boundary conditions * * If the positions is outside of this imageware, the method return 0.0. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @return a pixel value */ public double getPixel(int x, int y, int z) { if (x >= nx) return 0.0; if (y >= ny) return 0.0; if (z >= nz) return 0.0; if (x < 0) return 0.0; if (y < 0) return 0.0; if (z < 0) return 0.0; return ((double[]) data[z])[x + y * nx]; } /** * Get a pixel at specific position with specific boundary conditions * * If the positions is outside of this imageware, the method apply the * boundary conditions to return a value. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @return a pixel value */ public double getPixel(int x, int y, int z, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a pixel \n" + "at the position (" + x + "," + y + "," + z + ".\n" + "-------------------------------------------------------\n"); } int xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } return ((double[]) data[zp])[xp + yp * nx]; } /** * Get a interpolated pixel value at specific position without specific * boundary conditions. * * If the positions is not on the pixel grid, the method return a * interpolated value of the pixel (linear interpolation). If the positions * is outside of this imageware, the method return 0.0. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @return a interpolated value */ public double getInterpolatedPixel(double x, double y, double z) { if (x > nx - 1) return 0.0; if (y > ny - 1) return 0.0; if (z > nz - 1) return 0.0; if (x < 0) return 0.0; if (y < 0) return 0.0; if (z < 0) return 0.0; double output = 0.0; /* * int i = (x >= 0.0 ? ((int)x) : ((int)x - 1)); int j = (y >= 0.0 ? * ((int)y) : ((int)y - 1)); int k = (z >= 0.0 ? ((int)z) : ((int)z - * 1)); */ int i = (x >= 0.0 ? ((int) x) : ((int) x - 1)); int j = (y >= 0.0 ? ((int) y) : ((int) y - 1)); int k = (z >= 0.0 ? ((int) z) : ((int) z - 1)); boolean fi = (i == nx - 1); boolean fj = (j == ny - 1); boolean fk = (k == nz - 1); int index = i + j * nx; switch (getDimension()) { case 1: double v1_0 = (double) (((double[]) data[k])[index]); double v1_1 = (fi ? v1_0 : (double) (((double[]) data[k])[index + 1])); double dx1 = x - (double) i; return v1_1 * dx1 - v1_0 * (dx1 - 1.0); case 2: double v2_00 = (double) (((double[]) data[k])[index]); double v2_10 = (fi ? v2_00 : (double) (((double[]) data[k])[index + 1])); double v2_01 = (fj ? v2_00 : (double) (((double[]) data[k])[index + nx])); double v2_11 = (fi ? (fj ? v2_00 : v2_01) : (double) (((double[]) data[k])[index + 1 + nx])); double dx2 = x - (double) i; double dy2 = y - (double) j; return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0))); case 3: double v3_000 = (double) (((double[]) data[k])[index]); double v3_100 = (fi ? v3_000 : (double) (((double[]) data[k])[index + 1])); double v3_010 = (fj ? v3_000 : (double) (((double[]) data[k])[index + nx])); double v3_110 = (fi ? (fj ? v3_000 : v3_010) : (double) (((double[]) data[k])[index + 1 + nx])); double v3_001 = (fk ? v3_000 : (double) (((double[]) data[k + 1])[index])); double v3_011 = (fk ? (fj ? v3_000 : v3_010) : (double) (((double[]) data[k + 1])[index + 1])); double v3_101 = (fk ? (fi ? v3_000 : v3_100) : (double) (((double[]) data[k + 1])[index + nx])); double v3_111 = (fk ? (fj ? (fi ? v3_000 : v3_100) : v3_110) : (double) (((double[]) data[k + 1])[index + 1 + nx])); double dx3 = x - (double) i; double dy3 = y - (double) j; double dz3 = z - (double) k; double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0))); double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0))); return z2 * dz3 - z1 * (dz3 - 1.0); } return output; } /** * Get a interpolated pixel value at specific position with specific * boundary conditions. * * If the positions is not on the pixel grid, the method return a * interpolated value of the pixel (linear interpolation). If the positions * is outside of this imageware, the method apply the boundary conditions to * return a value. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @param boundaryConditions * MIRROR or PERIODIC boundary conditions * @return a interpolated value */ public double getInterpolatedPixel(double x, double y, double z, byte boundaryConditions) { double output = 0.0; int i = (x >= 0.0 ? ((int) x) : ((int) x - 1)); int j = (y >= 0.0 ? ((int) y) : ((int) y - 1)); int k = (z >= 0.0 ? ((int) z) : ((int) z - 1)); switch (getDimension()) { case 1: double v1_0 = getPixel(i, j, k, boundaryConditions); double v1_1 = getPixel(i + 1, j, k, boundaryConditions); double dx1 = x - (double) i; return v1_1 * dx1 - v1_0 * (dx1 - 1.0); case 2: double v2_00 = getPixel(i, j, k, boundaryConditions); double v2_10 = getPixel(i + 1, j, k, boundaryConditions); double v2_01 = getPixel(i, j + 1, k, boundaryConditions); double v2_11 = getPixel(i + 1, j + 1, k, boundaryConditions); double dx2 = x - (double) i; double dy2 = y - (double) j; return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0))); case 3: double v3_000 = getPixel(i, j, k, boundaryConditions); double v3_100 = getPixel(i + 1, j, k, boundaryConditions); double v3_010 = getPixel(i, j + 1, k, boundaryConditions); double v3_110 = getPixel(i + 1, j + 1, k, boundaryConditions); double v3_001 = getPixel(i, j, k + 1, boundaryConditions); double v3_011 = getPixel(i + 1, j, k + 1, boundaryConditions); double v3_101 = getPixel(i, j + 1, k + 1, boundaryConditions); double v3_111 = getPixel(i + 1, j + 1, k + 1, boundaryConditions); double dx3 = x - (double) i; double dy3 = y - (double) j; double dz3 = z - (double) k; double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0))); double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0))); return z2 * dz3 - z1 * (dz3 - 1.0); } return output; } // ------------------------------------------------------------------ // // putPixel section // // ------------------------------------------------------------------ /** * Put a pixel at specific position * * If the positions is outside of this imageware, the method does nothing. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis */ public void putPixel(int x, int y, int z, double value) { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; if (x < 0) return; if (y < 0) return; if (z < 0) return; ((double[]) data[z])[x + y * nx] = (double) value; } // ------------------------------------------------------------------ // // getBounded section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (byte) (tmp[offset]); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (short) (tmp[offset]); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (float) (tmp[offset]); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (double) (tmp[offset]); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (byte) (tmp[offset]); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (short) (tmp[offset]); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (float) (tmp[offset]); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (double) (tmp[offset]); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (byte) (((double[]) data[k])[offset]); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (short) (((double[]) data[k])[offset]); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (float) (((double[]) data[k])[offset]); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (double) (((double[]) data[k])[offset]); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); double[] tmp = (double[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (byte) (tmp[offset]); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); double[] tmp = (double[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (short) (tmp[offset]); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); double[] tmp = (double[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (float) (tmp[offset]); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); double[] tmp = (double[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (double) (tmp[offset]); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (byte) (((double[]) data[z])[offset]); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (short) (((double[]) data[z])[offset]); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (float) (((double[]) data[z])[offset]); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (double) (((double[]) data[z])[offset]); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (byte) (((double[]) data[z])[offset]); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (short) (((double[]) data[z])[offset]); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (float) (((double[]) data[z])[offset]); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (double) (((double[]) data[z])[offset]); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, byte[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { double[] tmp = (double[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (byte) (tmp[offset]); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, short[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { double[] tmp = (double[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (short) (tmp[offset]); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, float[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { double[] tmp = (double[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (float) (tmp[offset]); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, double[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { double[] tmp = (double[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (double) (tmp[offset]); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } // ------------------------------------------------------------------ // // getBlock section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; double[] tmp = (double[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (byte) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; double[] tmp = (double[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (short) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; double[] tmp = (double[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (float) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; double[] tmp = (double[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (double) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } double[] tmp = (double[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (byte) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } double[] tmp = (double[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (short) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } double[] tmp = (double[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (float) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } double[] tmp = (double[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (double) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (byte) (((double[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (short) (((double[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (float) (((double[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (double) (((double[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } double[] tmp = (double[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } double[] tmp = (double[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } double[] tmp = (double[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } double[] tmp = (double[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (((double[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (((double[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (((double[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (((double[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (byte) (((double[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (short) (((double[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (float) (((double[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (double) (((double[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } double[] tmp = (double[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (byte) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } double[] tmp = (double[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (short) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } double[] tmp = (double[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (float) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } double[] tmp = (double[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (double) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } // ------------------------------------------------------------------ // // getBlock section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; double[] tmp = ((double[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (byte) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; double[] tmp = ((double[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (short) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; double[] tmp = ((double[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (float) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; double[] tmp = ((double[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (double) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; double[] tmp = ((double[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (byte) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; double[] tmp = ((double[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (short) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; double[] tmp = ((double[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (float) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; double[] tmp = ((double[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (double) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (byte) (((double[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (short) (((double[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (float) (((double[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (double) (((double[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; double[] tmp = ((double[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; double[] tmp = ((double[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; double[] tmp = ((double[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; double[] tmp = ((double[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (((double[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (((double[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (((double[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (((double[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (byte) (((double[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (short) (((double[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (float) (((double[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (double) (((double[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } double[] tmp = ((double[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (byte) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } double[] tmp = ((double[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (short) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } double[] tmp = ((double[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (float) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } double[] tmp = ((double[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (double) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } // ------------------------------------------------------------------ // // putBounded section // // ------------------------------------------------------------------ /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i] & 0xFF); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i] & 0xFFFF); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i]); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i]); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i] & 0xFF); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i] & 0xFFFF); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i]); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); double[] tmp = (double[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i]); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((double[]) data[k])[offset] = (double) (buffer[i] & 0xFF); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((double[]) data[k])[offset] = (double) (buffer[i] & 0xFFFF); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((double[]) data[k])[offset] = (double) (buffer[i]); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((double[]) data[k])[offset] = (double) (buffer[i]); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); double[] tmp = (double[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i][j] & 0xFF); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); double[] tmp = (double[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i][j] & 0xFFFF); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); double[] tmp = (double[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i][j]); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); double[] tmp = (double[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i][j]); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((double[]) data[k])[offset] = (double) (buffer[i][j] & 0xFF); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((double[]) data[k])[offset] = (double) (buffer[i][j] & 0xFFFF); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((double[]) data[k])[offset] = (double) (buffer[i][j]); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((double[]) data[k])[offset] = (double) (buffer[i][j]); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((double[]) data[k])[offset] = (double) (buffer[i][j] & 0xFF); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((double[]) data[k])[offset] = (double) (buffer[i][j] & 0xFFFF); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((double[]) data[k])[offset] = (double) (buffer[i][j]); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((double[]) data[k])[offset] = (double) (buffer[i][j]); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, byte[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { double[] tmp = (double[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i][j][k] & 0xFF); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, short[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { double[] tmp = (double[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i][j][k] & 0xFFFF); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, float[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { double[] tmp = (double[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i][j][k]); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, double[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { double[] tmp = (double[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (double) (buffer[i][j][k]); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/DoubleBuffer.java b/src/bilib/src/imageware/DoubleBuffer.java new file mode 100644 index 0000000..7995ce6 --- /dev/null +++ b/src/bilib/src/imageware/DoubleBuffer.java @@ -0,0 +1,2769 @@ +package imageware; + +import ij.ImageStack; +import ij.process.ByteProcessor; +import ij.process.ColorProcessor; +import ij.process.FloatProcessor; +import ij.process.ImageProcessor; +import ij.process.ShortProcessor; + +import java.awt.Image; +import java.awt.image.ImageObserver; +import java.awt.image.PixelGrabber; + +/** + * Class DoubleBuffer. + * + * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de + * Lausanne, Lausanne, Switzerland + */ + +public class DoubleBuffer implements Buffer { + + protected Object[] data = null; + protected int nx = 0; + protected int ny = 0; + protected int nz = 0; + protected int nxy = 0; + + /** + * Constructor of a empty 3D double buffer. + * + * @param nx + * size of the 3D buffer in the X axis + * @param ny + * size of the 3D buffer in the Y axis + * @param nz + * size of the 3D buffer in the Z axis + */ + protected DoubleBuffer(int nx, int ny, int nz) { + this.nx = nx; + this.ny = ny; + this.nz = nz; + if (nx <= 0 || ny <= 0 || nz <= 0) + throw_constructor(nx, ny, nz); + allocate(); + } + + /** + * Constructor of a double buffer from a object Image of Java. + * + * @param image + * source to build a new imageware + */ + protected DoubleBuffer(Image image, int mode) { + if (image == null) { + throw_constructor(); + } + ImageObserver observer = null; + this.nx = image.getWidth(observer); + this.ny = image.getHeight(observer); + this.nz = 1; + this.nxy = nx * ny; + byte[] pixels = new byte[nxy]; + PixelGrabber pg = new PixelGrabber(image, 0, 0, nx, ny, false); + try { + pg.grabPixels(); + pixels = (byte[]) (pg.getPixels()); + } + catch (Exception e) { + throw_constructor(); + } + allocate(); + for (int k = 0; k < nxy; k++) + ((double[]) data[0])[k] = (double) (pixels[k] & 0xFF); + } + + /** + * Constructor of a double buffer from a ImageStack. + * + * New data are allocated if the mode is CREATE, the imageware use the data + * of ImageJ if the mode is WRAP. + * + * @param stack + * source to build a new imageware + * @param mode + * WRAP or CREATE + */ + protected DoubleBuffer(ImageStack stack, int mode) { + if (stack == null) { + throw_constructor(); + } + this.nx = stack.getWidth(); + this.ny = stack.getHeight(); + this.nz = stack.getSize(); + this.nxy = nx * ny; + switch (mode) { + case ImageWare.WRAP: + throw_constructor(); + break; + case ImageWare.CREATE: + allocate(); + ImageProcessor ip = stack.getProcessor(1); + if (ip instanceof ByteProcessor) { + Object[] vol = stack.getImageArray(); + for (int z = 0; z < nz; z++) { + byte[] slice = (byte[]) vol[z]; + for (int k = 0; k < nxy; k++) { + ((double[]) data[z])[k] = (double) (slice[k] & 0xFF); + } + } + } + else if (ip instanceof ShortProcessor) { + Object[] vol = stack.getImageArray(); + for (int z = 0; z < nz; z++) { + short[] slice = (short[]) vol[z]; + for (int k = 0; k < nxy; k++) { + ((double[]) data[z])[k] = (double) (slice[k] & 0xFFFF); + } + } + } + else if (ip instanceof FloatProcessor) { + Object[] vol = stack.getImageArray(); + for (int z = 0; z < nz; z++) { + float[] slice = (float[]) vol[z]; + for (int k = 0; k < nxy; k++) { + ((double[]) data[z])[k] = (double) slice[k]; + } + } + } + else if (ip instanceof ColorProcessor) { + double r, g, b; + int c; + ColorProcessor cp; + int[] pixels; + for (int z = 0; z < nz; z++) { + cp = (ColorProcessor) stack.getProcessor(z + 1); + pixels = (int[]) cp.getPixels(); + for (int k = 0; k < nxy; k++) { + c = pixels[k]; + r = (double) ((c & 0xFF0000) >> 16); + g = (double) ((c & 0xFF00) >> 8); + b = (double) ((c & 0xFF)); + ((double[]) data[z])[k] = (double) ((r + g + b) / 3.0); + } + } + } + else { + throw_constructor(); + } + break; + default: + throw_constructor(); + break; + } + } + + /** + * Constructor of a double buffer from a specific color channel of + * ImageStack. + * + * New data are always allocated. If it is a gray image the imageware is + * created and fill up with data of the source ImageStack. If it is a color + * image only the selected channel is used to create this imageware. + * + * @param stack + * source to build a new imageware + * @param channel + * RED, GREEN or BLUE + */ + protected DoubleBuffer(ImageStack stack, byte channel) { + if (stack == null) { + throw_constructor(); + } + this.nx = stack.getWidth(); + this.ny = stack.getHeight(); + this.nz = stack.getSize(); + this.nxy = nx * ny; + allocate(); + ImageProcessor ip = stack.getProcessor(1); + if (ip instanceof ByteProcessor) { + Object[] vol = stack.getImageArray(); + for (int z = 0; z < nz; z++) { + byte[] slice = (byte[]) vol[z]; + for (int k = 0; k < nxy; k++) { + ((double[]) data[z])[k] = (double) (slice[k] & 0xFF); + } + } + } + else if (ip instanceof ShortProcessor) { + Object[] vol = stack.getImageArray(); + for (int z = 0; z < nz; z++) { + short[] slice = (short[]) vol[z]; + for (int k = 0; k < nxy; k++) { + ((double[]) data[z])[k] = (double) (slice[k] & 0xFFFF); + } + } + } + else if (ip instanceof FloatProcessor) { + Object[] vol = stack.getImageArray(); + for (int z = 0; z < nz; z++) { + float[] slice = (float[]) vol[z]; + for (int k = 0; k < nxy; k++) { + ((double[]) data[z])[k] = (double) slice[k]; + } + } + } + else if (ip instanceof ColorProcessor) { + ColorProcessor cp; + int[] pixels; + for (int z = 0; z < nz; z++) { + cp = (ColorProcessor) stack.getProcessor(z + 1); + pixels = (int[]) cp.getPixels(); + switch (channel) { + case ImageWare.RED: + for (int k = 0; k < nxy; k++) { + ((double[]) data[z])[k] = (double) ((pixels[k] & 0xFF0000) >> 16); + } + break; + case ImageWare.GREEN: + for (int k = 0; k < nxy; k++) { + ((double[]) data[z])[k] = (double) ((pixels[k] & 0xFF00) >> 8); + } + break; + case ImageWare.BLUE: + for (int k = 0; k < nxy; k++) { + ((double[]) data[z])[k] = (double) (pixels[k] & 0xFF); + } + break; + default: + throw_constructor(); + } + } + } + else { + throw_constructor(); + } + } + + /** + * Constructor of a double buffer from a byte array. + * + * @param array + * source to build this new imageware + */ + protected DoubleBuffer(byte[] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = 1; + this.nz = 1; + allocate(); + putX(0, 0, 0, array); + } + + /** + * Constructor of a double buffer from a byte array. + * + * @param array + * source to build this new imageware + */ + protected DoubleBuffer(byte[][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = 1; + allocate(); + putXY(0, 0, 0, array); + } + + /** + * Constructor of a double buffer from a byte array. + * + * @param array + * source to build this new imageware + */ + protected DoubleBuffer(byte[][][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = array[0][0].length; + allocate(); + putXYZ(0, 0, 0, array); + } + + /** + * Constructor of a double buffer from a short array. + * + * @param array + * source to build this new imageware + */ + protected DoubleBuffer(short[] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = 1; + this.nz = 1; + allocate(); + putX(0, 0, 0, array); + } + + /** + * Constructor of a double buffer from a short array. + * + * @param array + * source to build this new imageware + */ + protected DoubleBuffer(short[][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = 1; + allocate(); + putXY(0, 0, 0, array); + } + + /** + * Constructor of a double buffer from a short array. + * + * @param array + * source to build this new imageware + */ + protected DoubleBuffer(short[][][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = array[0][0].length; + allocate(); + putXYZ(0, 0, 0, array); + } + + /** + * Constructor of a double buffer from a float array. + * + * @param array + * source to build this new imageware + */ + protected DoubleBuffer(float[] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = 1; + this.nz = 1; + allocate(); + putX(0, 0, 0, array); + } + + /** + * Constructor of a double buffer from a float array. + * + * @param array + * source to build this new imageware + */ + protected DoubleBuffer(float[][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = 1; + allocate(); + putXY(0, 0, 0, array); + } + + /** + * Constructor of a double buffer from a float array. + * + * @param array + * source to build this new imageware + */ + protected DoubleBuffer(float[][][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = array[0][0].length; + allocate(); + putXYZ(0, 0, 0, array); + } + + /** + * Constructor of a double buffer from a double array. + * + * @param array + * source to build this new imageware + */ + protected DoubleBuffer(double[] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = 1; + this.nz = 1; + allocate(); + putX(0, 0, 0, array); + } + + /** + * Constructor of a double buffer from a double array. + * + * @param array + * source to build this new imageware + */ + protected DoubleBuffer(double[][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = 1; + allocate(); + putXY(0, 0, 0, array); + } + + /** + * Constructor of a double buffer from a double array. + * + * @param array + * source to build this new imageware + */ + protected DoubleBuffer(double[][][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = array[0][0].length; + allocate(); + putXYZ(0, 0, 0, array); + } + + /** + * Return the type of this imageware. + * + * @return the type of this imageware + */ + public int getType() { + return ImageWare.DOUBLE; + } + + /** + * Return the type of this imageware in a string format. + * + * @return the type of this imageware translated in a string format + */ + public String getTypeToString() { + return "Double"; + } + + /** + * Return the number of dimension of this imageware (1, 2 or 3). + * + * @return the number of dimension of this imageware + */ + public int getDimension() { + int dims = 0; + dims += (nx > 1 ? 1 : 0); + dims += (ny > 1 ? 1 : 0); + dims += (nz > 1 ? 1 : 0); + return dims; + } + + /** + * Return the size of the imageware int[0] : x, int[1] : y, int[2] : z. + * + * @return an array given the size of the imageware + */ + public int[] getSize() { + int[] size = { nx, ny, nz }; + return size; + } + + /** + * Return the size in the X axis. + * + * @return the size in the X axis + */ + public int getSizeX() { + return nx; + } + + /** + * Return the size in the Y axis. + * + * @return the size in the Y axis + */ + public int getSizeY() { + return ny; + } + + /** + * Return the size in the Z axis. + * + * @return the size in the Z axis + */ + public int getSizeZ() { + return nz; + } + + /** + * Return the size in the X axis. + * + * @return the size in the X axis + */ + public int getWidth() { + return nx; + } + + /** + * Return the size in the Y axis. + * + * @return the size in the Y axis + */ + public int getHeight() { + return ny; + } + + /** + * Return the size in the Z axis. + * + * @return the size in the Z axis + */ + public int getDepth() { + return nz; + } + + /** + * Return the number of pixels in the imageware. + * + * @return number of pixels in the imageware + */ + public int getTotalSize() { + return nxy * nz; + } + + /** + * Return true is this imageware has the same size the imageware given as + * parameter. + * + * @param imageware + * imageware to be compared + * @return true if the imageware of the same size than this imageware + */ + public boolean isSameSize(ImageWare imageware) { + if (nx != imageware.getSizeX()) + return false; + if (ny != imageware.getSizeY()) + return false; + if (nz != imageware.getSizeZ()) + return false; + return true; + } + + // ------------------------------------------------------------------ + // + // put Section + // + // ------------------------------------------------------------------ + + /** + * Put an array into the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putX(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + double buf[] = new double[bnx]; + buffer.getX(0, 0, 0, buf); + putX(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putY(int x, int y, int z, ImageWare buffer) { + int bny = buffer.getSizeY(); + double buf[] = new double[bny]; + buffer.getY(0, 0, 0, buf); + putY(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putZ(int x, int y, int z, ImageWare buffer) { + int bnz = buffer.getSizeZ(); + double buf[] = new double[bnz]; + buffer.getZ(0, 0, 0, buf); + putZ(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putXY(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + int bny = buffer.getSizeY(); + double buf[][] = new double[bnx][bny]; + buffer.getXY(0, 0, 0, buf); + putXY(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putXZ(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + int bnz = buffer.getSizeZ(); + double buf[][] = new double[bnx][bnz]; + buffer.getXZ(0, 0, 0, buf); + putXZ(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putYZ(int x, int y, int z, ImageWare buffer) { + int bny = buffer.getSizeY(); + int bnz = buffer.getSizeZ(); + double buf[][] = new double[bny][bnz]; + buffer.getYZ(0, 0, 0, buf); + putYZ(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putXYZ(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + int bny = buffer.getSizeY(); + int bnz = buffer.getSizeZ(); + double buf[][][] = new double[bnx][bny][bnz]; + buffer.getXYZ(0, 0, 0, buf); + putXYZ(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byte 1D array to put into the imageware + */ + public void putX(int x, int y, int z, byte[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + + for (int i = 0; i < leni; i++) { + tmp[offset] = (double) (buffer[i] & 0xFF); + offset++; + } + } + catch (Exception e) { + throw_put("X", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * short 1D array to put into the imageware + */ + public void putX(int x, int y, int z, short[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + + for (int i = 0; i < leni; i++) { + tmp[offset] = (double) (buffer[i] & 0xFFFF); + offset++; + } + } + catch (Exception e) { + throw_put("X", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * float 1D array to put into the imageware + */ + public void putX(int x, int y, int z, float[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + + for (int i = 0; i < leni; i++) { + tmp[offset] = (double) (buffer[i]); + offset++; + } + } + catch (Exception e) { + throw_put("X", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * double 1D array to put into the imageware + */ + public void putX(int x, int y, int z, double[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + + System.arraycopy(buffer, 0, tmp, offset, leni); + } + catch (Exception e) { + throw_put("X", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byte 1D array to put into the imageware + */ + public void putY(int x, int y, int z, byte[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + for (int i = 0; i < leni; i++) { + tmp[offset] = (double) (buffer[i] & 0xFF); + offset += nx; + } + } + catch (Exception e) { + throw_put("Y", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * short 1D array to put into the imageware + */ + public void putY(int x, int y, int z, short[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + for (int i = 0; i < leni; i++) { + tmp[offset] = (double) (buffer[i] & 0xFFFF); + offset += nx; + } + } + catch (Exception e) { + throw_put("Y", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * float 1D array to put into the imageware + */ + public void putY(int x, int y, int z, float[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + for (int i = 0; i < leni; i++) { + tmp[offset] = (double) (buffer[i]); + offset += nx; + } + } + catch (Exception e) { + throw_put("Y", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * double 1D array to put into the imageware + */ + public void putY(int x, int y, int z, double[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + for (int i = 0; i < leni; i++) { + tmp[offset] = (double) (buffer[i]); + offset += nx; + } + } + catch (Exception e) { + throw_put("Y", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * bybytete 1D array to put into the imageware + */ + public void putZ(int x, int y, int z, byte[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + ((double[]) data[z])[offset] = (double) (buffer[i] & 0xFF); + z++; + } + } + catch (Exception e) { + throw_put("Z", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byshortte 1D array to put into the imageware + */ + public void putZ(int x, int y, int z, short[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + ((double[]) data[z])[offset] = (double) (buffer[i] & 0xFFFF); + z++; + } + } + catch (Exception e) { + throw_put("Z", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byfloatte 1D array to put into the imageware + */ + public void putZ(int x, int y, int z, float[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + ((double[]) data[z])[offset] = (double) (buffer[i]); + z++; + } + } + catch (Exception e) { + throw_put("Z", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * bydoublete 1D array to put into the imageware + */ + public void putZ(int x, int y, int z, double[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + ((double[]) data[z])[offset] = (double) (buffer[i]); + z++; + } + } + catch (Exception e) { + throw_put("Z", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byte 2D array to put into the imageware + */ + public void putXY(int x, int y, int z, byte[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (double) (buffer[i][j] & 0xFF); + } + } + } + catch (Exception e) { + throw_put("XY", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * short 2D array to put into the imageware + */ + public void putXY(int x, int y, int z, short[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (double) (buffer[i][j] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_put("XY", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * float 2D array to put into the imageware + */ + public void putXY(int x, int y, int z, float[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (double) (buffer[i][j]); + } + } + } + catch (Exception e) { + throw_put("XY", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * double 2D array to put into the imageware + */ + public void putXY(int x, int y, int z, double[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (double) (buffer[i][j]); + } + } + } + catch (Exception e) { + throw_put("XY", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byte 2D array to put into the imageware + */ + public void putXZ(int x, int y, int z, byte[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + j * nx; + for (int i = 0; i < leni; i++, offset++) { + ((double[]) data[z])[offset] = (double) (buffer[i][j] & 0xFF); + } + } + } + catch (Exception e) { + throw_put("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * short 2D array to put into the imageware + */ + public void putXZ(int x, int y, int z, short[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + j * nx; + for (int i = 0; i < leni; i++, offset++) { + ((double[]) data[z])[offset] = (double) (buffer[i][j] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_put("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * float 2D array to put into the imageware + */ + public void putXZ(int x, int y, int z, float[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + j * nx; + for (int i = 0; i < leni; i++, offset++) { + ((double[]) data[z])[offset] = (double) (buffer[i][j]); + } + } + } + catch (Exception e) { + throw_put("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * double 2D array to put into the imageware + */ + public void putXZ(int x, int y, int z, double[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + j * nx; + for (int i = 0; i < leni; i++, offset++) { + ((double[]) data[z])[offset] = (double) (buffer[i][j]); + } + } + } + catch (Exception e) { + throw_put("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byte 2D array to put into the imageware + */ + public void putYZ(int x, int y, int z, byte[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) + for (int i = 0; i < leni; i++, offset += nx) { + ((double[]) data[z])[offset] = (double) (buffer[i][j] & 0xFF); + } + } + catch (Exception e) { + throw_put("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * short 2D array to put into the imageware + */ + public void putYZ(int x, int y, int z, short[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) + for (int i = 0; i < leni; i++, offset += nx) { + ((double[]) data[z])[offset] = (double) (buffer[i][j] & 0xFFFF); + } + } + catch (Exception e) { + throw_put("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * float 2D array to put into the imageware + */ + public void putYZ(int x, int y, int z, float[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) + for (int i = 0; i < leni; i++, offset += nx) { + ((double[]) data[z])[offset] = (double) (buffer[i][j]); + } + } + catch (Exception e) { + throw_put("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * double 2D array to put into the imageware + */ + public void putYZ(int x, int y, int z, double[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) + for (int i = 0; i < leni; i++, offset += nx) { + ((double[]) data[z])[offset] = (double) (buffer[i][j]); + } + } + catch (Exception e) { + throw_put("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byte 3D array to put into the imageware + */ + public void putXYZ(int x, int y, int z, byte[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (double) (buffer[i][j][k] & 0xFF); + } + } + } + } + catch (Exception e) { + throw_put("XYZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * short 3D array to put into the imageware + */ + public void putXYZ(int x, int y, int z, short[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (double) (buffer[i][j][k] & 0xFFFF); + } + } + } + } + catch (Exception e) { + throw_put("XYZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * float 3D array to put into the imageware + */ + public void putXYZ(int x, int y, int z, float[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (double) (buffer[i][j][k]); + } + } + } + } + catch (Exception e) { + throw_put("XYZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * double 3D array to put into the imageware + */ + public void putXYZ(int x, int y, int z, double[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (double) (buffer[i][j][k]); + } + } + } + } + catch (Exception e) { + throw_put("XYZ", "No check", buffer, x, y, z); + } + } + + // ------------------------------------------------------------------ + // + // get Section + // + // ------------------------------------------------------------------ + + /** + * Get an array from the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the imageware + */ + public void getX(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + double buf[] = new double[bnx]; + getX(x, y, z, buf); + buffer.putX(0, 0, 0, buf); + } + + /** + * Get an array from the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the imageware + */ + public void getY(int x, int y, int z, ImageWare buffer) { + int bny = buffer.getSizeY(); + double buf[] = new double[bny]; + getY(x, y, z, buf); + buffer.putY(0, 0, 0, buf); + } + + /** + * Get an array from the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the imageware + */ + public void getZ(int x, int y, int z, ImageWare buffer) { + int bnz = buffer.getSizeZ(); + double buf[] = new double[bnz]; + getZ(x, y, z, buf); + buffer.putZ(0, 0, 0, buf); + } + + /** + * get an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the imageware + */ + public void getXY(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + int bny = buffer.getSizeY(); + double buf[][] = new double[bnx][bny]; + getXY(x, y, z, buf); + buffer.putXY(0, 0, 0, buf); + } + + /** + * Get an array from the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the imageware + */ + public void getXZ(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + int bnz = buffer.getSizeZ(); + double buf[][] = new double[bnx][bnz]; + getXZ(x, y, z, buf); + buffer.putXZ(0, 0, 0, buf); + } + + /** + * Get an array from the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the datase + */ + public void getYZ(int x, int y, int z, ImageWare buffer) { + int bny = buffer.getSizeY(); + int bnz = buffer.getSizeZ(); + double buf[][] = new double[bny][bnz]; + getYZ(x, y, z, buf); + buffer.putYZ(0, 0, 0, buf); + } + + /** + * Get an array from the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the imageware + */ + public void getXYZ(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + int bny = buffer.getSizeY(); + int bnz = buffer.getSizeZ(); + double buf[][][] = new double[bnx][bny][bnz]; + getXYZ(x, y, z, buf); + buffer.putXYZ(0, 0, 0, buf); + } + + /** + * Get an array from the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 1D array to get into the imageware + */ + public void getX(int x, int y, int z, byte[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + + for (int i = 0; i < leni; i++) { + buffer[i] = (byte) (tmp[offset]); + offset++; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 1D array to get into the imageware + */ + public void getX(int x, int y, int z, short[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + + for (int i = 0; i < leni; i++) { + buffer[i] = (short) (tmp[offset]); + offset++; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 1D array to get into the imageware + */ + public void getX(int x, int y, int z, float[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + + for (int i = 0; i < leni; i++) { + buffer[i] = (float) (tmp[offset]); + offset++; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 1D array to get into the imageware + */ + public void getX(int x, int y, int z, double[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + + System.arraycopy(tmp, offset, buffer, 0, leni); + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 1D array to get into the imageware + */ + public void getY(int x, int y, int z, byte[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + for (int i = 0; i < leni; i++) { + buffer[i] = (byte) (tmp[offset]); + offset += nx; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 1D array to get into the imageware + */ + public void getY(int x, int y, int z, short[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + for (int i = 0; i < leni; i++) { + buffer[i] = (short) (tmp[offset]); + offset += nx; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 1D array to get into the imageware + */ + public void getY(int x, int y, int z, float[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + for (int i = 0; i < leni; i++) { + buffer[i] = (float) (tmp[offset]); + offset += nx; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 1D array to get into the imageware + */ + public void getY(int x, int y, int z, double[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + double[] tmp = (double[]) data[z]; + for (int i = 0; i < leni; i++) { + buffer[i] = (double) (tmp[offset]); + offset += nx; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 1D array to get into the imageware + */ + public void getZ(int x, int y, int z, byte[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + buffer[i] = (byte) (((double[]) data[z])[offset]); + z++; + } + } + catch (Exception e) { + throw_get("Y", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 1D array to get into the imageware + */ + public void getZ(int x, int y, int z, short[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + buffer[i] = (short) (((double[]) data[z])[offset]); + z++; + } + } + catch (Exception e) { + throw_get("Y", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 1D array to get into the imageware + */ + public void getZ(int x, int y, int z, float[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + buffer[i] = (float) (((double[]) data[z])[offset]); + z++; + } + } + catch (Exception e) { + throw_get("Y", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 1D array to get into the imageware + */ + public void getZ(int x, int y, int z, double[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + buffer[i] = (double) (((double[]) data[z])[offset]); + z++; + } + } + catch (Exception e) { + throw_get("Y", "No check", buffer, x, y, z); + } + } + + /** + * get an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 2D array to get into the imageware + */ + public void getXY(int x, int y, int z, byte[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (byte) (tmp[offset]); + } + } + } + catch (Exception e) { + throw_get("XY", "No check", buffer, x, y, z); + } + } + + /** + * get an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 2D array to get into the imageware + */ + public void getXY(int x, int y, int z, short[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (short) (tmp[offset]); + } + } + } + catch (Exception e) { + throw_get("XY", "No check", buffer, x, y, z); + } + } + + /** + * get an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 2D array to get into the imageware + */ + public void getXY(int x, int y, int z, float[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (float) (tmp[offset]); + } + } + } + catch (Exception e) { + throw_get("XY", "No check", buffer, x, y, z); + } + } + + /** + * get an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 2D array to get into the imageware + */ + public void getXY(int x, int y, int z, double[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (double) (tmp[offset]); + } + } + } + catch (Exception e) { + throw_get("XY", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 2D array to get into the imageware + */ + public void getXZ(int x, int y, int z, byte[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + y * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (byte) (((double[]) data[z])[offset]); + } + } + } + catch (Exception e) { + throw_get("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 2D array to get into the imageware + */ + public void getXZ(int x, int y, int z, short[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + y * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (short) (((double[]) data[z])[offset]); + } + } + } + catch (Exception e) { + throw_get("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 2D array to get into the imageware + */ + public void getXZ(int x, int y, int z, float[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + y * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (float) (((double[]) data[z])[offset]); + } + } + } + catch (Exception e) { + throw_get("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 2D array to get into the imageware + */ + public void getXZ(int x, int y, int z, double[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + y * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (double) (((double[]) data[z])[offset]); + } + } + } + catch (Exception e) { + throw_get("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 2D array to get into the datase + */ + public void getYZ(int x, int y, int z, byte[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { + for (int i = 0; i < leni; i++, offset += nx) { + buffer[i][j] = (byte) (((double[]) data[z])[offset]); + } + } + } + catch (Exception e) { + throw_get("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 2D array to get into the datase + */ + public void getYZ(int x, int y, int z, short[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { + for (int i = 0; i < leni; i++, offset += nx) { + buffer[i][j] = (short) (((double[]) data[z])[offset]); + } + } + } + catch (Exception e) { + throw_get("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 2D array to get into the datase + */ + public void getYZ(int x, int y, int z, float[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { + for (int i = 0; i < leni; i++, offset += nx) { + buffer[i][j] = (float) (((double[]) data[z])[offset]); + } + } + } + catch (Exception e) { + throw_get("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 2D array to get into the datase + */ + public void getYZ(int x, int y, int z, double[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { + for (int i = 0; i < leni; i++, offset += nx) { + buffer[i][j] = (double) (((double[]) data[z])[offset]); + } + } + } + catch (Exception e) { + throw_get("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 3D array to get into the imageware + */ + public void getXYZ(int x, int y, int z, byte[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j][k] = (byte) (tmp[offset]); + } + } + } + } + catch (Exception e) { + throw_get("XYZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 3D array to get into the imageware + */ + public void getXYZ(int x, int y, int z, short[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j][k] = (short) (tmp[offset]); + } + } + } + } + catch (Exception e) { + throw_get("XYZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 3D array to get into the imageware + */ + public void getXYZ(int x, int y, int z, float[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j][k] = (float) (tmp[offset]); + } + } + } + } + catch (Exception e) { + throw_get("XYZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 3D array to get into the imageware + */ + public void getXYZ(int x, int y, int z, double[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + double[] tmp = (double[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j][k] = (double) (tmp[offset]); + } + } + } + } + catch (Exception e) { + throw_get("XYZ", "No check", buffer, x, y, z); + } + } + + // ------------------------------------------------------------------ + // + // Private Section + // + // ------------------------------------------------------------------ + + /** + * Prepare a complete error message from the errors coming the constructors. + */ + protected void throw_constructor() { + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a double imageware.\n" + + "-------------------------------------------------------\n"); + } + + /** + * Prepare a complete error message from the errors coming the constructors. + */ + protected void throw_constructor(int nx, int ny, int nz) { + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a double imageware " + nx + "," + ny + + "," + nz + "].\n" + "-------------------------------------------------------\n"); + } + + /** + * Prepare a complete error message from the errors coming the get routines. + */ + protected void throw_get(String direction, String border, Object buffer, int x, int y, int z) { + int leni = 0; + int lenj = 0; + int lenk = 0; + String type = " unknown type"; + if (buffer instanceof byte[]) { + leni = ((byte[]) buffer).length; + type = " 1D byte"; + } + else if (buffer instanceof short[]) { + leni = ((short[]) buffer).length; + type = " 1D short"; + } + else if (buffer instanceof float[]) { + leni = ((float[]) buffer).length; + type = " 1D float"; + } + else if (buffer instanceof double[]) { + leni = ((double[]) buffer).length; + type = " 1D double"; + } + else if (buffer instanceof byte[][]) { + leni = ((byte[][]) buffer).length; + lenj = ((byte[][]) buffer)[0].length; + type = " 2D byte"; + } + else if (buffer instanceof short[][]) { + leni = ((short[][]) buffer).length; + lenj = ((short[][]) buffer)[0].length; + type = " 2D short"; + } + else if (buffer instanceof float[][]) { + leni = ((float[][]) buffer).length; + lenj = ((float[][]) buffer)[0].length; + type = " 2D float"; + } + else if (buffer instanceof double[][]) { + leni = ((double[][]) buffer).length; + lenj = ((double[][]) buffer)[0].length; + type = " 2D double"; + } + else if (buffer instanceof byte[][][]) { + leni = ((byte[][][]) buffer).length; + lenj = ((byte[][][]) buffer)[0].length; + lenk = ((byte[][][]) buffer)[0][0].length; + type = " 3D byte"; + } + else if (buffer instanceof short[][][]) { + leni = ((short[][][]) buffer).length; + lenj = ((short[][][]) buffer)[0].length; + lenk = ((short[][][]) buffer)[0][0].length; + type = " 3D short"; + } + else if (buffer instanceof float[][][]) { + leni = ((float[][][]) buffer).length; + lenj = ((float[][][]) buffer)[0].length; + lenk = ((float[][][]) buffer)[0][0].length; + type = " 3D float"; + } + else if (buffer instanceof double[][][]) { + leni = ((double[][][]) buffer).length; + lenj = ((double[][][]) buffer)[0].length; + lenk = ((double[][][]) buffer)[0][0].length; + type = " 3D double"; + } + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get a" + type + " buffer [" + + (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "from the double imageware [" + nx + "," + ny + "," + nz + + "]\n" + "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + + "-------------------------------------------------------\n"); + } + + /** + * Prepare a complete error message from the errors coming the put routines. + */ + protected void throw_put(String direction, String border, Object buffer, int x, int y, int z) { + int leni = 0; + int lenj = 0; + int lenk = 0; + String type = " unknown type"; + if (buffer instanceof byte[]) { + leni = ((byte[]) buffer).length; + type = " 1D byte"; + } + else if (buffer instanceof short[]) { + leni = ((short[]) buffer).length; + type = " 1D short"; + } + else if (buffer instanceof float[]) { + leni = ((float[]) buffer).length; + type = " 1D float"; + } + else if (buffer instanceof double[]) { + leni = ((double[]) buffer).length; + type = " 1D double"; + } + else if (buffer instanceof byte[][]) { + leni = ((byte[][]) buffer).length; + lenj = ((byte[][]) buffer)[0].length; + type = " 2D byte"; + } + else if (buffer instanceof short[][]) { + leni = ((short[][]) buffer).length; + lenj = ((short[][]) buffer)[0].length; + type = " 2D short"; + } + else if (buffer instanceof float[][]) { + leni = ((float[][]) buffer).length; + lenj = ((float[][]) buffer)[0].length; + type = " 2D float"; + } + else if (buffer instanceof double[][]) { + leni = ((double[][]) buffer).length; + lenj = ((double[][]) buffer)[0].length; + type = " 2D double"; + } + else if (buffer instanceof byte[][][]) { + leni = ((byte[][][]) buffer).length; + lenj = ((byte[][][]) buffer)[0].length; + lenk = ((byte[][][]) buffer)[0][0].length; + type = " 3D byte"; + } + else if (buffer instanceof short[][][]) { + leni = ((short[][][]) buffer).length; + lenj = ((short[][][]) buffer)[0].length; + lenk = ((short[][][]) buffer)[0][0].length; + type = " 3D short"; + } + else if (buffer instanceof float[][][]) { + leni = ((float[][][]) buffer).length; + lenj = ((float[][][]) buffer)[0].length; + lenk = ((float[][][]) buffer)[0][0].length; + type = " 3D float"; + } + else if (buffer instanceof double[][][]) { + leni = ((double[][][]) buffer).length; + lenj = ((double[][][]) buffer)[0].length; + lenk = ((double[][][]) buffer)[0][0].length; + type = " 3D double"; + } + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a" + type + " buffer [" + + (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "into the double imageware [" + nx + "," + ny + "," + nz + + "]\n" + "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + + "-------------------------------------------------------\n"); + } + + // ------------------------------------------------------------------ + // + // Get slice fast and direct access Section + // + // ------------------------------------------------------------------ + + /** + * Get a reference of the whole volume data. + * + * @return a reference of the data of this imageware + */ + public Object[] getVolume() { + return data; + } + + /** + * Get a specific slice, fast and direct access, but only for byte + * imageware. + * + * @param z + * number of the requested slice + * @return a reference of the data of one slice of this imageware + */ + public byte[] getSliceByte(int z) { + return null; + } + + /** + * Get a specific slice, fast and direct access, but only for short + * imageware. + * + * @param z + * number of the requested slice + * @return a reference of the data of one slice of this imageware + */ + public short[] getSliceShort(int z) { + return null; + } + + /** + * Get a specific slice, fast and direct access, but only for float + * imageware. + * + * @param z + * number of the requested slice + * @return a reference of the data of one slice of this imageware + */ + public float[] getSliceFloat(int z) { + return null; + } + + /** + * Get a specific slice, fast and direct access, but only for double + * imageware. + * + * @param z + * number of the requested slice + * @return a reference of the data of one slice of this imageware + */ + public double[] getSliceDouble(int z) { + return (double[]) data[z]; + } + + /** + * Allocate a buffer of size [nx,ny,nz]. + */ + private void allocate() { + try { + this.data = new Object[nz]; + this.nxy = nx * ny; + for (int z = 0; z < nz; z++) + this.data[z] = new double[nxy]; + } + catch (Exception e) { + throw_constructor(nx, ny, nz); + } + } + +} // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/DoublePointwise.java b/src/bilib/src/imageware/DoublePointwise.java new file mode 100644 index 0000000..42f5b06 --- /dev/null +++ b/src/bilib/src/imageware/DoublePointwise.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import ij.process.FloatProcessor; import java.awt.Image; import java.util.Random; /** * Class DoublePointwise. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class DoublePointwise extends DoubleAccess implements Pointwise { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected DoublePointwise(int nx, int ny, int nz) { super(nx, ny, nz); } protected DoublePointwise(Image image, int mode) { super(image, mode); } protected DoublePointwise(ImageStack stack, int mode) { super(stack, mode); } protected DoublePointwise(ImageStack stack, byte chan) { super(stack, chan); } protected DoublePointwise(byte[] array, int mode) { super(array, mode); } protected DoublePointwise(byte[][] array, int mode) { super(array, mode); } protected DoublePointwise(byte[][][] array, int mode) { super(array, mode); } protected DoublePointwise(short[] array, int mode) { super(array, mode); } protected DoublePointwise(short[][] array, int mode) { super(array, mode); } protected DoublePointwise(short[][][] array, int mode) { super(array, mode); } protected DoublePointwise(float[] array, int mode) { super(array, mode); } protected DoublePointwise(float[][] array, int mode) { super(array, mode); } protected DoublePointwise(float[][][] array, int mode) { super(array, mode); } protected DoublePointwise(double[] array, int mode) { super(array, mode); } protected DoublePointwise(double[][] array, int mode) { super(array, mode); } protected DoublePointwise(double[][][] array, int mode) { super(array, mode); } /** * Fill this imageware with a constant value. * * @param value * the constant value */ public void fillConstant(double value) { double typedValue = (double) value; double[] slice = null; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) slice[k] = typedValue; } } /** * Fill this imageware with ramp. */ public void fillRamp() { int off = 0; double[] slice = null; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) slice[k] = (double) (off + k); off += nxy; } } /** * Generate a gaussian noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void fillGaussianNoise(double amplitude) { Random rnd = new Random(); double[] slice = null; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (double) ((rnd.nextGaussian()) * amplitude); } } } /** * Generate a uniform noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void fillUniformNoise(double amplitude) { Random rnd = new Random(); double[] slice = null; amplitude *= 2.0; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (double) ((rnd.nextDouble() - 0.5) * amplitude); } } } /** * Generate a salt and pepper noise. * * @param amplitudeSalt * amplitude of the salt noise * @param amplitudePepper * amplitude of the pepper noise * @param percentageSalt * percentage of the salt noise * @param percentagePepper * percentage of the pepper noise */ public void fillSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) { Random rnd = new Random(); int index, z; if (percentageSalt > 0) { double nbSalt = nxy * nz / percentageSalt; for (int k = 0; k < nbSalt; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((double[]) data[z])[index] = (double) (rnd.nextDouble() * amplitudeSalt); } } if (percentagePepper > 0) { double nbPepper = nxy * nz / percentagePepper; for (int k = 0; k < nbPepper; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((double[]) data[z])[index] = (double) (-rnd.nextDouble() * amplitudeSalt); } } } /** * Build an ImageStack of ImageJ. */ public ImageStack buildImageStack() { ImageStack imagestack = new ImageStack(nx, ny); for (int z = 0; z < nz; z++) { FloatProcessor ip = new FloatProcessor(nx, ny); float pix[] = (float[]) ip.getPixels(); for (int k = 0; k < nxy; k++) pix[k] = (float) (((double[]) data[z])[k]); imagestack.addSlice("" + z, ip); } return imagestack; } /** * Invert the pixel intensity. */ public void invert() { double max = -Double.MAX_VALUE; double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k]) > max) max = slice[k]; } } for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (double) (max - ((double) (slice[k]))); } } } /** * Negate the pixel intensity. */ public void negate() { double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (double) (-((double) (slice[k]))); } } } /** * Clip the pixel intensity into [0..255]. */ public void clip() { clip(0.0, 255.0); } /** * Clip the pixel intensity into [minLevel..maxLevel]. * * @param minLevel * double value given the threshold * @param maxLevel * double value given the threshold */ public void clip(double minLevel, double maxLevel) { double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; double value; double min = (double) minLevel; double max = (double) maxLevel; for (int k = 0; k < nxy; k++) { value = (double) (slice[k]); if (value < min) slice[k] = min; if (value > max) slice[k] = max; } } } /** * Rescale the pixel intensity into [0..255]. */ public void rescale() { double maxImage = -Double.MAX_VALUE; double minImage = Double.MAX_VALUE; double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k]) > maxImage) maxImage = slice[k]; if ((slice[k]) < minImage) minImage = slice[k]; } } double a; if (minImage - maxImage == 0) { a = 1.0; minImage = 128.0; } else { a = 255.0 / (maxImage - minImage); } for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (double) (a * (((double) (slice[k])) - minImage)); } } } /** * Rescale the pixel intensity into [minLevel..maxLevel]. * * @param minLevel * double value given the threshold * @param maxLevel * double value given the threshold */ public void rescale(double minLevel, double maxLevel) { double maxImage = -Double.MAX_VALUE; double minImage = Double.MAX_VALUE; double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k]) > maxImage) maxImage = slice[k]; if ((slice[k]) < minImage) minImage = slice[k]; } } double a; if (minImage - maxImage == 0) { a = 1.0; minImage = (maxLevel - minLevel) / 2.0; } else { a = (maxLevel - minLevel) / (maxImage - minImage); } for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (double) (a * (((double) (slice[k])) - minImage) + minLevel); } } } /** * Rescale the pixel intensity with a linear curve passing through * (maxLevel-minLevel)/2 at the 0 input intensity. * * @param minLevel * double value given the threshold * @param maxLevel * double value given the threshold */ public void rescaleCenter(double minLevel, double maxLevel) { double maxImage = -Double.MAX_VALUE; double minImage = Double.MAX_VALUE; double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k]) > maxImage) maxImage = slice[k]; if ((slice[k]) < minImage) minImage = slice[k]; } } double center = (maxLevel + minLevel) / 2.0; double a; if (minImage - maxImage == 0) { a = 1.0; minImage = (maxLevel - minLevel) / 2.0; } else { if (Math.abs(maxImage) > Math.abs(minImage)) a = (maxLevel - center) / Math.abs(maxImage); else a = (center - minLevel) / Math.abs(minImage); } for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (double) (a * (((double) (slice[k])) - minImage) + center); } } } /** * Compute the absolute value of this imageware. */ public void abs() { double zero = (double) 0.0; double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { if (slice[k] < zero) slice[k] = -slice[k]; } } } /** * Compute the log of this imageware. */ public void log() { double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (double) Math.log(slice[k]); } } } /** * Compute the exponential of this imageware. */ public void exp() { double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (double) Math.exp(slice[k]); } } } /** * Compute the square root of this imageware. */ public void sqrt() { double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (double) Math.sqrt(slice[k]); } } } /** * Compute the square of this imageware. */ public void sqr() { double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] *= slice[k]; } } } /** * Compute the power of a of this imageware. * * @param a * exponent */ public void pow(double a) { double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (double) Math.pow(slice[k], a); } } } /** * Add a constant value to this imageware. */ public void add(double constant) { double cst = (double) constant; double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] += cst; } } } /** * Multiply a constant value to this imageware. * * @param constant * the constant value */ public void multiply(double constant) { double cst = (double) constant; double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] *= cst; } } } /** * Subtract a constant value to this imageware. * * @param constant * the constant value */ public void subtract(double constant) { double cst = (double) constant; double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] -= cst; } } } /** * Divide by a constant value to this imageware. * * @param constant * the constant value */ public void divide(double constant) { if (constant == 0.0) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to divide because the constant is 0.\n" + "-------------------------------------------------------\n"); double cst = (double) constant; double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] /= cst; } } } /** * Threshold a imageware in two levels 0 and 255. * * All the pixels values strictly greater than 'thresholdValue' and are set * to 0. The remaining values are set to 255. * * @param thresholdValue * double value given the threshold */ public void threshold(double thresholdValue) { threshold(thresholdValue, 0.0, 255.0); } /** * Threshold a imageware in two levels minLevel and maxLevel. * * All the pixels values strictly greater than 'thresholdValue' and are set * to maxLevel. The remaining values are set to minLevel. * * @param thresholdValue * double value given the threshold * @param minLevel * double value given the minimum level * @param maxLevel * double value given the maximum level */ public void threshold(double thresholdValue, double minLevel, double maxLevel) { double low = (double) (minLevel); double high = (double) (maxLevel); double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = ((double) (slice[k]) > thresholdValue ? high : low); } } } /** * Apply a soft thresholding. * * All the pixels values strictly greater than '-thresholdValue' and stricty * lower than 'thresholdValue' set to 0. The remaining positive values are * reduced by 'thresholdvalue'; the remaining negative values are augmented * by 'thresholdValue'. * * @param thresholdValue * double value given the threshold */ public void thresholdSoft(double thresholdValue) { double zero = (double) (0.0); double pixel; double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { pixel = (double) (slice[k]); slice[k] = (pixel <= -thresholdValue ? (double) (pixel + thresholdValue) : (pixel > thresholdValue ? (double) (pixel - thresholdValue) : zero)); } } } /** * Apply a hard thresholding. * * All the pixels values strictly greater than '-thresholdValue' and stricty * lower than 'thresholdValue' are set to 0. The remaining values are * unchanged. * * @param thresholdValue * double value given the threshold */ public void thresholdHard(double thresholdValue) { double zero = (double) (0.0); double pixel; double[] slice; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { pixel = (double) (slice[k]); if (pixel > -thresholdValue && pixel < thresholdValue) slice[k] = zero; } } } /** * Add a gaussian noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void addGaussianNoise(double amplitude) { Random rnd = new Random(); double[] slice = null; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] += (double) ((rnd.nextGaussian()) * amplitude); } } } /** * Add a uniform noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void addUniformNoise(double amplitude) { Random rnd = new Random(); double[] slice = null; amplitude *= 2.0; for (int z = 0; z < nz; z++) { slice = (double[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] += (double) ((rnd.nextDouble() - 0.5) * amplitude); } } } /** * Add a salt and pepper noise. * * @param amplitudeSalt * amplitude of the salt noise * @param amplitudePepper * amplitude of the pepper noise * @param percentageSalt * percentage of the salt noise * @param percentagePepper * percentage of the pepper noise */ public void addSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) { Random rnd = new Random(); int index, z; if (percentageSalt > 0) { double nbSalt = nxy * nz / percentageSalt; for (int k = 0; k < nbSalt; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((double[]) data[z])[index] += (double) (rnd.nextDouble() * amplitudeSalt); } } if (percentagePepper > 0) { double nbPepper = nxy * nz / percentagePepper; for (int k = 0; k < nbPepper; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((double[]) data[z])[index] -= (double) (rnd.nextDouble() * amplitudeSalt); } } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/DoubleProcess.java b/src/bilib/src/imageware/DoubleProcess.java new file mode 100644 index 0000000..412d139 --- /dev/null +++ b/src/bilib/src/imageware/DoubleProcess.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import java.awt.Image; /** * Class DoubleProcess. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class DoubleProcess extends DoublePointwise implements Process { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected DoubleProcess(int nx, int ny, int nz) { super(nx, ny, nz); } protected DoubleProcess(Image image, int mode) { super(image, mode); } protected DoubleProcess(ImageStack stack, int mode) { super(stack, mode); } protected DoubleProcess(ImageStack stack, byte chan) { super(stack, chan); } protected DoubleProcess(byte[] array, int mode) { super(array, mode); } protected DoubleProcess(byte[][] array, int mode) { super(array, mode); } protected DoubleProcess(byte[][][] array, int mode) { super(array, mode); } protected DoubleProcess(short[] array, int mode) { super(array, mode); } protected DoubleProcess(short[][] array, int mode) { super(array, mode); } protected DoubleProcess(short[][][] array, int mode) { super(array, mode); } protected DoubleProcess(float[] array, int mode) { super(array, mode); } protected DoubleProcess(float[][] array, int mode) { super(array, mode); } protected DoubleProcess(float[][][] array, int mode) { super(array, mode); } protected DoubleProcess(double[] array, int mode) { super(array, mode); } protected DoubleProcess(double[][] array, int mode) { super(array, mode); } protected DoubleProcess(double[][][] array, int mode) { super(array, mode); } /** * Apply a separable gaussian smoothing over the image with the same * strengthness in all directions. To have a smmothing effect the * strengthness should be strictly greater than 0 and the size in the * considered directions should be greater strictly than 1. * * @param sigma * Strengthness of the smoothing */ public void smoothGaussian(double sigma) { smoothGaussian(sigma, sigma, sigma); } /** * Apply a separablegaussian smoothing over the image with an independant * strengthness in the different directions. To have a smmothing effect the * strengthness should be strictly greater than 0 and the size in the * considered directions should be greater strictly than 1. * * @param sigmaX * Strengthness of the smoothing in X axis * @param sigmaY * Strengthness of the smoothing in X axis * @param sigmaZ * Strengthness of the smoothing in X axis */ public void smoothGaussian(double sigmaX, double sigmaY, double sigmaZ) { int n = 3; double N = (double) n; double poles[] = new double[n]; if (nx > 1 && sigmaX > 0.0) { double s2 = sigmaX * sigmaX; double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2); poles[0] = poles[1] = poles[2] = alpha; double line[] = new double[nx]; for (int z = 0; z < nz; z++) { for (int y = 0; y < ny; y++) { getX(0, y, z, line); putX(0, y, z, Convolver.convolveIIR(line, poles)); } } } if (ny > 1 && sigmaY > 0.0) { double s2 = sigmaY * sigmaY; double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2); poles[0] = poles[1] = poles[2] = alpha; double line[] = new double[ny]; for (int x = 0; x < nx; x++) { for (int z = 0; z < nz; z++) { getY(x, 0, z, line); putY(x, 0, z, Convolver.convolveIIR(line, poles)); } } } if (nz > 1 && sigmaZ > 0.0) { double s2 = sigmaZ * sigmaZ; double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2); poles[0] = poles[1] = poles[2] = alpha; double line[] = new double[nz]; for (int y = 0; y < ny; y++) { for (int x = 0; x < nx; x++) { getZ(x, y, 0, line); putZ(x, y, 0, Convolver.convolveIIR(line, poles)); } } } } /** * Get the maximum of this imageware and a imageware. * * @param imageware * imageware to max */ public void max(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get the maximum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { if (((double[]) data[z])[k] < (double) tmp[k]) ((double[]) data[z])[k] = (double) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { if (((double[]) data[z])[k] < (double) tmp[k]) ((double[]) data[z])[k] = (double) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { if (((double[]) data[z])[k] < (double) tmp[k]) ((double[]) data[z])[k] = (double) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { if (((double[]) data[z])[k] < (double) tmp[k]) ((double[]) data[z])[k] = (double) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Get the minimum of this imageware and a imageware. * * @param imageware * imageware to min */ public void min(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get the minimum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { if (((double[]) data[z])[k] > (double) tmp[k]) ((double[]) data[z])[k] = (double) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { if (((double[]) data[z])[k] > (double) tmp[k]) ((double[]) data[z])[k] = (double) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { if (((double[]) data[z])[k] > (double) tmp[k]) ((double[]) data[z])[k] = (double) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { if (((double[]) data[z])[k] > (double) tmp[k]) ((double[]) data[z])[k] = (double) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Add a imageware to the current imageware. * * @param imageware * imageware to add */ public void add(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to add because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] += (double) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] += (double) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] += (double) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] += (double) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Multiply a imageware to the current imageware. * * @param imageware * imageware to multiply */ public void multiply(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to multiply because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] *= (double) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] *= (double) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] *= (double) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] *= (double) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Subtract a imageware to the current imageware. * * @param imageware * imageware to subtract */ public void subtract(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to subtract because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] -= (double) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] -= (double) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] -= (double) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] -= (double) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Divide a imageware to the current imageware. * * @param imageware * imageware to divide */ public void divide(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to divide because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] /= (double) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] /= (double) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] /= (double) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((double[]) data[z])[k] /= (double) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/DoubleSet.java b/src/bilib/src/imageware/DoubleSet.java new file mode 100644 index 0000000..2d9ed25 --- /dev/null +++ b/src/bilib/src/imageware/DoubleSet.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import java.awt.Image; /** * Class DoubleSet. * * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class DoubleSet extends DoubleProcess implements ImageWare { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected DoubleSet(int nx, int ny, int nz) { super(nx, ny, nz); } protected DoubleSet(Image image, int mode) { super(image, mode); } protected DoubleSet(ImageStack stack, int mode) { super(stack, mode); } protected DoubleSet(ImageStack stack, byte chan) { super(stack, chan); } protected DoubleSet(byte[] array, int mode) { super(array, mode); } protected DoubleSet(byte[][] array, int mode) { super(array, mode); } protected DoubleSet(byte[][][] array, int mode) { super(array, mode); } protected DoubleSet(short[] array, int mode) { super(array, mode); } protected DoubleSet(short[][] array, int mode) { super(array, mode); } protected DoubleSet(short[][][] array, int mode) { super(array, mode); } protected DoubleSet(float[] array, int mode) { super(array, mode); } protected DoubleSet(float[][] array, int mode) { super(array, mode); } protected DoubleSet(float[][][] array, int mode) { super(array, mode); } protected DoubleSet(double[] array, int mode) { super(array, mode); } protected DoubleSet(double[][] array, int mode) { super(array, mode); } protected DoubleSet(double[][][] array, int mode) { super(array, mode); } /** * Duplicate the imageware. * * Create a new imageware with the same size, same type and same data than * the calling one. * * @return a duplicated version of this imageware */ public ImageWare duplicate() { ImageWare out = new DoubleSet(nx, ny, nz); double[] outdata; for (int z = 0; z < nz; z++) { outdata = (double[]) (((DoubleSet) out).data[z]); System.arraycopy(data[z], 0, outdata, 0, nxy); } return out; } /** * Replicate the imageware. * * Create a new imageware with the same size, same type than the calling * one. The data are not copied. * * @return a replicated version of this imageware */ public ImageWare replicate() { return new DoubleSet(nx, ny, nz); } /** * Replicate the imageware. * * Create a new imageware with the same size and a specified type than the * calling one. The data are not copied. * * @param type * requested type * @return a replicated version of this imageware */ public ImageWare replicate(int type) { switch (type) { case ImageWare.BYTE: return new ByteSet(nx, ny, nz); case ImageWare.SHORT: return new ShortSet(nx, ny, nz); case ImageWare.FLOAT: return new FloatSet(nx, ny, nz); case ImageWare.DOUBLE: return new DoubleSet(nx, ny, nz); default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n" + "-------------------------------------------------------\n"); } } /** * Copy all the data of source in the current imageware. The source should * have the same size and same type than the calling one. * * @param source * a source imageware */ public void copy(ImageWare source) { if (nx != source.getSizeX()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size (" + nx + " != " + source.getSizeX() + ").\n" + "-------------------------------------------------------\n"); if (ny != source.getSizeY()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size (" + ny + " != " + source.getSizeY() + ").\n" + "-------------------------------------------------------\n"); if (nz != source.getSizeZ()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size (" + nz + " != " + source.getSizeZ() + ").\n" + "-------------------------------------------------------\n"); if (getType() != source.getType()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same type (" + getType() + " != " + source.getType() + ").\n" + "-------------------------------------------------------\n"); double[] src; for (int z = 0; z < nz; z++) { src = (double[]) (((DoubleSet) source).data[z]); System.arraycopy(src, 0, data[z], 0, nxy); } } /** * convert the imageware in a specified type. * * Create a new imageware with the same size and converted data than the * calling one. * * @param type * indicates the type of the output * @return a converted version of this imageware */ public ImageWare convert(int type) { if (type == ImageWare.DOUBLE) return duplicate(); ImageWare out = null; switch (type) { case ImageWare.BYTE: { double[] slice; out = new ByteSet(nx, ny, nz); byte[] outslice; for (int z = 0; z < nz; z++) { slice = ((double[]) data[z]); outslice = ((byte[]) ((ByteSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (byte) (slice[k]); } } } break; case ImageWare.SHORT: { double[] slice; out = new ShortSet(nx, ny, nz); short[] outslice; for (int z = 0; z < nz; z++) { slice = ((double[]) data[z]); outslice = ((short[]) ((ShortSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (short) (slice[k]); } } } break; case ImageWare.FLOAT: { double[] slice; out = new FloatSet(nx, ny, nz); float[] outslice; for (int z = 0; z < nz; z++) { slice = ((double[]) data[z]); outslice = ((float[]) ((FloatSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (float) (slice[k]); } } } break; case ImageWare.DOUBLE: { double[] slice; out = new DoubleSet(nx, ny, nz); double[] outslice; for (int z = 0; z < nz; z++) { slice = ((double[]) data[z]); outslice = ((double[]) ((DoubleSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (double) (slice[k]); } } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n" + "-------------------------------------------------------\n"); } return out; } /** * Print information of this ImageWare object. */ public void printInfo() { System.out.println("ImageWare object information"); System.out.println("Dimension: " + getDimension()); System.out.println("Size: [" + nx + ", " + ny + ", " + nz + "]"); System.out.println("TotalSize: " + getTotalSize()); System.out.println("Type: " + getTypeToString()); System.out.println("Maximun: " + getMaximum()); System.out.println("Minimun: " + getMinimum()); System.out.println("Mean: " + getMean()); System.out.println("Norm1: " + getNorm1()); System.out.println("Norm2: " + getNorm2()); System.out.println("Total: " + getTotal()); System.out.println(""); } /** * Show this ImageWare object. */ public void show() { String title = getTypeToString(); switch (getDimension()) { case 1: title += " line"; break; case 2: title += " image"; break; case 3: title += " volume"; break; } Display.show(title, this); // ImagePlus imp = new ImagePlus(title, buildImageStack()); // imp.show(); } /** * Show the data in ImagePlus object with a specify title. * * @param title * a string given the title of the window */ public void show(String title) { Display.show(title, this); // ImagePlus imp = new ImagePlus(title, buildImageStack()); // imp.show(); } /** * Return the minimum value of this imageware. * * @return the min value of this imageware */ public double getMinimum() { double min = Double.MAX_VALUE; double[] slice; for (int z = 0; z < nz; z++) { slice = ((double[]) data[z]); for (int k = 0; k < nxy; k++) if ((slice[k]) < min) min = slice[k]; } return min; } /** * Return the maximum value of this imageware. * * @return the max value of this imageware */ public double getMaximum() { double max = -Double.MAX_VALUE; double[] slice; for (int z = 0; z < nz; z++) { slice = ((double[]) data[z]); for (int k = 0; k < nxy; k++) if ((slice[k]) > max) max = slice[k]; } return max; } /** * Return the mean value of this imageware. * * @return the mean value of this imageware */ public double getMean() { return getTotal() / (nz * nxy); } /** * Return the norm value of order 1. * * @return the norm value of this imageware in L1 sense */ public double getNorm1() { double norm = 0.0; double value = 0; double[] slice; for (int z = 0; z < nz; z++) { slice = ((double[]) data[z]); for (int k = 0; k < nxy; k++) { value = (double) (slice[k]); norm += (value > 0.0 ? value : -value); } } return norm; } /** * Return the norm value of order 2. * * @return the norm value of this imageware in L2 sense */ public double getNorm2() { double norm = 0.0; double[] slice; for (int z = 0; z < nz; z++) { slice = ((double[]) data[z]); for (int k = 0; k < nxy; k++) norm += (slice[k]) * (slice[k]); } return norm; } /** * Return the sum of all pixel in this imageware. * * @return the total sum of all pixel in this imageware */ public double getTotal() { double total = 0.0; double[] slice; for (int z = 0; z < nz; z++) { slice = ((double[]) data[z]); for (int k = 0; k < nxy; k++) total += slice[k]; } return total; } /** * Return the the minumum [0] and the maximum [1] value of this imageware. * Faster routine than call one getMinimum() and then one getMaximum(). * * @return an array of two values, the min and the max values of the images */ public double[] getMinMax() { double max = -Double.MAX_VALUE; double min = Double.MAX_VALUE; double[] slice; for (int z = 0; z < nz; z++) { slice = ((double[]) data[z]); for (int k = 0; k < nxy; k++) { if ((slice[k]) > max) max = slice[k]; if ((slice[k]) < min) min = slice[k]; } } double minmax[] = { min, max }; return minmax; } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/FMath.java b/src/bilib/src/imageware/FMath.java new file mode 100644 index 0000000..ae00013 --- /dev/null +++ b/src/bilib/src/imageware/FMath.java @@ -0,0 +1 @@ +package imageware; /** * Class FMath. The <b>FMath</b> class provides methods for carrying out a * number of elementary mathematical operations.<br> * <br> * * @author Erik Meijering Biomedical Imaging Group Swiss Federal Institute of * Technology Lausanne EPFL, CH-1015 Lausanne, Switzerland */ public final class FMath { /** * Returns the largest integral value that is not greater than the argument. * If the argument value is already equal to a mathematical integer, then * the result is the same as the argument. Note that the method works only * for <b>float</b> values that fall within the range spanned by the * integers. In that case, this method gives the same result as the * corresponding method in Java's <b>Math</b> class, but is in general much * faster. */ public static int floor(final float f) { if (f >= 0.0f) return (int) f; else { final int iAdd = (int) f - 1; return (((int) (f - iAdd)) + iAdd); } } /** * Returns the largest integral value that is not greater than the argument. * If the argument value is already equal to a mathematical integer, then * the result is the same as the argument. Note that the method works only * for <b>double</b> values that fall within the range spanned by the * integers. In that case, this method gives the same result as the * corresponding method in Java's <b>Math</b> class, but is in general much * faster. */ public static int floor(final double d) { if (d >= 0.0) return (int) d; else { final int iAdd = (int) d - 1; return (((int) (d - iAdd)) + iAdd); } } /** * Returns the smallest integral value that is not less than the argument. * If the argument value is already equal to a mathematical integer, then * the result is the same as the argument. Note that the method works only * for <b>float</b> values that fall within the range spanned by the * integers. In that case, this method gives the same result as the * corresponding method in Java's <b>Math</b> class, but is in general much * faster. */ public static int ceil(final float f) { final float mf = -f; if (mf >= 0.0f) return -((int) mf); else { final int iAdd = (int) mf - 1; return -(((int) (mf - iAdd)) + iAdd); } } /** * Returns the smallest integral value that is not less than the argument. * If the argument value is already equal to a mathematical integer, then * the result is the same as the argument. Note that the method works only * for <b>double</b> values that fall within the range spanned by the * integers. In that case, this method gives the same result as the * corresponding method in Java's <b>Math</b> class, but is in general much * faster. */ public static int ceil(final double d) { final double md = -d; if (md >= 0.0) return -((int) md); else { final int iAdd = (int) md - 1; return -(((int) (md - iAdd)) + iAdd); } } /** * Returns the integral value closest to the argument. Note that the method * works only for <b>float</b> values that fall within the range spanned by * the integers. In that case, this method gives the same result as the * corresponding method in Java's <b>Math</b> class, but is in general much * faster. */ public static int round(final float f) { final float f05 = f + 0.5f; if (f05 >= 0.0) return (int) f05; else { final int iAdd = (int) f05 - 1; return (((int) (f05 - iAdd)) + iAdd); } } /** * Returns the integral value closest to the argument. Note that the method * works only for <b>double</b> values that fall within the range spanned by * the integers. In that case, this method gives the same result as the * corresponding method in Java's <b>Math</b> class, but is in general much * faster. */ public static int round(final double d) { final double d05 = d + 0.5; if (d05 >= 0.0) return (int) d05; else { final int iAdd = (int) d05 - 1; return (((int) (d05 - iAdd)) + iAdd); } } /** Returns the minimum value of the two arguments. */ public static float min(final float f1, final float f2) { return ((f1 < f2) ? f1 : f2); } /** Returns the minimum value of the two arguments. */ public static double min(final double d1, final double d2) { return ((d1 < d2) ? d1 : d2); } /** Returns the minimum value of the two arguments. */ public static float max(final float f1, final float f2) { return ((f1 > f2) ? f1 : f2); } /** Returns the maximum value of the two arguments. */ public static double max(final double d1, final double d2) { return ((d1 > d2) ? d1 : d2); } } \ No newline at end of file diff --git a/src/bilib/src/imageware/FloatAccess.java b/src/bilib/src/imageware/FloatAccess.java new file mode 100644 index 0000000..16823a8 --- /dev/null +++ b/src/bilib/src/imageware/FloatAccess.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import java.awt.Image; /** * Class FloatAccess. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class FloatAccess extends FloatBuffer implements Access { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected FloatAccess(int nx, int ny, int nz) { super(nx, ny, nz); } protected FloatAccess(Image image, int mode) { super(image, mode); } protected FloatAccess(ImageStack stack, int mode) { super(stack, mode); } protected FloatAccess(ImageStack stack, byte chan) { super(stack, chan); } protected FloatAccess(byte[] array, int mode) { super(array, mode); } protected FloatAccess(byte[][] array, int mode) { super(array, mode); } protected FloatAccess(byte[][][] array, int mode) { super(array, mode); } protected FloatAccess(short[] array, int mode) { super(array, mode); } protected FloatAccess(short[][] array, int mode) { super(array, mode); } protected FloatAccess(short[][][] array, int mode) { super(array, mode); } protected FloatAccess(float[] array, int mode) { super(array, mode); } protected FloatAccess(float[][] array, int mode) { super(array, mode); } protected FloatAccess(float[][][] array, int mode) { super(array, mode); } protected FloatAccess(double[] array, int mode) { super(array, mode); } protected FloatAccess(double[][] array, int mode) { super(array, mode); } protected FloatAccess(double[][][] array, int mode) { super(array, mode); } // ------------------------------------------------------------------ // // getPixel section // // ------------------------------------------------------------------ /** * Get a pixel at specific position without specific boundary conditions * * If the positions is outside of this imageware, the method return 0.0. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @return a pixel value */ public double getPixel(int x, int y, int z) { if (x >= nx) return 0.0; if (y >= ny) return 0.0; if (z >= nz) return 0.0; if (x < 0) return 0.0; if (y < 0) return 0.0; if (z < 0) return 0.0; return ((float[]) data[z])[x + y * nx]; } /** * Get a pixel at specific position with specific boundary conditions * * If the positions is outside of this imageware, the method apply the * boundary conditions to return a value. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @return a pixel value */ public double getPixel(int x, int y, int z, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a pixel \n" + "at the position (" + x + "," + y + "," + z + ".\n" + "-------------------------------------------------------\n"); } int xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } return ((float[]) data[zp])[xp + yp * nx]; } /** * Get a interpolated pixel value at specific position without specific * boundary conditions. * * If the positions is not on the pixel grid, the method return a * interpolated value of the pixel (linear interpolation). If the positions * is outside of this imageware, the method return 0.0. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @return a interpolated value */ public double getInterpolatedPixel(double x, double y, double z) { if (x > nx - 1) return 0.0; if (y > ny - 1) return 0.0; if (z > nz - 1) return 0.0; if (x < 0) return 0.0; if (y < 0) return 0.0; if (z < 0) return 0.0; double output = 0.0; /* * int i = (x >= 0.0 ? ((int)x) : ((int)x - 1)); int j = (y >= 0.0 ? * ((int)y) : ((int)y - 1)); int k = (z >= 0.0 ? ((int)z) : ((int)z - * 1)); */ int i = (x >= 0.0 ? ((int) x) : ((int) x - 1)); int j = (y >= 0.0 ? ((int) y) : ((int) y - 1)); int k = (z >= 0.0 ? ((int) z) : ((int) z - 1)); boolean fi = (i == nx - 1); boolean fj = (j == ny - 1); boolean fk = (k == nz - 1); int index = i + j * nx; switch (getDimension()) { case 1: double v1_0 = (double) (((float[]) data[k])[index]); double v1_1 = (fi ? v1_0 : (double) (((float[]) data[k])[index + 1])); double dx1 = x - (double) i; return v1_1 * dx1 - v1_0 * (dx1 - 1.0); case 2: double v2_00 = (double) (((float[]) data[k])[index]); double v2_10 = (fi ? v2_00 : (double) (((float[]) data[k])[index + 1])); double v2_01 = (fj ? v2_00 : (double) (((float[]) data[k])[index + nx])); double v2_11 = (fi ? (fj ? v2_00 : v2_01) : (double) (((float[]) data[k])[index + 1 + nx])); double dx2 = x - (double) i; double dy2 = y - (double) j; return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0))); case 3: double v3_000 = (double) (((float[]) data[k])[index]); double v3_100 = (fi ? v3_000 : (double) (((float[]) data[k])[index + 1])); double v3_010 = (fj ? v3_000 : (double) (((float[]) data[k])[index + nx])); double v3_110 = (fi ? (fj ? v3_000 : v3_010) : (double) (((float[]) data[k])[index + 1 + nx])); double v3_001 = (fk ? v3_000 : (double) (((float[]) data[k + 1])[index])); double v3_011 = (fk ? (fj ? v3_000 : v3_010) : (double) (((float[]) data[k + 1])[index + 1])); double v3_101 = (fk ? (fi ? v3_000 : v3_100) : (double) (((float[]) data[k + 1])[index + nx])); double v3_111 = (fk ? (fj ? (fi ? v3_000 : v3_100) : v3_110) : (double) (((float[]) data[k + 1])[index + 1 + nx])); double dx3 = x - (double) i; double dy3 = y - (double) j; double dz3 = z - (double) k; double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0))); double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0))); return z2 * dz3 - z1 * (dz3 - 1.0); } return output; } /** * Get a interpolated pixel value at specific position with specific * boundary conditions. * * If the positions is not on the pixel grid, the method return a * interpolated value of the pixel (linear interpolation). If the positions * is outside of this imageware, the method apply the boundary conditions to * return a value. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @param boundaryConditions * MIRROR or PERIODIC boundary conditions * @return a interpolated value */ public double getInterpolatedPixel(double x, double y, double z, byte boundaryConditions) { double output = 0.0; int i = (x >= 0.0 ? ((int) x) : ((int) x - 1)); int j = (y >= 0.0 ? ((int) y) : ((int) y - 1)); int k = (z >= 0.0 ? ((int) z) : ((int) z - 1)); switch (getDimension()) { case 1: double v1_0 = getPixel(i, j, k, boundaryConditions); double v1_1 = getPixel(i + 1, j, k, boundaryConditions); double dx1 = x - (double) i; return v1_1 * dx1 - v1_0 * (dx1 - 1.0); case 2: double v2_00 = getPixel(i, j, k, boundaryConditions); double v2_10 = getPixel(i + 1, j, k, boundaryConditions); double v2_01 = getPixel(i, j + 1, k, boundaryConditions); double v2_11 = getPixel(i + 1, j + 1, k, boundaryConditions); double dx2 = x - (double) i; double dy2 = y - (double) j; return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0))); case 3: double v3_000 = getPixel(i, j, k, boundaryConditions); double v3_100 = getPixel(i + 1, j, k, boundaryConditions); double v3_010 = getPixel(i, j + 1, k, boundaryConditions); double v3_110 = getPixel(i + 1, j + 1, k, boundaryConditions); double v3_001 = getPixel(i, j, k + 1, boundaryConditions); double v3_011 = getPixel(i + 1, j, k + 1, boundaryConditions); double v3_101 = getPixel(i, j + 1, k + 1, boundaryConditions); double v3_111 = getPixel(i + 1, j + 1, k + 1, boundaryConditions); double dx3 = x - (double) i; double dy3 = y - (double) j; double dz3 = z - (double) k; double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0))); double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0))); return z2 * dz3 - z1 * (dz3 - 1.0); } return output; } // ------------------------------------------------------------------ // // putPixel section // // ------------------------------------------------------------------ /** * Put a pixel at specific position * * If the positions is outside of this imageware, the method does nothing. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis */ public void putPixel(int x, int y, int z, double value) { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; if (x < 0) return; if (y < 0) return; if (z < 0) return; ((float[]) data[z])[x + y * nx] = (float) value; } // ------------------------------------------------------------------ // // getBounded section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (byte) (tmp[offset]); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (short) (tmp[offset]); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (float) (tmp[offset]); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (double) (tmp[offset]); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (byte) (tmp[offset]); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (short) (tmp[offset]); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (float) (tmp[offset]); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (double) (tmp[offset]); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (byte) (((float[]) data[k])[offset]); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (short) (((float[]) data[k])[offset]); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (float) (((float[]) data[k])[offset]); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (double) (((float[]) data[k])[offset]); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); float[] tmp = (float[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (byte) (tmp[offset]); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); float[] tmp = (float[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (short) (tmp[offset]); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); float[] tmp = (float[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (float) (tmp[offset]); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); float[] tmp = (float[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (double) (tmp[offset]); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (byte) (((float[]) data[z])[offset]); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (short) (((float[]) data[z])[offset]); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (float) (((float[]) data[z])[offset]); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (double) (((float[]) data[z])[offset]); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (byte) (((float[]) data[z])[offset]); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (short) (((float[]) data[z])[offset]); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (float) (((float[]) data[z])[offset]); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (double) (((float[]) data[z])[offset]); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, byte[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { float[] tmp = (float[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (byte) (tmp[offset]); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, short[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { float[] tmp = (float[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (short) (tmp[offset]); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, float[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { float[] tmp = (float[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (float) (tmp[offset]); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, double[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { float[] tmp = (float[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (double) (tmp[offset]); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } // ------------------------------------------------------------------ // // getBlock section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; float[] tmp = (float[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (byte) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; float[] tmp = (float[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (short) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; float[] tmp = (float[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (float) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; float[] tmp = (float[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (double) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } float[] tmp = (float[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (byte) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } float[] tmp = (float[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (short) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } float[] tmp = (float[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (float) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } float[] tmp = (float[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (double) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (byte) (((float[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (short) (((float[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (float) (((float[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (double) (((float[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } float[] tmp = (float[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } float[] tmp = (float[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } float[] tmp = (float[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } float[] tmp = (float[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (((float[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (((float[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (((float[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (((float[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (byte) (((float[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (short) (((float[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (float) (((float[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (double) (((float[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } float[] tmp = (float[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (byte) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } float[] tmp = (float[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (short) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } float[] tmp = (float[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (float) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } float[] tmp = (float[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (double) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } // ------------------------------------------------------------------ // // getBlock section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; float[] tmp = ((float[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (byte) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; float[] tmp = ((float[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (short) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; float[] tmp = ((float[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (float) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; float[] tmp = ((float[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (double) (tmp[xp + yp]); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; float[] tmp = ((float[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (byte) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; float[] tmp = ((float[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (short) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; float[] tmp = ((float[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (float) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; float[] tmp = ((float[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (double) (tmp[xp + yp * nx]); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (byte) (((float[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (short) (((float[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (float) (((float[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (double) (((float[]) data[zp])[xyp]); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; float[] tmp = ((float[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; float[] tmp = ((float[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; float[] tmp = ((float[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; float[] tmp = ((float[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (tmp[xp + yp]); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (((float[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (((float[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (((float[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (((float[]) data[zp])[xp + yp]); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (byte) (((float[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (short) (((float[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (float) (((float[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (double) (((float[]) data[zp])[xp + yp * nx]); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } float[] tmp = ((float[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (byte) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } float[] tmp = ((float[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (short) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } float[] tmp = ((float[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (float) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } float[] tmp = ((float[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (double) (tmp[xp + yp]); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } // ------------------------------------------------------------------ // // putBounded section // // ------------------------------------------------------------------ /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i] & 0xFF); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i] & 0xFFFF); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i]); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i]); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i] & 0xFF); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i] & 0xFFFF); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i]); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); float[] tmp = (float[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i]); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((float[]) data[k])[offset] = (float) (buffer[i] & 0xFF); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((float[]) data[k])[offset] = (float) (buffer[i] & 0xFFFF); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((float[]) data[k])[offset] = (float) (buffer[i]); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((float[]) data[k])[offset] = (float) (buffer[i]); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); float[] tmp = (float[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i][j] & 0xFF); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); float[] tmp = (float[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i][j] & 0xFFFF); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); float[] tmp = (float[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i][j]); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); float[] tmp = (float[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i][j]); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((float[]) data[k])[offset] = (float) (buffer[i][j] & 0xFF); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((float[]) data[k])[offset] = (float) (buffer[i][j] & 0xFFFF); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((float[]) data[k])[offset] = (float) (buffer[i][j]); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((float[]) data[k])[offset] = (float) (buffer[i][j]); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((float[]) data[k])[offset] = (float) (buffer[i][j] & 0xFF); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((float[]) data[k])[offset] = (float) (buffer[i][j] & 0xFFFF); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((float[]) data[k])[offset] = (float) (buffer[i][j]); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((float[]) data[k])[offset] = (float) (buffer[i][j]); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, byte[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { float[] tmp = (float[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i][j][k] & 0xFF); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, short[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { float[] tmp = (float[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i][j][k] & 0xFFFF); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, float[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { float[] tmp = (float[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i][j][k]); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, double[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { float[] tmp = (float[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (float) (buffer[i][j][k]); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/FloatBuffer.java b/src/bilib/src/imageware/FloatBuffer.java new file mode 100644 index 0000000..2da717d --- /dev/null +++ b/src/bilib/src/imageware/FloatBuffer.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import ij.process.ByteProcessor; import ij.process.ColorProcessor; import ij.process.FloatProcessor; import ij.process.ImageProcessor; import ij.process.ShortProcessor; import java.awt.Image; import java.awt.image.ImageObserver; import java.awt.image.PixelGrabber; /** * Class FloatBuffer. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class FloatBuffer implements Buffer { protected Object[] data = null; protected int nx = 0; protected int ny = 0; protected int nz = 0; protected int nxy = 0; /** * Constructor of a empty 3D float buffer. * * @param nx * size of the 3D buffer in the X axis * @param ny * size of the 3D buffer in the Y axis * @param nz * size of the 3D buffer in the Z axis */ protected FloatBuffer(int nx, int ny, int nz) { this.nx = nx; this.ny = ny; this.nz = nz; if (nx <= 0 || ny <= 0 || nz <= 0) throw_constructor(nx, ny, nz); allocate(); } /** * Constructor of a float buffer from a object Image of Java. * * @param image * source to build a new imageware */ protected FloatBuffer(Image image, int mode) { if (image == null) { throw_constructor(); } ImageObserver observer = null; this.nx = image.getWidth(observer); this.ny = image.getHeight(observer); this.nz = 1; this.nxy = nx * ny; byte[] pixels = new byte[nxy]; PixelGrabber pg = new PixelGrabber(image, 0, 0, nx, ny, false); try { pg.grabPixels(); pixels = (byte[]) (pg.getPixels()); } catch (Exception e) { throw_constructor(); } allocate(); for (int k = 0; k < nxy; k++) ((float[]) data[0])[k] = (float) (pixels[k] & 0xFF); } /** * Constructor of a float buffer from a ImageStack. * * New data are allocated if the mode is CREATE, the imageware use the data * of ImageJ if the mode is WRAP. * * @param stack * source to build a new imageware * @param mode * WRAP or CREATE */ protected FloatBuffer(ImageStack stack, int mode) { if (stack == null) { throw_constructor(); } this.nx = stack.getWidth(); this.ny = stack.getHeight(); this.nz = stack.getSize(); this.nxy = nx * ny; switch (mode) { case ImageWare.WRAP: this.data = stack.getImageArray(); break; case ImageWare.CREATE: allocate(); ImageProcessor ip = stack.getProcessor(1); if (ip instanceof ByteProcessor) { Object[] vol = stack.getImageArray(); for (int z = 0; z < nz; z++) { byte[] slice = (byte[]) vol[z]; for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] = (float) (slice[k] & 0xFF); } } } else if (ip instanceof ShortProcessor) { Object[] vol = stack.getImageArray(); for (int z = 0; z < nz; z++) { short[] slice = (short[]) vol[z]; for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] = (float) (slice[k] & 0xFFFF); } } } else if (ip instanceof FloatProcessor) { Object[] vol = stack.getImageArray(); for (int z = 0; z < nz; z++) { float[] slice = (float[]) vol[z]; for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] = (float) slice[k]; } } } else if (ip instanceof ColorProcessor) { double r, g, b; int c; ColorProcessor cp; int[] pixels; for (int z = 0; z < nz; z++) { cp = (ColorProcessor) stack.getProcessor(z + 1); pixels = (int[]) cp.getPixels(); for (int k = 0; k < nxy; k++) { c = pixels[k]; r = (double) ((c & 0xFF0000) >> 16); g = (double) ((c & 0xFF00) >> 8); b = (double) ((c & 0xFF)); ((float[]) data[z])[k] = (float) ((r + g + b) / 3.0); } } } else { throw_constructor(); } break; default: throw_constructor(); break; } } /** * Constructor of a float buffer from a specific color channel of * ImageStack. * * New data are always allocated. If it is a gray image the imageware is * created and fill up with data of the source ImageStack. If it is a color * image only the selected channel is used to create this imageware. * * @param stack * source to build a new imageware * @param channel * RED, GREEN or BLUE */ protected FloatBuffer(ImageStack stack, byte channel) { if (stack == null) { throw_constructor(); } this.nx = stack.getWidth(); this.ny = stack.getHeight(); this.nz = stack.getSize(); this.nxy = nx * ny; allocate(); ImageProcessor ip = stack.getProcessor(1); if (ip instanceof ByteProcessor) { Object[] vol = stack.getImageArray(); for (int z = 0; z < nz; z++) { byte[] slice = (byte[]) vol[z]; for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] = (float) (slice[k] & 0xFF); } } } else if (ip instanceof ShortProcessor) { Object[] vol = stack.getImageArray(); for (int z = 0; z < nz; z++) { short[] slice = (short[]) vol[z]; for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] = (float) (slice[k] & 0xFFFF); } } } else if (ip instanceof FloatProcessor) { Object[] vol = stack.getImageArray(); for (int z = 0; z < nz; z++) { float[] slice = (float[]) vol[z]; for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] = (float) slice[k]; } } } else if (ip instanceof ColorProcessor) { ColorProcessor cp; int[] pixels; for (int z = 0; z < nz; z++) { cp = (ColorProcessor) stack.getProcessor(z + 1); pixels = (int[]) cp.getPixels(); switch (channel) { case ImageWare.RED: for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] = (float) ((pixels[k] & 0xFF0000) >> 16); } break; case ImageWare.GREEN: for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] = (float) ((pixels[k] & 0xFF00) >> 8); } break; case ImageWare.BLUE: for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] = (float) (pixels[k] & 0xFF); } break; default: throw_constructor(); } } } else { throw_constructor(); } } /** * Constructor of a float buffer from a byte array. * * @param array * source to build this new imageware */ protected FloatBuffer(byte[] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = 1; this.nz = 1; allocate(); putX(0, 0, 0, array); } /** * Constructor of a float buffer from a byte array. * * @param array * source to build this new imageware */ protected FloatBuffer(byte[][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = 1; allocate(); putXY(0, 0, 0, array); } /** * Constructor of a float buffer from a byte array. * * @param array * source to build this new imageware */ protected FloatBuffer(byte[][][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = array[0][0].length; allocate(); putXYZ(0, 0, 0, array); } /** * Constructor of a float buffer from a short array. * * @param array * source to build this new imageware */ protected FloatBuffer(short[] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = 1; this.nz = 1; allocate(); putX(0, 0, 0, array); } /** * Constructor of a float buffer from a short array. * * @param array * source to build this new imageware */ protected FloatBuffer(short[][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = 1; allocate(); putXY(0, 0, 0, array); } /** * Constructor of a float buffer from a short array. * * @param array * source to build this new imageware */ protected FloatBuffer(short[][][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = array[0][0].length; allocate(); putXYZ(0, 0, 0, array); } /** * Constructor of a float buffer from a float array. * * @param array * source to build this new imageware */ protected FloatBuffer(float[] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = 1; this.nz = 1; allocate(); putX(0, 0, 0, array); } /** * Constructor of a float buffer from a float array. * * @param array * source to build this new imageware */ protected FloatBuffer(float[][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = 1; allocate(); putXY(0, 0, 0, array); } /** * Constructor of a float buffer from a float array. * * @param array * source to build this new imageware */ protected FloatBuffer(float[][][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = array[0][0].length; allocate(); putXYZ(0, 0, 0, array); } /** * Constructor of a float buffer from a double array. * * @param array * source to build this new imageware */ protected FloatBuffer(double[] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = 1; this.nz = 1; allocate(); putX(0, 0, 0, array); } /** * Constructor of a float buffer from a double array. * * @param array * source to build this new imageware */ protected FloatBuffer(double[][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = 1; allocate(); putXY(0, 0, 0, array); } /** * Constructor of a float buffer from a double array. * * @param array * source to build this new imageware */ protected FloatBuffer(double[][][] array, int mode) { if (array == null) { throw_constructor(); } this.nx = array.length; this.ny = array[0].length; this.nz = array[0][0].length; allocate(); putXYZ(0, 0, 0, array); } /** * Return the type of this imageware. * * @return the type of this imageware */ public int getType() { return ImageWare.FLOAT; } /** * Return the type of this imageware in a string format. * * @return the type of this imageware translated in a string format */ public String getTypeToString() { return "Float"; } /** * Return the number of dimension of this imageware (1, 2 or 3). * * @return the number of dimension of this imageware */ public int getDimension() { int dims = 0; dims += (nx > 1 ? 1 : 0); dims += (ny > 1 ? 1 : 0); dims += (nz > 1 ? 1 : 0); return dims; } /** * Return the size of the imageware int[0] : x, int[1] : y, int[2] : z. * * @return an array given the size of the imageware */ public int[] getSize() { int[] size = { nx, ny, nz }; return size; } /** * Return the size in the X axis. * * @return the size in the X axis */ public int getSizeX() { return nx; } /** * Return the size in the Y axis. * * @return the size in the Y axis */ public int getSizeY() { return ny; } /** * Return the size in the Z axis. * * @return the size in the Z axis */ public int getSizeZ() { return nz; } /** * Return the size in the X axis. * * @return the size in the X axis */ public int getWidth() { return nx; } /** * Return the size in the Y axis. * * @return the size in the Y axis */ public int getHeight() { return ny; } /** * Return the size in the Z axis. * * @return the size in the Z axis */ public int getDepth() { return nz; } /** * Return the number of pixels in the imageware. * * @return number of pixels in the imageware */ public int getTotalSize() { return nxy * nz; } /** * Return true is this imageware has the same size the imageware given as * parameter. * * @param imageware * imageware to be compared * @return true if the imageware of the same size than this imageware */ public boolean isSameSize(ImageWare imageware) { if (nx != imageware.getSizeX()) return false; if (ny != imageware.getSizeY()) return false; if (nz != imageware.getSizeZ()) return false; return true; } // ------------------------------------------------------------------ // // put Section // // ------------------------------------------------------------------ /** * Put an array into the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putX(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); double buf[] = new double[bnx]; buffer.getX(0, 0, 0, buf); putX(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putY(int x, int y, int z, ImageWare buffer) { int bny = buffer.getSizeY(); double buf[] = new double[bny]; buffer.getY(0, 0, 0, buf); putY(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putZ(int x, int y, int z, ImageWare buffer) { int bnz = buffer.getSizeZ(); double buf[] = new double[bnz]; buffer.getZ(0, 0, 0, buf); putZ(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putXY(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); int bny = buffer.getSizeY(); double buf[][] = new double[bnx][bny]; buffer.getXY(0, 0, 0, buf); putXY(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putXZ(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); int bnz = buffer.getSizeZ(); double buf[][] = new double[bnx][bnz]; buffer.getXZ(0, 0, 0, buf); putXZ(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putYZ(int x, int y, int z, ImageWare buffer) { int bny = buffer.getSizeY(); int bnz = buffer.getSizeZ(); double buf[][] = new double[bny][bnz]; buffer.getYZ(0, 0, 0, buf); putYZ(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * ImageWare object to put into the imageware */ public void putXYZ(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); int bny = buffer.getSizeY(); int bnz = buffer.getSizeZ(); double buf[][][] = new double[bnx][bny][bnz]; buffer.getXYZ(0, 0, 0, buf); putXYZ(x, y, z, buf); } /** * Put an array into the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putX(int x, int y, int z, byte[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (float) (buffer[i] & 0xFF); offset++; } } catch (Exception e) { throw_put("X", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putX(int x, int y, int z, short[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (float) (buffer[i] & 0xFFFF); offset++; } } catch (Exception e) { throw_put("X", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putX(int x, int y, int z, float[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; System.arraycopy(buffer, 0, tmp, offset, leni); } catch (Exception e) { throw_put("X", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putX(int x, int y, int z, double[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (float) (buffer[i]); offset++; } } catch (Exception e) { throw_put("X", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putY(int x, int y, int z, byte[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (float) (buffer[i] & 0xFF); offset += nx; } } catch (Exception e) { throw_put("Y", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putY(int x, int y, int z, short[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (float) (buffer[i] & 0xFFFF); offset += nx; } } catch (Exception e) { throw_put("Y", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putY(int x, int y, int z, float[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (float) (buffer[i]); offset += nx; } } catch (Exception e) { throw_put("Y", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putY(int x, int y, int z, double[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { tmp[offset] = (float) (buffer[i]); offset += nx; } } catch (Exception e) { throw_put("Y", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * bybytete 1D array to put into the imageware */ public void putZ(int x, int y, int z, byte[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { ((float[]) data[z])[offset] = (float) (buffer[i] & 0xFF); z++; } } catch (Exception e) { throw_put("Z", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byshortte 1D array to put into the imageware */ public void putZ(int x, int y, int z, short[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { ((float[]) data[z])[offset] = (float) (buffer[i] & 0xFFFF); z++; } } catch (Exception e) { throw_put("Z", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byfloatte 1D array to put into the imageware */ public void putZ(int x, int y, int z, float[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { ((float[]) data[z])[offset] = (float) (buffer[i]); z++; } } catch (Exception e) { throw_put("Z", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * bydoublete 1D array to put into the imageware */ public void putZ(int x, int y, int z, double[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { ((float[]) data[z])[offset] = (float) (buffer[i]); z++; } } catch (Exception e) { throw_put("Z", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putXY(int x, int y, int z, byte[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (float) (buffer[i][j] & 0xFF); } } } catch (Exception e) { throw_put("XY", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putXY(int x, int y, int z, short[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (float) (buffer[i][j] & 0xFFFF); } } } catch (Exception e) { throw_put("XY", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putXY(int x, int y, int z, float[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (float) (buffer[i][j]); } } } catch (Exception e) { throw_put("XY", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putXY(int x, int y, int z, double[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (float) (buffer[i][j]); } } } catch (Exception e) { throw_put("XY", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putXZ(int x, int y, int z, byte[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + j * nx; for (int i = 0; i < leni; i++, offset++) { ((float[]) data[z])[offset] = (float) (buffer[i][j] & 0xFF); } } } catch (Exception e) { throw_put("YZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putXZ(int x, int y, int z, short[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + j * nx; for (int i = 0; i < leni; i++, offset++) { ((float[]) data[z])[offset] = (float) (buffer[i][j] & 0xFFFF); } } } catch (Exception e) { throw_put("YZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putXZ(int x, int y, int z, float[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + j * nx; for (int i = 0; i < leni; i++, offset++) { ((float[]) data[z])[offset] = (float) (buffer[i][j]); } } } catch (Exception e) { throw_put("YZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putXZ(int x, int y, int z, double[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + j * nx; for (int i = 0; i < leni; i++, offset++) { ((float[]) data[z])[offset] = (float) (buffer[i][j]); } } } catch (Exception e) { throw_put("YZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putYZ(int x, int y, int z, byte[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) for (int i = 0; i < leni; i++, offset += nx) { ((float[]) data[z])[offset] = (float) (buffer[i][j] & 0xFF); } } catch (Exception e) { throw_put("XZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putYZ(int x, int y, int z, short[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) for (int i = 0; i < leni; i++, offset += nx) { ((float[]) data[z])[offset] = (float) (buffer[i][j] & 0xFFFF); } } catch (Exception e) { throw_put("XZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putYZ(int x, int y, int z, float[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) for (int i = 0; i < leni; i++, offset += nx) { ((float[]) data[z])[offset] = (float) (buffer[i][j]); } } catch (Exception e) { throw_put("XZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putYZ(int x, int y, int z, double[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) for (int i = 0; i < leni; i++, offset += nx) { ((float[]) data[z])[offset] = (float) (buffer[i][j]); } } catch (Exception e) { throw_put("XZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 3D array to put into the imageware */ public void putXYZ(int x, int y, int z, byte[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (float) (buffer[i][j][k] & 0xFF); } } } } catch (Exception e) { throw_put("XYZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 3D array to put into the imageware */ public void putXYZ(int x, int y, int z, short[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (float) (buffer[i][j][k] & 0xFFFF); } } } } catch (Exception e) { throw_put("XYZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 3D array to put into the imageware */ public void putXYZ(int x, int y, int z, float[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (float) (buffer[i][j][k]); } } } } catch (Exception e) { throw_put("XYZ", "No check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 3D array to put into the imageware */ public void putXYZ(int x, int y, int z, double[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { tmp[offset] = (float) (buffer[i][j][k]); } } } } catch (Exception e) { throw_put("XYZ", "No check", buffer, x, y, z); } } // ------------------------------------------------------------------ // // get Section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the imageware */ public void getX(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); double buf[] = new double[bnx]; getX(x, y, z, buf); buffer.putX(0, 0, 0, buf); } /** * Get an array from the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the imageware */ public void getY(int x, int y, int z, ImageWare buffer) { int bny = buffer.getSizeY(); double buf[] = new double[bny]; getY(x, y, z, buf); buffer.putY(0, 0, 0, buf); } /** * Get an array from the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the imageware */ public void getZ(int x, int y, int z, ImageWare buffer) { int bnz = buffer.getSizeZ(); double buf[] = new double[bnz]; getZ(x, y, z, buf); buffer.putZ(0, 0, 0, buf); } /** * get an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the imageware */ public void getXY(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); int bny = buffer.getSizeY(); double buf[][] = new double[bnx][bny]; getXY(x, y, z, buf); buffer.putXY(0, 0, 0, buf); } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the imageware */ public void getXZ(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); int bnz = buffer.getSizeZ(); double buf[][] = new double[bnx][bnz]; getXZ(x, y, z, buf); buffer.putXZ(0, 0, 0, buf); } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the datase */ public void getYZ(int x, int y, int z, ImageWare buffer) { int bny = buffer.getSizeY(); int bnz = buffer.getSizeZ(); double buf[][] = new double[bny][bnz]; getYZ(x, y, z, buf); buffer.putYZ(0, 0, 0, buf); } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * ImageWare object to get into the imageware */ public void getXYZ(int x, int y, int z, ImageWare buffer) { int bnx = buffer.getSizeX(); int bny = buffer.getSizeY(); int bnz = buffer.getSizeZ(); double buf[][][] = new double[bnx][bny][bnz]; getXYZ(x, y, z, buf); buffer.putXYZ(0, 0, 0, buf); } /** * Get an array from the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware */ public void getX(int x, int y, int z, byte[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (byte) (tmp[offset]); offset++; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware */ public void getX(int x, int y, int z, short[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (short) (tmp[offset]); offset++; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware */ public void getX(int x, int y, int z, float[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; System.arraycopy(tmp, offset, buffer, 0, leni); } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware */ public void getX(int x, int y, int z, double[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (double) (tmp[offset]); offset++; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware */ public void getY(int x, int y, int z, byte[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (byte) (tmp[offset]); offset += nx; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware */ public void getY(int x, int y, int z, short[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (short) (tmp[offset]); offset += nx; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware */ public void getY(int x, int y, int z, float[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (float) (tmp[offset]); offset += nx; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware */ public void getY(int x, int y, int z, double[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; float[] tmp = (float[]) data[z]; for (int i = 0; i < leni; i++) { buffer[i] = (double) (tmp[offset]); offset += nx; } } catch (Exception e) { throw_get("X", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware */ public void getZ(int x, int y, int z, byte[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { buffer[i] = (byte) (((float[]) data[z])[offset]); z++; } } catch (Exception e) { throw_get("Y", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware */ public void getZ(int x, int y, int z, short[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { buffer[i] = (short) (((float[]) data[z])[offset]); z++; } } catch (Exception e) { throw_get("Y", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware */ public void getZ(int x, int y, int z, float[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { buffer[i] = (float) (((float[]) data[z])[offset]); z++; } } catch (Exception e) { throw_get("Y", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware */ public void getZ(int x, int y, int z, double[] buffer) { try { int offset = x + y * nx; int leni = buffer.length; for (int i = 0; i < leni; i++) { buffer[i] = (double) (((float[]) data[z])[offset]); z++; } } catch (Exception e) { throw_get("Y", "No check", buffer, x, y, z); } } /** * get an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware */ public void getXY(int x, int y, int z, byte[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (byte) (tmp[offset]); } } } catch (Exception e) { throw_get("XY", "No check", buffer, x, y, z); } } /** * get an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware */ public void getXY(int x, int y, int z, short[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (short) (tmp[offset]); } } } catch (Exception e) { throw_get("XY", "No check", buffer, x, y, z); } } /** * get an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware */ public void getXY(int x, int y, int z, float[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (float) (tmp[offset]); } } } catch (Exception e) { throw_get("XY", "No check", buffer, x, y, z); } } /** * get an array into the imageware at the position (x,y,z) in XY axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware */ public void getXY(int x, int y, int z, double[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (y + j) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (double) (tmp[offset]); } } } catch (Exception e) { throw_get("XY", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware */ public void getXZ(int x, int y, int z, byte[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + y * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (byte) (((float[]) data[z])[offset]); } } } catch (Exception e) { throw_get("XZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware */ public void getXZ(int x, int y, int z, short[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + y * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (short) (((float[]) data[z])[offset]); } } } catch (Exception e) { throw_get("XZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware */ public void getXZ(int x, int y, int z, float[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + y * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (float) (((float[]) data[z])[offset]); } } } catch (Exception e) { throw_get("XZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware */ public void getXZ(int x, int y, int z, double[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++) { offset = x + y * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j] = (double) (((float[]) data[z])[offset]); } } } catch (Exception e) { throw_get("XZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the datase */ public void getYZ(int x, int y, int z, byte[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { for (int i = 0; i < leni; i++, offset += nx) { buffer[i][j] = (byte) (((float[]) data[z])[offset]); } } } catch (Exception e) { throw_get("YZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the datase */ public void getYZ(int x, int y, int z, short[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { for (int i = 0; i < leni; i++, offset += nx) { buffer[i][j] = (short) (((float[]) data[z])[offset]); } } } catch (Exception e) { throw_get("YZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the datase */ public void getYZ(int x, int y, int z, float[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { for (int i = 0; i < leni; i++, offset += nx) { buffer[i][j] = (float) (((float[]) data[z])[offset]); } } } catch (Exception e) { throw_get("YZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the datase */ public void getYZ(int x, int y, int z, double[][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { for (int i = 0; i < leni; i++, offset += nx) { buffer[i][j] = (double) (((float[]) data[z])[offset]); } } } catch (Exception e) { throw_get("YZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 3D array to get into the imageware */ public void getXYZ(int x, int y, int z, byte[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j][k] = (byte) (tmp[offset]); } } } } catch (Exception e) { throw_get("XYZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 3D array to get into the imageware */ public void getXYZ(int x, int y, int z, short[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j][k] = (short) (tmp[offset]); } } } } catch (Exception e) { throw_get("XYZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 3D array to get into the imageware */ public void getXYZ(int x, int y, int z, float[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j][k] = (float) (tmp[offset]); } } } } catch (Exception e) { throw_get("XYZ", "No check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. No * check are performed if the array is outside of the imageware. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 3D array to get into the imageware */ public void getXYZ(int x, int y, int z, double[][][] buffer) { try { int offset = x + y * nx; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++, z++) { float[] tmp = (float[]) data[z]; for (int j = 0; j < lenj; j++) { offset = x + (j + y) * nx; for (int i = 0; i < leni; i++, offset++) { buffer[i][j][k] = (double) (tmp[offset]); } } } } catch (Exception e) { throw_get("XYZ", "No check", buffer, x, y, z); } } // ------------------------------------------------------------------ // // Private Section // // ------------------------------------------------------------------ /** * Prepare a complete error message from the errors coming the constructors. */ protected void throw_constructor() { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a float imageware.\n" + "-------------------------------------------------------\n"); } /** * Prepare a complete error message from the errors coming the constructors. */ protected void throw_constructor(int nx, int ny, int nz) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a float imageware " + nx + "," + ny + "," + nz + "].\n" + "-------------------------------------------------------\n"); } /** * Prepare a complete error message from the errors coming the get routines. */ protected void throw_get(String direction, String border, Object buffer, int x, int y, int z) { int leni = 0; int lenj = 0; int lenk = 0; String type = " unknown type"; if (buffer instanceof byte[]) { leni = ((byte[]) buffer).length; type = " 1D byte"; } else if (buffer instanceof short[]) { leni = ((short[]) buffer).length; type = " 1D short"; } else if (buffer instanceof float[]) { leni = ((float[]) buffer).length; type = " 1D float"; } else if (buffer instanceof double[]) { leni = ((double[]) buffer).length; type = " 1D double"; } else if (buffer instanceof byte[][]) { leni = ((byte[][]) buffer).length; lenj = ((byte[][]) buffer)[0].length; type = " 2D byte"; } else if (buffer instanceof short[][]) { leni = ((short[][]) buffer).length; lenj = ((short[][]) buffer)[0].length; type = " 2D short"; } else if (buffer instanceof float[][]) { leni = ((float[][]) buffer).length; lenj = ((float[][]) buffer)[0].length; type = " 2D float"; } else if (buffer instanceof double[][]) { leni = ((double[][]) buffer).length; lenj = ((double[][]) buffer)[0].length; type = " 2D double"; } else if (buffer instanceof byte[][][]) { leni = ((byte[][][]) buffer).length; lenj = ((byte[][][]) buffer)[0].length; lenk = ((byte[][][]) buffer)[0][0].length; type = " 3D byte"; } else if (buffer instanceof short[][][]) { leni = ((short[][][]) buffer).length; lenj = ((short[][][]) buffer)[0].length; lenk = ((short[][][]) buffer)[0][0].length; type = " 3D short"; } else if (buffer instanceof float[][][]) { leni = ((float[][][]) buffer).length; lenj = ((float[][][]) buffer)[0].length; lenk = ((float[][][]) buffer)[0][0].length; type = " 3D float"; } else if (buffer instanceof double[][][]) { leni = ((double[][][]) buffer).length; lenj = ((double[][][]) buffer)[0].length; lenk = ((double[][][]) buffer)[0][0].length; type = " 3D double"; } throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get a" + type + " buffer [" + (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "from the float imageware [" + nx + "," + ny + "," + nz + "]\n" + "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + "-------------------------------------------------------\n"); } /** * Prepare a complete error message from the errors coming the put routines. */ protected void throw_put(String direction, String border, Object buffer, int x, int y, int z) { int leni = 0; int lenj = 0; int lenk = 0; String type = " unknown type"; if (buffer instanceof byte[]) { leni = ((byte[]) buffer).length; type = " 1D byte"; } else if (buffer instanceof short[]) { leni = ((short[]) buffer).length; type = " 1D short"; } else if (buffer instanceof float[]) { leni = ((float[]) buffer).length; type = " 1D float"; } else if (buffer instanceof double[]) { leni = ((double[]) buffer).length; type = " 1D double"; } else if (buffer instanceof byte[][]) { leni = ((byte[][]) buffer).length; lenj = ((byte[][]) buffer)[0].length; type = " 2D byte"; } else if (buffer instanceof short[][]) { leni = ((short[][]) buffer).length; lenj = ((short[][]) buffer)[0].length; type = " 2D short"; } else if (buffer instanceof float[][]) { leni = ((float[][]) buffer).length; lenj = ((float[][]) buffer)[0].length; type = " 2D float"; } else if (buffer instanceof double[][]) { leni = ((double[][]) buffer).length; lenj = ((double[][]) buffer)[0].length; type = " 2D double"; } else if (buffer instanceof byte[][][]) { leni = ((byte[][][]) buffer).length; lenj = ((byte[][][]) buffer)[0].length; lenk = ((byte[][][]) buffer)[0][0].length; type = " 3D byte"; } else if (buffer instanceof short[][][]) { leni = ((short[][][]) buffer).length; lenj = ((short[][][]) buffer)[0].length; lenk = ((short[][][]) buffer)[0][0].length; type = " 3D short"; } else if (buffer instanceof float[][][]) { leni = ((float[][][]) buffer).length; lenj = ((float[][][]) buffer)[0].length; lenk = ((float[][][]) buffer)[0][0].length; type = " 3D float"; } else if (buffer instanceof double[][][]) { leni = ((double[][][]) buffer).length; lenj = ((double[][][]) buffer)[0].length; lenk = ((double[][][]) buffer)[0][0].length; type = " 3D double"; } throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a" + type + " buffer [" + (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "into the float imageware [" + nx + "," + ny + "," + nz + "]\n" + "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + "-------------------------------------------------------\n"); } // ------------------------------------------------------------------ // // Get slice fast and direct access Section // // ------------------------------------------------------------------ /** * Get a reference of the whole volume data. * * @return a reference of the data of this imageware */ public Object[] getVolume() { return data; } /** * Get a specific slice, fast and direct access, but only for byte * imageware. * * @param z * number of the requested slice * @return a reference of the data of one slice of this imageware */ public byte[] getSliceByte(int z) { return null; } /** * Get a specific slice, fast and direct access, but only for short * imageware. * * @param z * number of the requested slice * @return a reference of the data of one slice of this imageware */ public short[] getSliceShort(int z) { return null; } /** * Get a specific slice, fast and direct access, but only for float * imageware. * * @param z * number of the requested slice * @return a reference of the data of one slice of this imageware */ public float[] getSliceFloat(int z) { return (float[]) data[z]; } /** * Get a specific slice, fast and direct access, but only for double * imageware. * * @param z * number of the requested slice * @return a reference of the data of one slice of this imageware */ public double[] getSliceDouble(int z) { return null; } /** * Allocate a buffer of size [nx,ny,nz]. */ private void allocate() { try { this.data = new Object[nz]; this.nxy = nx * ny; for (int z = 0; z < nz; z++) this.data[z] = new float[nxy]; } catch (Exception e) { throw_constructor(nx, ny, nz); } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/FloatPointwise.java b/src/bilib/src/imageware/FloatPointwise.java new file mode 100644 index 0000000..0883b25 --- /dev/null +++ b/src/bilib/src/imageware/FloatPointwise.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import ij.process.FloatProcessor; import java.awt.Image; import java.util.Random; /** * Class FloatPointwise. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class FloatPointwise extends FloatAccess implements Pointwise { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected FloatPointwise(int nx, int ny, int nz) { super(nx, ny, nz); } protected FloatPointwise(Image image, int mode) { super(image, mode); } protected FloatPointwise(ImageStack stack, int mode) { super(stack, mode); } protected FloatPointwise(ImageStack stack, byte chan) { super(stack, chan); } protected FloatPointwise(byte[] array, int mode) { super(array, mode); } protected FloatPointwise(byte[][] array, int mode) { super(array, mode); } protected FloatPointwise(byte[][][] array, int mode) { super(array, mode); } protected FloatPointwise(short[] array, int mode) { super(array, mode); } protected FloatPointwise(short[][] array, int mode) { super(array, mode); } protected FloatPointwise(short[][][] array, int mode) { super(array, mode); } protected FloatPointwise(float[] array, int mode) { super(array, mode); } protected FloatPointwise(float[][] array, int mode) { super(array, mode); } protected FloatPointwise(float[][][] array, int mode) { super(array, mode); } protected FloatPointwise(double[] array, int mode) { super(array, mode); } protected FloatPointwise(double[][] array, int mode) { super(array, mode); } protected FloatPointwise(double[][][] array, int mode) { super(array, mode); } /** * Fill this imageware with a constant value. * * @param value * the constant value */ public void fillConstant(double value) { float typedValue = (float) value; float[] slice = null; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) slice[k] = typedValue; } } /** * Fill this imageware with ramp. */ public void fillRamp() { int off = 0; float[] slice = null; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) slice[k] = (float) (off + k); off += nxy; } } /** * Generate a gaussian noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void fillGaussianNoise(double amplitude) { Random rnd = new Random(); float[] slice = null; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (float) ((rnd.nextGaussian()) * amplitude); } } } /** * Generate a uniform noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void fillUniformNoise(double amplitude) { Random rnd = new Random(); float[] slice = null; amplitude *= 2.0; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (float) ((rnd.nextDouble() - 0.5) * amplitude); } } } /** * Generate a salt and pepper noise. * * @param amplitudeSalt * amplitude of the salt noise * @param amplitudePepper * amplitude of the pepper noise * @param percentageSalt * percentage of the salt noise * @param percentagePepper * percentage of the pepper noise */ public void fillSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) { Random rnd = new Random(); int index, z; if (percentageSalt > 0) { double nbSalt = nxy * nz / percentageSalt; for (int k = 0; k < nbSalt; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((float[]) data[z])[index] = (float) (rnd.nextDouble() * amplitudeSalt); } } if (percentagePepper > 0) { double nbPepper = nxy * nz / percentagePepper; for (int k = 0; k < nbPepper; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((float[]) data[z])[index] = (float) (-rnd.nextDouble() * amplitudeSalt); } } } /** * Build an ImageStack of ImageJ. */ public ImageStack buildImageStack() { ImageStack imagestack = new ImageStack(nx, ny); for (int z = 0; z < nz; z++) { FloatProcessor ip = new FloatProcessor(nx, ny); float pix[] = (float[]) ip.getPixels(); for (int k = 0; k < nxy; k++) pix[k] = (float) (((float[]) data[z])[k]); imagestack.addSlice("" + z, ip); } return imagestack; } /** * Invert the pixel intensity. */ public void invert() { double max = -Double.MAX_VALUE; float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k]) > max) max = slice[k]; } } for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (float) (max - ((double) (slice[k]))); } } } /** * Negate the pixel intensity. */ public void negate() { float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (float) (-((double) (slice[k]))); } } } /** * Clip the pixel intensity into [0..255]. */ public void clip() { clip(0.0, 255.0); } /** * Clip the pixel intensity into [minLevel..maxLevel]. * * @param minLevel * double value given the threshold * @param maxLevel * double value given the threshold */ public void clip(double minLevel, double maxLevel) { float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; float value; float min = (float) minLevel; float max = (float) maxLevel; for (int k = 0; k < nxy; k++) { value = (float) (slice[k]); if (value < min) slice[k] = min; if (value > max) slice[k] = max; } } } /** * Rescale the pixel intensity into [0..255]. */ public void rescale() { double maxImage = -Double.MAX_VALUE; double minImage = Double.MAX_VALUE; float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k]) > maxImage) maxImage = slice[k]; if ((slice[k]) < minImage) minImage = slice[k]; } } double a; if (minImage - maxImage == 0) { a = 1.0; minImage = 128.0; } else { a = 255.0 / (maxImage - minImage); } for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (float) (a * (((double) (slice[k])) - minImage)); } } } /** * Rescale the pixel intensity into [minLevel..maxLevel]. * * @param minLevel * double value given the threshold * @param maxLevel * double value given the threshold */ public void rescale(double minLevel, double maxLevel) { double maxImage = -Double.MAX_VALUE; double minImage = Double.MAX_VALUE; float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k]) > maxImage) maxImage = slice[k]; if ((slice[k]) < minImage) minImage = slice[k]; } } double a; if (minImage - maxImage == 0) { a = 1.0; minImage = (maxLevel - minLevel) / 2.0; } else { a = (maxLevel - minLevel) / (maxImage - minImage); } for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (float) (a * (((double) (slice[k])) - minImage) + minLevel); } } } /** * Rescale the pixel intensity with a linear curve passing through * (maxLevel-minLevel)/2 at the 0 input intensity. * * @param minLevel * double value given the threshold * @param maxLevel * double value given the threshold */ public void rescaleCenter(double minLevel, double maxLevel) { double maxImage = -Double.MAX_VALUE; double minImage = Double.MAX_VALUE; float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k]) > maxImage) maxImage = slice[k]; if ((slice[k]) < minImage) minImage = slice[k]; } } double center = (maxLevel + minLevel) / 2.0; double a; if (minImage - maxImage == 0) { a = 1.0; minImage = (maxLevel - minLevel) / 2.0; } else { if (Math.abs(maxImage) > Math.abs(minImage)) a = (maxLevel - center) / Math.abs(maxImage); else a = (center - minLevel) / Math.abs(minImage); } for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (float) (a * (((double) (slice[k])) - minImage) + center); } } } /** * Compute the absolute value of this imageware. */ public void abs() { float zero = (float) 0.0; float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { if (slice[k] < zero) slice[k] = -slice[k]; } } } /** * Compute the log of this imageware. */ public void log() { float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (float) Math.log(slice[k]); } } } /** * Compute the exponential of this imageware. */ public void exp() { float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (float) Math.exp(slice[k]); } } } /** * Compute the square root of this imageware. */ public void sqrt() { float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (float) Math.sqrt(slice[k]); } } } /** * Compute the square of this imageware. */ public void sqr() { float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] *= slice[k]; } } } /** * Compute the power of a of this imageware. * * @param a * exponent */ public void pow(double a) { float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (float) Math.pow(slice[k], a); } } } /** * Add a constant value to this imageware. */ public void add(double constant) { float cst = (float) constant; float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] += cst; } } } /** * Multiply a constant value to this imageware. * * @param constant * the constant value */ public void multiply(double constant) { float cst = (float) constant; float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] *= cst; } } } /** * Subtract a constant value to this imageware. * * @param constant * the constant value */ public void subtract(double constant) { float cst = (float) constant; float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] -= cst; } } } /** * Divide by a constant value to this imageware. * * @param constant * the constant value */ public void divide(double constant) { if (constant == 0.0) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to divide because the constant is 0.\n" + "-------------------------------------------------------\n"); float cst = (float) constant; float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] /= cst; } } } /** * Threshold a imageware in two levels 0 and 255. * * All the pixels values strictly greater than 'thresholdValue' and are set * to 0. The remaining values are set to 255. * * @param thresholdValue * double value given the threshold */ public void threshold(double thresholdValue) { threshold(thresholdValue, 0.0, 255.0); } /** * Threshold a imageware in two levels minLevel and maxLevel. * * All the pixels values strictly greater than 'thresholdValue' and are set * to maxLevel. The remaining values are set to minLevel. * * @param thresholdValue * double value given the threshold * @param minLevel * double value given the minimum level * @param maxLevel * double value given the maximum level */ public void threshold(double thresholdValue, double minLevel, double maxLevel) { float low = (float) (minLevel); float high = (float) (maxLevel); float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = ((double) (slice[k]) > thresholdValue ? high : low); } } } /** * Apply a soft thresholding. * * All the pixels values strictly greater than '-thresholdValue' and stricty * lower than 'thresholdValue' set to 0. The remaining positive values are * reduced by 'thresholdvalue'; the remaining negative values are augmented * by 'thresholdValue'. * * @param thresholdValue * double value given the threshold */ public void thresholdSoft(double thresholdValue) { float zero = (float) (0.0); double pixel; float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { pixel = (double) (slice[k]); slice[k] = (pixel <= -thresholdValue ? (float) (pixel + thresholdValue) : (pixel > thresholdValue ? (float) (pixel - thresholdValue) : zero)); } } } /** * Apply a hard thresholding. * * All the pixels values strictly greater than '-thresholdValue' and stricty * lower than 'thresholdValue' are set to 0. The remaining values are * unchanged. * * @param thresholdValue * double value given the threshold */ public void thresholdHard(double thresholdValue) { float zero = (float) (0.0); double pixel; float[] slice; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { pixel = (double) (slice[k]); if (pixel > -thresholdValue && pixel < thresholdValue) slice[k] = zero; } } } /** * Add a gaussian noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void addGaussianNoise(double amplitude) { Random rnd = new Random(); float[] slice = null; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] += (float) ((rnd.nextGaussian()) * amplitude); } } } /** * Add a uniform noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void addUniformNoise(double amplitude) { Random rnd = new Random(); float[] slice = null; amplitude *= 2.0; for (int z = 0; z < nz; z++) { slice = (float[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] += (float) ((rnd.nextDouble() - 0.5) * amplitude); } } } /** * Add a salt and pepper noise. * * @param amplitudeSalt * amplitude of the salt noise * @param amplitudePepper * amplitude of the pepper noise * @param percentageSalt * percentage of the salt noise * @param percentagePepper * percentage of the pepper noise */ public void addSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) { Random rnd = new Random(); int index, z; if (percentageSalt > 0) { double nbSalt = nxy * nz / percentageSalt; for (int k = 0; k < nbSalt; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((float[]) data[z])[index] += (float) (rnd.nextDouble() * amplitudeSalt); } } if (percentagePepper > 0) { double nbPepper = nxy * nz / percentagePepper; for (int k = 0; k < nbPepper; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((float[]) data[z])[index] -= (float) (rnd.nextDouble() * amplitudeSalt); } } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/FloatProcess.java b/src/bilib/src/imageware/FloatProcess.java new file mode 100644 index 0000000..bfdf542 --- /dev/null +++ b/src/bilib/src/imageware/FloatProcess.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import java.awt.Image; /** * Class FloatProcess. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class FloatProcess extends FloatPointwise implements Process { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected FloatProcess(int nx, int ny, int nz) { super(nx, ny, nz); } protected FloatProcess(Image image, int mode) { super(image, mode); } protected FloatProcess(ImageStack stack, int mode) { super(stack, mode); } protected FloatProcess(ImageStack stack, byte chan) { super(stack, chan); } protected FloatProcess(byte[] array, int mode) { super(array, mode); } protected FloatProcess(byte[][] array, int mode) { super(array, mode); } protected FloatProcess(byte[][][] array, int mode) { super(array, mode); } protected FloatProcess(short[] array, int mode) { super(array, mode); } protected FloatProcess(short[][] array, int mode) { super(array, mode); } protected FloatProcess(short[][][] array, int mode) { super(array, mode); } protected FloatProcess(float[] array, int mode) { super(array, mode); } protected FloatProcess(float[][] array, int mode) { super(array, mode); } protected FloatProcess(float[][][] array, int mode) { super(array, mode); } protected FloatProcess(double[] array, int mode) { super(array, mode); } protected FloatProcess(double[][] array, int mode) { super(array, mode); } protected FloatProcess(double[][][] array, int mode) { super(array, mode); } /** * Apply a separable gaussian smoothing over the image with the same * strengthness in all directions. To have a smmothing effect the * strengthness should be strictly greater than 0 and the size in the * considered directions should be greater strictly than 1. * * @param sigma * Strengthness of the smoothing */ public void smoothGaussian(double sigma) { smoothGaussian(sigma, sigma, sigma); } /** * Apply a separablegaussian smoothing over the image with an independant * strengthness in the different directions. To have a smmothing effect the * strengthness should be strictly greater than 0 and the size in the * considered directions should be greater strictly than 1. * * @param sigmaX * Strengthness of the smoothing in X axis * @param sigmaY * Strengthness of the smoothing in X axis * @param sigmaZ * Strengthness of the smoothing in X axis */ public void smoothGaussian(double sigmaX, double sigmaY, double sigmaZ) { int n = 3; double N = (double) n; double poles[] = new double[n]; if (nx > 1 && sigmaX > 0.0) { double s2 = sigmaX * sigmaX; double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2); poles[0] = poles[1] = poles[2] = alpha; double line[] = new double[nx]; for (int z = 0; z < nz; z++) { for (int y = 0; y < ny; y++) { getX(0, y, z, line); putX(0, y, z, Convolver.convolveIIR(line, poles)); } } } if (ny > 1 && sigmaY > 0.0) { double s2 = sigmaY * sigmaY; double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2); poles[0] = poles[1] = poles[2] = alpha; double line[] = new double[ny]; for (int x = 0; x < nx; x++) { for (int z = 0; z < nz; z++) { getY(x, 0, z, line); putY(x, 0, z, Convolver.convolveIIR(line, poles)); } } } if (nz > 1 && sigmaZ > 0.0) { double s2 = sigmaZ * sigmaZ; double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2); poles[0] = poles[1] = poles[2] = alpha; double line[] = new double[nz]; for (int y = 0; y < ny; y++) { for (int x = 0; x < nx; x++) { getZ(x, y, 0, line); putZ(x, y, 0, Convolver.convolveIIR(line, poles)); } } } } /** * Get the maximum of this imageware and a imageware. * * @param imageware * imageware to max */ public void max(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get the maximum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { if (((float[]) data[z])[k] < (float) tmp[k]) ((float[]) data[z])[k] = (float) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { if (((float[]) data[z])[k] < (float) tmp[k]) ((float[]) data[z])[k] = (float) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { if (((float[]) data[z])[k] < (float) tmp[k]) ((float[]) data[z])[k] = (float) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { if (((float[]) data[z])[k] < (float) tmp[k]) ((float[]) data[z])[k] = (float) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Get the minimum of this imageware and a imageware. * * @param imageware * imageware to min */ public void min(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get the minimum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { if (((float[]) data[z])[k] > (float) tmp[k]) ((float[]) data[z])[k] = (float) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { if (((float[]) data[z])[k] > (float) tmp[k]) ((float[]) data[z])[k] = (float) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { if (((float[]) data[z])[k] > (float) tmp[k]) ((float[]) data[z])[k] = (float) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { if (((float[]) data[z])[k] > (float) tmp[k]) ((float[]) data[z])[k] = (float) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Add a imageware to the current imageware. * * @param imageware * imageware to add */ public void add(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to add because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] += (float) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] += (float) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] += (float) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] += (float) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Multiply a imageware to the current imageware. * * @param imageware * imageware to multiply */ public void multiply(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to multiply because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] *= (float) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] *= (float) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] *= (float) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] *= (float) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Subtract a imageware to the current imageware. * * @param imageware * imageware to subtract */ public void subtract(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to subtract because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] -= (float) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] -= (float) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] -= (float) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] -= (float) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Divide a imageware to the current imageware. * * @param imageware * imageware to divide */ public void divide(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to divide because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] /= (float) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] /= (float) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] /= (float) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((float[]) data[z])[k] /= (float) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/FloatSet.java b/src/bilib/src/imageware/FloatSet.java new file mode 100644 index 0000000..706b922 --- /dev/null +++ b/src/bilib/src/imageware/FloatSet.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import java.awt.Image; /** * Class FloatSet. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class FloatSet extends FloatProcess implements ImageWare { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected FloatSet(int nx, int ny, int nz) { super(nx, ny, nz); } protected FloatSet(Image image, int mode) { super(image, mode); } protected FloatSet(ImageStack stack, int mode) { super(stack, mode); } protected FloatSet(ImageStack stack, byte chan) { super(stack, chan); } protected FloatSet(byte[] array, int mode) { super(array, mode); } protected FloatSet(byte[][] array, int mode) { super(array, mode); } protected FloatSet(byte[][][] array, int mode) { super(array, mode); } protected FloatSet(short[] array, int mode) { super(array, mode); } protected FloatSet(short[][] array, int mode) { super(array, mode); } protected FloatSet(short[][][] array, int mode) { super(array, mode); } protected FloatSet(float[] array, int mode) { super(array, mode); } protected FloatSet(float[][] array, int mode) { super(array, mode); } protected FloatSet(float[][][] array, int mode) { super(array, mode); } protected FloatSet(double[] array, int mode) { super(array, mode); } protected FloatSet(double[][] array, int mode) { super(array, mode); } protected FloatSet(double[][][] array, int mode) { super(array, mode); } /** * Duplicate the imageware. * * Create a new imageware with the same size, same type and same data than * the calling one. * * @return a duplicated version of this imageware */ public ImageWare duplicate() { ImageWare out = new FloatSet(nx, ny, nz); float[] outdata; for (int z = 0; z < nz; z++) { outdata = (float[]) (((FloatSet) out).data[z]); System.arraycopy(data[z], 0, outdata, 0, nxy); } return out; } /** * Replicate the imageware. * * Create a new imageware with the same size, same type than the calling * one. The data are not copied. * * @return a replicated version of this imageware */ public ImageWare replicate() { return new FloatSet(nx, ny, nz); } /** * Replicate the imageware. * * Create a new imageware with the same size and a specified type than the * calling one. The data are not copied. * * @param type * requested type * @return a replicated version of this imageware */ public ImageWare replicate(int type) { switch (type) { case ImageWare.BYTE: return new ByteSet(nx, ny, nz); case ImageWare.SHORT: return new ShortSet(nx, ny, nz); case ImageWare.FLOAT: return new FloatSet(nx, ny, nz); case ImageWare.DOUBLE: return new DoubleSet(nx, ny, nz); default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n" + "-------------------------------------------------------\n"); } } /** * Copy all the data of source in the current imageware. The source should * have the same size and same type than the calling one. * * @param source * a source imageware */ public void copy(ImageWare source) { if (nx != source.getSizeX()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size (" + nx + " != " + source.getSizeX() + ").\n" + "-------------------------------------------------------\n"); if (ny != source.getSizeY()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size (" + ny + " != " + source.getSizeY() + ").\n" + "-------------------------------------------------------\n"); if (nz != source.getSizeZ()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size (" + nz + " != " + source.getSizeZ() + ").\n" + "-------------------------------------------------------\n"); if (getType() != source.getType()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same type (" + getType() + " != " + source.getType() + ").\n" + "-------------------------------------------------------\n"); float[] src; for (int z = 0; z < nz; z++) { src = (float[]) (((FloatSet) source).data[z]); System.arraycopy(src, 0, data[z], 0, nxy); } } /** * convert the imageware in a specified type. * * Create a new imageware with the same size and converted data than the * calling one. * * @param type * indicates the type of the output * @return a converted version of this imageware */ public ImageWare convert(int type) { if (type == ImageWare.FLOAT) return duplicate(); ImageWare out = null; switch (type) { case ImageWare.BYTE: { float[] slice; out = new ByteSet(nx, ny, nz); byte[] outslice; for (int z = 0; z < nz; z++) { slice = ((float[]) data[z]); outslice = ((byte[]) ((ByteSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (byte) (slice[k]); } } } break; case ImageWare.SHORT: { float[] slice; out = new ShortSet(nx, ny, nz); short[] outslice; for (int z = 0; z < nz; z++) { slice = ((float[]) data[z]); outslice = ((short[]) ((ShortSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (short) (slice[k]); } } } break; case ImageWare.FLOAT: { float[] slice; out = new FloatSet(nx, ny, nz); float[] outslice; for (int z = 0; z < nz; z++) { slice = ((float[]) data[z]); outslice = ((float[]) ((FloatSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (float) (slice[k]); } } } break; case ImageWare.DOUBLE: { float[] slice; out = new DoubleSet(nx, ny, nz); double[] outslice; for (int z = 0; z < nz; z++) { slice = ((float[]) data[z]); outslice = ((double[]) ((DoubleSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (double) (slice[k]); } } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n" + "-------------------------------------------------------\n"); } return out; } /** * Print information of this ImageWare object. */ public void printInfo() { System.out.println("ImageWare object information"); System.out.println("Dimension: " + getDimension()); System.out.println("Size: [" + nx + ", " + ny + ", " + nz + "]"); System.out.println("TotalSize: " + getTotalSize()); System.out.println("Type: " + getTypeToString()); System.out.println("Maximun: " + getMaximum()); System.out.println("Minimun: " + getMinimum()); System.out.println("Mean: " + getMean()); System.out.println("Norm1: " + getNorm1()); System.out.println("Norm2: " + getNorm2()); System.out.println("Total: " + getTotal()); System.out.println(""); } /** * Show this ImageWare object. */ public void show() { String title = getTypeToString(); switch (getDimension()) { case 1: title += " line"; break; case 2: title += " image"; break; case 3: title += " volume"; break; } Display.show(title, this); // ImagePlus imp = new ImagePlus(title, buildImageStack()); // imp.show(); } /** * Show the data in ImagePlus object with a specify title. * * @param title * a string given the title of the window */ public void show(String title) { Display.show(title, this); // ImagePlus imp = new ImagePlus(title, buildImageStack()); // imp.show(); } /** * Return the minimum value of this imageware. * * @return the min value of this imageware */ public double getMinimum() { double min = Double.MAX_VALUE; float[] slice; for (int z = 0; z < nz; z++) { slice = ((float[]) data[z]); for (int k = 0; k < nxy; k++) if ((slice[k]) < min) min = slice[k]; } return min; } /** * Return the maximum value of this imageware. * * @return the max value of this imageware */ public double getMaximum() { double max = -Double.MAX_VALUE; float[] slice; for (int z = 0; z < nz; z++) { slice = ((float[]) data[z]); for (int k = 0; k < nxy; k++) if ((slice[k]) > max) max = slice[k]; } return max; } /** * Return the mean value of this imageware. * * @return the mean value of this imageware */ public double getMean() { return getTotal() / (nz * nxy); } /** * Return the norm value of order 1. * * @return the norm value of this imageware in L1 sense */ public double getNorm1() { double norm = 0.0; double value = 0; float[] slice; for (int z = 0; z < nz; z++) { slice = ((float[]) data[z]); for (int k = 0; k < nxy; k++) { value = (double) (slice[k]); norm += (value > 0.0 ? value : -value); } } return norm; } /** * Return the norm value of order 2. * * @return the norm value of this imageware in L2 sense */ public double getNorm2() { double norm = 0.0; float[] slice; for (int z = 0; z < nz; z++) { slice = ((float[]) data[z]); for (int k = 0; k < nxy; k++) norm += (slice[k]) * (slice[k]); } return norm; } /** * Return the sum of all pixel in this imageware. * * @return the total sum of all pixel in this imageware */ public double getTotal() { double total = 0.0; float[] slice; for (int z = 0; z < nz; z++) { slice = ((float[]) data[z]); for (int k = 0; k < nxy; k++) total += slice[k]; } return total; } /** * Return the the minumum [0] and the maximum [1] value of this imageware. * Faster routine than call one getMinimum() and then one getMaximum(). * * @return an array of two values, the min and the max values of the images */ public double[] getMinMax() { double max = -Double.MAX_VALUE; double min = Double.MAX_VALUE; float[] slice; for (int z = 0; z < nz; z++) { slice = ((float[]) data[z]); for (int k = 0; k < nxy; k++) { if ((slice[k]) > max) max = slice[k]; if ((slice[k]) < min) min = slice[k]; } } double minmax[] = { min, max }; return minmax; } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/ImageAccess.java b/src/bilib/src/imageware/ImageAccess.java new file mode 100644 index 0000000..2a32227 --- /dev/null +++ b/src/bilib/src/imageware/ImageAccess.java @@ -0,0 +1 @@ +package imageware; import ij.ImagePlus; import ij.process.ByteProcessor; import ij.process.ColorProcessor; import ij.process.FloatProcessor; import ij.process.ImageProcessor; import ij.process.ShortProcessor; /** * ImageAccess is an interface layer to facilitate the access to the pixels of * ImageJ images. Methods of ImageAccess provides an easy and robust way to * access to the pixels of images. * * The data are stored in an double array. Many methods get/put allows to access * to the data. If the user try to access outside of the image, the mirror * boundary conditions are applied. * * This version of ImageAccess is based on the imageware library. * * @author Daniel Sage Biomedical Imaging Group Swiss Federal Institute of * Technology Lausanne EPFL, CH-1015 Lausanne, Switzerland */ public class ImageAccess extends Object { public static final int PATTERN_SQUARE_3x3 = 0; public static final int PATTERN_CROSS_3x3 = 1; private ImageWare imageware = null; private int nx = 0; private int ny = 0; /** * Creates a new ImageAccess object from a 2D double array of pixels. The * size of the array determines the size of the image. * * @param array * an array of pixel (2D) */ public ImageAccess(double[][] array) { if (array == null) throw new ArrayStoreException("Constructor: array == null."); imageware = Builder.create(array); this.nx = imageware.getSizeX(); this.ny = imageware.getSizeY(); } /** * Creates a new object of the class ImageAccess from an ImageProcessor * object. * * ImageProcessor object contains the image data, the size and the type of * the image. The ImageProcessor is provided by ImageJ, it should by a * 8-bit, 16-bit. * * @param ip * an ImageProcessor object provided by ImageJ */ public ImageAccess(ImageProcessor ip) { if (ip == null) throw new ArrayStoreException("Constructor: ImageProcessor == null."); ImagePlus imp = new ImagePlus("", ip); if (ip instanceof ByteProcessor) imageware = Builder.create(imp, ImageWare.DOUBLE); else if (ip instanceof ShortProcessor) imageware = Builder.create(imp, ImageWare.DOUBLE); else if (ip instanceof FloatProcessor) imageware = Builder.create(imp, ImageWare.DOUBLE); this.nx = imageware.getSizeX(); this.ny = imageware.getSizeY(); } /** * Creates a new object of the class ImageAccess from an ColorProcessor * object. * * ImageProcessor object contains the image data, the size and the type of * the image. The ColorProcessor is provided by ImageJ, The ImageAccess * contains one plane (red, green or blue) selected with the colorPlane * parameter. * * @param cp * an ColorProcessor object * @param colorPlane * index of the color plane 0, 1 or 2 */ public ImageAccess(ColorProcessor cp, int colorPlane) { if (cp == null) throw new ArrayStoreException("Constructor: ColorProcessor == null."); if (colorPlane < 0) throw new ArrayStoreException("Constructor: colorPlane < 0."); if (colorPlane > 2) throw new ArrayStoreException("Constructor: colorPlane > 2."); this.nx = cp.getWidth(); this.ny = cp.getHeight(); ImagePlus imp = new ImagePlus("", cp); imageware = new DoubleSet(imp.getStack(), (byte) colorPlane); } /** * Creates a new object of the class tImageAccess. * * The size of the image are given as parameter. The data pixels are empty * and are not initialized. * * @param nx * the size of the image along the X-axis * @param ny * the size of the image along the Y-axis */ public ImageAccess(int nx, int ny) { imageware = new DoubleSet(nx, ny, 1); this.nx = nx; this.ny = ny; } /** * Return the imageware of the image. * * @return the imageware object */ public ImageWare getDataset() { return imageware; } /** * Return the width of the image. * * @return the image width */ public int getWidth() { return nx; } /** * Return the height of the image. * * @return the image height */ public int getHeight() { return ny; } /** * Return the maximum value of ImageAccess. * * @return the maximum value */ public double getMaximum() { return imageware.getMaximum(); } /** * Return the minimum value of ImageAccess. * * @return the minimum value */ public double getMinimum() { return imageware.getMinimum(); } /** * Return the mean value of ImageAccess. * * @return the mean value */ public double getMean() { return imageware.getMean(); } /** * Returns a copy of the pixel data organize in a 2D array. * * @return the 2D double array */ public double[][] getArrayPixels() { double[][] array = new double[nx][ny]; imageware.getXY(0, 0, 0, array); return array; } /** * Returns a reference to the pixel data in double (1D). * * @return the 1D double array */ public double[] getPixels() { return imageware.getSliceDouble(0); } /** * Create a FloatProcessor from the pixel data. * * @return the FloatProcessor */ public FloatProcessor createFloatProcessor() { FloatProcessor fp = new FloatProcessor(nx, ny); double[] pixels = getPixels(); int size = pixels.length; float[] fsrc = new float[size]; for (int k = 0; k < size; k++) fsrc[k] = (float) (pixels[k]); fp.setPixels(fsrc); return fp; } /** * Create a ByteProcessor from the pixel data. * * @return the ByteProcessor */ public ByteProcessor createByteProcessor() { ByteProcessor bp = new ByteProcessor(nx, ny); double[] pixels = getPixels(); int size = pixels.length; byte[] bsrc = new byte[size]; double p; for (int k = 0; k < size; k++) { p = pixels[k]; if (p < 0) p = 0.0; if (p > 255.0) p = 255.0; bsrc[k] = (byte) p; } bp.setPixels(bsrc); return bp; } /** * Create a new ImageAccess object by duplication of the current the * ImageAccess object. * * @return a new ImageAccess object **/ public ImageAccess duplicate() { double[][] array = new double[nx][ny]; imageware.getXY(0, 0, 0, array); ImageAccess ia = new ImageAccess(array); return ia; } /** * An ImageAccess object calls this method for getting the gray level of a * selected pixel. * * Mirror border conditions are applied. * * @param x * input, the integer x-coordinate of a pixel * @param y * input, the integer y-coordinate of a pixel * @return the gray level of the pixel (double) */ public double getPixel(int x, int y) { return imageware.getPixel(x, y, 0, ImageWare.MIRROR); } /** * An ImageAccess object calls this method for getting the gray level of a * selected pixel using a bilinear interpolation. The coordinates can be * given in double and the bilinear interpolation is applied the find the * gray level. * * Mirror border conditions are applied. * * @param x * input, the double x-coordinate of a pixel * @param y * input, the double y-coordinate of a pixel * @return the gray level of the pixel (double) */ public double getInterpolatedPixel(double x, double y) { return imageware.getInterpolatedPixel(x, y, 0, ImageWare.MIRROR); } /** * An ImageAccess object calls this method for getting a whole column of the * image. * * The column should already created with the correct size [ny]. * * @param x * input, the integer x-coordinate of a column * @param column * output, an array of the type double */ public void getColumn(int x, double[] column) { if (x < 0) throw new IndexOutOfBoundsException("getColumn: x < 0."); if (x >= nx) throw new IndexOutOfBoundsException("getColumn: x >= nx."); if (column == null) throw new ArrayStoreException("getColumn: column == null."); if (column.length != ny) throw new ArrayStoreException("getColumn: column.length != ny."); imageware.getBlockY(x, 0, 0, column, ImageWare.MIRROR); } /** * An ImageAccess object calls this method for getting a part of column. The * starting point is given by the y parameter and the ending determine by * the size of the column parameter. The column parameter should already * created. * * @param x * input, the integer x-coordinate of a column * @param y * input, starting point * @param column * output, an array of the type double */ public void getColumn(int x, int y, double[] column) { if (x < 0) throw new IndexOutOfBoundsException("getColumn: x < 0."); if (x >= nx) throw new IndexOutOfBoundsException("getColumn: x >= nx."); if (column == null) throw new ArrayStoreException("getColumn: column == null."); imageware.getBlockY(x, y, 0, column, ImageWare.MIRROR); } /** * An ImageAccess object calls this method for getting a whole row of the * image. * * The row should already created with the correct size [nx]. * * @param y * input, the integer y-coordinate of a row * @param row * output, an array of the type double */ public void getRow(int y, double[] row) { if (y < 0) throw new IndexOutOfBoundsException("getRow: y < 0."); if (y >= ny) throw new IndexOutOfBoundsException("getRow: y >= ny."); if (row == null) throw new ArrayStoreException("getColumn: row == null."); if (row.length != nx) throw new ArrayStoreException("getColumn: row.length != nx."); imageware.getBlockX(0, y, 0, row, ImageWare.MIRROR); } /** * An ImageAccess object calls this method for getting a part of row. The * starting point is given by the y parameter and the ending determine by * the size of the row parameter. The row parameter should already created. * * @param x * input, starting point * @param y * input, the integer y-coordinate of a row * @param row * output, an array of the type double */ public void getRow(int x, int y, double[] row) { if (y < 0) throw new IndexOutOfBoundsException("getRow: y < 0."); if (y >= ny) throw new IndexOutOfBoundsException("getRow: y >= ny."); if (row == null) throw new ArrayStoreException("getRow: row == null."); imageware.getBlockX(x, y, 0, row, ImageWare.MIRROR); } /** * An ImageAccess object calls this method for getting a neighborhood * arround a pixel position. * * The neigh parameter should already created. The size of the array * determines the neighborhood block. * * <br> * Mirror border conditions are applied. <br> * <br> * The pixel value of (x-n/2, y-n/2) is put into neigh[0][0] <br> * ... <br> * The pixel value of (x+n/2, y+n/2) is put into neigh[n-1][n-1] <br> * <br> * For example if neigh is a double[4][4]: <br> * The pixel value of (x-1, y-1) is put into neigh[0][0] <br> * The pixel value of (x , y ) is put into neigh[1][1] <br> * The pixel value of (x+1, y+1) is put into neigh[2][2] <br> * The pixel value of (x+2, y+2) is put into neigh[3][3] <br> * ... <br> * For example if neigh is a double[5][5]: <br> * The pixel value of (x-2, y-2) is put into neigh[0][0] <br> * The pixel value of (x-1, y-1) is put into neigh[1][1] <br> * The pixel value of (x , y ) is put into neigh[2][2] <br> * The pixel value of (x+1, y+1) is put into neigh[3][3] <br> * The pixel value of (x+2, y+2) is put into neigh[4][4] * * @param x * the integer x-coordinate of a selected central pixel * @param y * the integer y-coordinate of a selected central pixel * @param neigh * output, a 2D array s */ public void getNeighborhood(int x, int y, double neigh[][]) { imageware.getNeighborhoodXY(x, y, 0, neigh, ImageWare.MIRROR); } /** * An ImageAccess object calls this method for getting a neighborhood of a * predefined pattern around a selected pixel (x,y). <br> * The available patterns are: <br> * - a 3*3 block: PATTERN_SQUARE_3x3 (8-connect) <br> * - a 3*3 cross: PATTERN_CROSS_3x3 (4-connect) <br> * <br> * Mirror border conditions are applied. <br> * The pixel is arranged in a 1D array according the following rules: <br> * <br> * If the pattern is PATTERN_SQUARE_3x3 (8-connect) <br> * The pixel value of (x-1, y-1) are put into neigh[0] <br> * The pixel value of (x , y-1) are put into neigh[1] <br> * The pixel value of (x+1, y-1) are put into neigh[2] <br> * The pixel value of (x-1, y ) are put into neigh[3] <br> * The pixel value of (x , y ) are put into neigh[4] <br> * The pixel value of (x+1, y ) are put into neigh[5] <br> * The pixel value of (x-1, y+1) are put into neigh[6] <br> * The pixel value of (x , y+1) are put into neigh[7] <br> * The pixel value of (x+1, y+1) are put into neigh[8] <br> * <br> * If the pattern is PATTERN_CROSS_3x3 (4-connect) <br> * The pixel value of (x , y-1) are put into neigh[0] <br> * The pixel value of (x-1, y ) are put into neigh[1] <br> * The pixel value of (x , y ) are put into neigh[2] <br> * The pixel value of (x+1, y ) are put into neigh[3] <br> * The pixel value of (x , y+1) are put into neigh[4] <br> * <br> * The neigh should already created as a double array of 9 elements for the * PATTERN_SQUARE_3x3 or 5 elements for the PATTERN_CROSS_3x3. * * @param x * x-coordinate of a selected central pixel * @param y * y-coordinate of a selected central pixel * @param neigh * output, an array consisting of 9 or 5 elements * @param pattern * PATTERN_SQUARE_3x3 or PATTERN_CROSS_3x3. */ public void getPattern(int x, int y, double neigh[], int pattern) { if (neigh == null) throw new ArrayStoreException("getPattern: neigh == null."); double block[][] = new double[3][3]; imageware.getNeighborhoodXY(x, y, 0, block, ImageWare.MIRROR); switch (pattern) { case PATTERN_SQUARE_3x3: if (neigh.length != 9) throw new ArrayStoreException("getPattern: neigh.length != 9."); neigh[0] = block[0][0]; neigh[1] = block[1][0]; neigh[2] = block[2][0]; neigh[3] = block[0][1]; neigh[4] = block[1][1]; neigh[5] = block[2][1]; neigh[6] = block[0][2]; neigh[7] = block[1][2]; neigh[8] = block[2][2]; break; case PATTERN_CROSS_3x3: if (neigh.length != 5) throw new ArrayStoreException("getPattern: neigh.length != 5"); neigh[0] = block[1][0]; neigh[1] = block[0][1]; neigh[2] = block[1][1]; neigh[3] = block[2][1]; neigh[4] = block[0][1]; break; default: throw new ArrayStoreException("getPattern: unexpected pattern."); } } /** * An ImageAccess object calls this method to get a sub-image with the upper * left corner in the coordinate (x,y). * * The sub-image ouptut should be already created. * * @param x * x-coordinate in the source image * @param y * y-coordinate in the source image * @param output * an ImageAccess object with the sub-image; */ public void getSubImage(int x, int y, ImageAccess output) { if (output == null) throw new ArrayStoreException("getSubImage: output == null."); if (x < 0) throw new ArrayStoreException("getSubImage: Incompatible image size"); if (y < 0) throw new ArrayStoreException("getSubImage: Incompatible image size"); if (x >= nx) throw new ArrayStoreException("getSubImage: Incompatible image size"); if (y >= ny) throw new ArrayStoreException("getSubImage: Incompatible image size"); int nxcopy = output.getWidth(); int nycopy = output.getHeight(); double[][] neigh = new double[nxcopy][nycopy]; imageware.getBlockXY(x, y, 0, neigh, ImageWare.MIRROR); output.putArrayPixels(neigh); } /** * An ImageAccess object calls this method in order a value of the gray * level to be put to a position inside it given by the coordinates. * * @param x * input, the integer x-coordinate of a pixel * @param y * input, the integer y-coordinate of a pixel * @param value * input, a value of the gray level of the type double */ public void putPixel(int x, int y, double value) { if (x < 0) throw new IndexOutOfBoundsException("putPixel: x < 0"); if (x >= nx) throw new IndexOutOfBoundsException("putPixel: x >= nx"); if (y < 0) throw new IndexOutOfBoundsException("putPixel: y < 0"); if (y >= ny) throw new IndexOutOfBoundsException("putPixel: y >= ny"); imageware.putPixel(x, y, 0, value); } /** * An ImageAccess object calls this method to put a whole column in a * specified position into the image. * * @param x * input, the integer x-coordinate of a column * @param column * input, an array of the type double */ public void putColumn(int x, double[] column) { if (x < 0) throw new IndexOutOfBoundsException("putColumn: x < 0."); if (x >= nx) throw new IndexOutOfBoundsException("putColumn: x >= nx."); if (column == null) throw new ArrayStoreException("putColumn: column == null."); if (column.length != ny) throw new ArrayStoreException("putColumn: column.length != ny."); imageware.putBoundedY(x, 0, 0, column); } /** * An ImageAccess object calls this method to put a part of column into the * image. The starting poisition in given by y and the ending position is * determined by the size of the column array. * * @param x * input, the integer x-coordinate of a column * @param y * input, the integer y-coordinate of a column * @param column * input, an array of the type double */ public void putColumn(int x, int y, double[] column) { if (x < 0) throw new IndexOutOfBoundsException("putColumn: x < 0."); if (x >= nx) throw new IndexOutOfBoundsException("putColumn: x >= nx."); if (column == null) throw new ArrayStoreException("putColumn: column == null."); imageware.putBoundedY(x, y, 0, column); } /** * An ImageAccess object calls this method to put a whole row in a specified * position into the image. * * @param y * input, the integer x-coordinate of a column * @param row * input, an array of the type double */ public void putRow(int y, double[] row) { if (y < 0) throw new IndexOutOfBoundsException("putRow: y < 0."); if (y >= ny) throw new IndexOutOfBoundsException("putRow: y >= ny."); if (row == null) throw new ArrayStoreException("putRow: row == null."); if (row.length != nx) throw new ArrayStoreException("putRow: row.length != nx."); imageware.putBoundedX(0, y, 0, row); } /** * An ImageAccess object calls this method to put a part of row into the * image. The starting poisition in given by x and the ending position is * determined by the size of the row array. * * @param x * input, the integer x-coordinate of a column * @param y * input, the integer y-coordinate of a column * @param row * input, an array of the type double */ public void putRow(int x, int y, double[] row) { if (y < 0) throw new IndexOutOfBoundsException("putRow: y < 0."); if (y >= ny) throw new IndexOutOfBoundsException("putRow: y >= ny."); if (row == null) throw new ArrayStoreException("putRow: row == null."); imageware.putBoundedX(x, y, 0, row); } /** * An ImageAccess object calls this method in order to put an 2D array of * double in an ImageAccess. * * @param array * input, the double array */ public void putArrayPixels(double[][] array) { if (array == null) throw new IndexOutOfBoundsException("putArrayPixels: array == null."); imageware.putBoundedXY(0, 0, 0, array); } /** * An ImageAccess object calls this method to put a sub-image with the upper * left corner in the coordinate (x,y). * * The sub-image input should be already created. * * @param x * x-coordinate in the source image * @param y * y-coordinate in the source image * @param input * an ImageAccess object that we want to put; */ public void putSubImage(int x, int y, ImageAccess input) { if (input == null) throw new ArrayStoreException("putSubImage: input == null."); if (x < 0) throw new IndexOutOfBoundsException("putSubImage: x < 0."); if (y < 0) throw new IndexOutOfBoundsException("putSubImage: y < 0."); if (x >= nx) throw new IndexOutOfBoundsException("putSubImage: x >= nx."); if (y >= ny) throw new IndexOutOfBoundsException("putSubImage: y >= ny."); double[][] sub = input.getArrayPixels(); imageware.putBoundedXY(x, y, 0, sub); } /** * An ImageAccess object calls this method to set a constant value to all * pixels of the image. * * @param constant * a constant value */ public void setConstant(double constant) { imageware.fillConstant(constant); } /** * Stretches the contrast inside an image so that the gray levels are in the * range 0 to 255. */ public void normalizeContrast() { imageware.rescale(); } /** * Display an image at a specific position (x, y). * * @param title * a string for the title * @param loc * Point for the location */ public void show(String title, java.awt.Point loc) { FloatProcessor fp = createFloatProcessor(); fp.resetMinAndMax(); ImagePlus impResult = new ImagePlus(title, fp); ij.gui.ImageWindow window = impResult.getWindow(); window.setLocation(loc.x, loc.y); impResult.show(); } /** * Display an image. * * @param title * a string for the title of the window */ public void show(String title) { imageware.show(title); } /** * Compute the absolute value. */ public void abs() { imageware.abs(); } /** * Compute the square root of an ImageAccess. */ public void sqrt() { imageware.sqrt(); } /** * Raised an ImageAccess object to the power a. * * @param a * input */ public void pow(final double a) { imageware.pow(a); } /** * An ImageAccess object calls this method for adding a constant to each * pixel. * * @param constant * a constant to be added */ public void add(double constant) { imageware.add(constant); } /** * An ImageAccess object calls this method for multiplying a constant to * each pixel. * * @param constant * a constant to be multiplied */ public void multiply(final double constant) { imageware.multiply(constant); } /** * An ImageAccess object calls this method for adding a constant to each * pixel. * * @param constant * a constant to be added */ public void subtract(final double constant) { imageware.add(-constant); } /** * An ImageAccess object calls this method for dividing a constant to each * pixel. * * @param constant * a constant to be multiplied */ public void divide(final double constant) { if (constant == 0.0) throw new ArrayStoreException("divide: Divide by 0"); imageware.multiply(1.0 / constant); } /** * An ImageAccess object calls this method for adding two ImageAccess * objects. * * [this = im1 + im2] * * The resulting ImageAccess and the two operands should have the same size. * * @param im1 * an ImageAccess object to be added * @param im2 * an ImageAccess object to be added */ public void add(ImageAccess im1, ImageAccess im2) { if (im1.getWidth() != nx) throw new ArrayStoreException("add: incompatible size."); if (im1.getHeight() != ny) throw new ArrayStoreException("add: incompatible size."); if (im2.getWidth() != nx) throw new ArrayStoreException("add: incompatible size."); if (im2.getHeight() != ny) throw new ArrayStoreException("add: incompatible size."); imageware.copy(im1.getDataset()); imageware.add(im2.getDataset()); } /** * An ImageAccess object calls this method for multiplying two ImageAccess * objects. * * The resulting ImageAccess and the two operands should have the same size. * * [this = im1 * im2] * * @param im1 * an ImageAccess object to be multiplied * @param im2 * an ImageAccess object to be multiplied */ public void multiply(ImageAccess im1, ImageAccess im2) { if (im1.getWidth() != nx) throw new ArrayStoreException("multiply: incompatible size."); if (im1.getHeight() != ny) throw new ArrayStoreException("multiply: incompatible size."); if (im2.getWidth() != nx) throw new ArrayStoreException("multiply: incompatible size."); if (im2.getHeight() != ny) throw new ArrayStoreException("multiply: incompatible size."); imageware.copy(im1.getDataset()); imageware.multiply(im2.getDataset()); } /** * An ImageAccess object calls this method for subtracting two ImageAccess * objects. * * The resulting ImageAccess and the two operands should have the same size. * * [this = im1 - im2] * * @param im1 * an ImageAccess object to be subtracted * @param im2 * an ImageAccess object to be subtracted */ public void subtract(ImageAccess im1, ImageAccess im2) { if (im1.getWidth() != nx) throw new ArrayStoreException("subtract: incompatible size."); if (im1.getHeight() != ny) throw new ArrayStoreException("subtract: incompatible size."); if (im2.getWidth() != nx) throw new ArrayStoreException("subtract: incompatible size."); if (im2.getHeight() != ny) throw new ArrayStoreException("subtract: incompatible size."); imageware.copy(im1.getDataset()); imageware.subtract(im2.getDataset()); } /** * An ImageAccess object calls this method for dividing two ImageAccess * objects. * * [this = im1 / im2] * * The resulting ImageAccess and the two operands should have the same size. * * @param im1 * numerator * @param im2 * denominator */ public void divide(ImageAccess im1, ImageAccess im2) { if (im1.getWidth() != nx) throw new ArrayStoreException("divide: incompatible size."); if (im1.getHeight() != ny) throw new ArrayStoreException("divide: incompatible size."); if (im2.getWidth() != nx) throw new ArrayStoreException("divide: incompatible size."); if (im2.getHeight() != ny) throw new ArrayStoreException("divide: incompatible size."); imageware.copy(im1.getDataset()); imageware.divide(im2.getDataset()); } } // end of class ImageAccess \ No newline at end of file diff --git a/src/bilib/src/imageware/ImageWare.java b/src/bilib/src/imageware/ImageWare.java new file mode 100644 index 0000000..885f8ce --- /dev/null +++ b/src/bilib/src/imageware/ImageWare.java @@ -0,0 +1,62 @@ +package imageware; + +/** + * Class ImageWare. + * + * + * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de + * Lausanne, Lausanne, Switzerland + */ + +public interface ImageWare extends Process { + + public static final int UNDEFINED_TYPE = 0; + public static final int BYTE = 1; + public static final int SHORT = 2; + public static final int FLOAT = 3; + public static final int DOUBLE = 4; + + public static final byte UNDEFINED_BOUNDARY = 0; + public static final byte NONE = 1; + public static final byte MIRROR = 2; + public static final byte PERIODIC = 3; + + public static final int UNDEFINED = 0; + public static final int CREATE = 1; + public static final int WRAP = 2; + + public static final byte RED = 0; + public static final byte GREEN = 1; + public static final byte BLUE = 2; + + public ImageWare duplicate(); + + public ImageWare replicate(); + + public ImageWare replicate(int type); + + public void copy(ImageWare source); + + public ImageWare convert(int type); + + public void printInfo(); + + public void show(); + + public void show(String title); + + public double getMinimum(); + + public double getMaximum(); + + public double getMean(); + + public double getNorm1(); + + public double getNorm2(); + + public double getTotal(); + + public double[] getMinMax(); + +} diff --git a/src/bilib/src/imageware/Pointwise.java b/src/bilib/src/imageware/Pointwise.java new file mode 100644 index 0000000..60c7698 --- /dev/null +++ b/src/bilib/src/imageware/Pointwise.java @@ -0,0 +1,72 @@ +package imageware; + +import ij.ImageStack; + +/** + * Class Pointwise. + * + * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de + * Lausanne, Lausanne, Switzerland + */ + +public interface Pointwise extends Access { + public void fillConstant(double value); + + public void fillRamp(); + + public void fillGaussianNoise(double amplitude); + + public void fillUniformNoise(double amplitude); + + public void fillSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper); + + public ImageStack buildImageStack(); + + public void invert(); + + public void negate(); + + public void rescale(); + + public void clip(); + + public void clip(double minLevel, double maxLevel); + + public void rescale(double minLevel, double maxLevel); + + public void rescaleCenter(double minLevel, double maxLevel); + + public void abs(); + + public void log(); + + public void exp(); + + public void sqrt(); + + public void sqr(); + + public void pow(double a); + + public void add(double constant); + + public void multiply(double constant); + + public void subtract(double constant); + + public void divide(double constant); + + public void threshold(double thresholdValue); + + public void threshold(double thresholdValue, double minLevel, double maxLevel); + + public void thresholdHard(double thresholdValue); + + public void thresholdSoft(double thresholdValue); + + public void addGaussianNoise(double amplitude); + + public void addUniformNoise(double amplitude); + + public void addSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper); +} diff --git a/src/bilib/src/imageware/Process.java b/src/bilib/src/imageware/Process.java new file mode 100644 index 0000000..47209c4 --- /dev/null +++ b/src/bilib/src/imageware/Process.java @@ -0,0 +1,27 @@ +package imageware; + +/** + * Class Process. + * + * + * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de + * Lausanne, Lausanne, Switzerland + */ + +public interface Process extends Pointwise { + public void smoothGaussian(double sigma); + + public void smoothGaussian(double sigmaX, double sigmaY, double sigmaZ); + + public void max(ImageWare imageware); + + public void min(ImageWare imageware); + + public void add(ImageWare imageware); + + public void multiply(ImageWare imageware); + + public void subtract(ImageWare imageware); + + public void divide(ImageWare imageware); +} diff --git a/src/bilib/src/imageware/ShortAccess.java b/src/bilib/src/imageware/ShortAccess.java new file mode 100644 index 0000000..e24d4f6 --- /dev/null +++ b/src/bilib/src/imageware/ShortAccess.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import java.awt.Image; /** * Class ShortAccess. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class ShortAccess extends ShortBuffer implements Access { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected ShortAccess(int nx, int ny, int nz) { super(nx, ny, nz); } protected ShortAccess(Image image, int mode) { super(image, mode); } protected ShortAccess(ImageStack stack, int mode) { super(stack, mode); } protected ShortAccess(ImageStack stack, byte chan) { super(stack, chan); } protected ShortAccess(byte[] array, int mode) { super(array, mode); } protected ShortAccess(byte[][] array, int mode) { super(array, mode); } protected ShortAccess(byte[][][] array, int mode) { super(array, mode); } protected ShortAccess(short[] array, int mode) { super(array, mode); } protected ShortAccess(short[][] array, int mode) { super(array, mode); } protected ShortAccess(short[][][] array, int mode) { super(array, mode); } protected ShortAccess(float[] array, int mode) { super(array, mode); } protected ShortAccess(float[][] array, int mode) { super(array, mode); } protected ShortAccess(float[][][] array, int mode) { super(array, mode); } protected ShortAccess(double[] array, int mode) { super(array, mode); } protected ShortAccess(double[][] array, int mode) { super(array, mode); } protected ShortAccess(double[][][] array, int mode) { super(array, mode); } // ------------------------------------------------------------------ // // getPixel section // // ------------------------------------------------------------------ /** * Get a pixel at specific position without specific boundary conditions * * If the positions is outside of this imageware, the method return 0.0. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @return a pixel value */ public double getPixel(int x, int y, int z) { if (x >= nx) return 0.0; if (y >= ny) return 0.0; if (z >= nz) return 0.0; if (x < 0) return 0.0; if (y < 0) return 0.0; if (z < 0) return 0.0; return ((short[]) data[z])[x + y * nx] & 0xFFFF; } /** * Get a pixel at specific position with specific boundary conditions * * If the positions is outside of this imageware, the method apply the * boundary conditions to return a value. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @return a pixel value */ public double getPixel(int x, int y, int z, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a pixel \n" + "at the position (" + x + "," + y + "," + z + ".\n" + "-------------------------------------------------------\n"); } int xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } return ((short[]) data[zp])[xp + yp * nx] & 0xFFFF; } /** * Get a interpolated pixel value at specific position without specific * boundary conditions. * * If the positions is not on the pixel grid, the method return a * interpolated value of the pixel (linear interpolation). If the positions * is outside of this imageware, the method return 0.0. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @return a interpolated value */ public double getInterpolatedPixel(double x, double y, double z) { if (x > nx - 1) return 0.0; if (y > ny - 1) return 0.0; if (z > nz - 1) return 0.0; if (x < 0) return 0.0; if (y < 0) return 0.0; if (z < 0) return 0.0; double output = 0.0; /* * int i = (x >= 0.0 ? ((int)x) : ((int)x - 1)); int j = (y >= 0.0 ? * ((int)y) : ((int)y - 1)); int k = (z >= 0.0 ? ((int)z) : ((int)z - * 1)); */ int i = (x >= 0.0 ? ((int) x) : ((int) x - 1)); int j = (y >= 0.0 ? ((int) y) : ((int) y - 1)); int k = (z >= 0.0 ? ((int) z) : ((int) z - 1)); boolean fi = (i == nx - 1); boolean fj = (j == ny - 1); boolean fk = (k == nz - 1); int index = i + j * nx; switch (getDimension()) { case 1: double v1_0 = (double) (((short[]) data[k])[index] & 0xFFFF); double v1_1 = (fi ? v1_0 : (double) (((short[]) data[k])[index + 1] & 0xFFFF)); double dx1 = x - (double) i; return v1_1 * dx1 - v1_0 * (dx1 - 1.0); case 2: double v2_00 = (double) (((short[]) data[k])[index] & 0xFFFF); double v2_10 = (fi ? v2_00 : (double) (((short[]) data[k])[index + 1] & 0xFFFF)); double v2_01 = (fj ? v2_00 : (double) (((short[]) data[k])[index + nx] & 0xFFFF)); double v2_11 = (fi ? (fj ? v2_00 : v2_01) : (double) (((short[]) data[k])[index + 1 + nx] & 0xFFFF)); double dx2 = x - (double) i; double dy2 = y - (double) j; return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0))); case 3: double v3_000 = (double) (((short[]) data[k])[index] & 0xFFFF); double v3_100 = (fi ? v3_000 : (double) (((short[]) data[k])[index + 1] & 0xFFFF)); double v3_010 = (fj ? v3_000 : (double) (((short[]) data[k])[index + nx] & 0xFFFF)); double v3_110 = (fi ? (fj ? v3_000 : v3_010) : (double) (((short[]) data[k])[index + 1 + nx] & 0xFFFF)); double v3_001 = (fk ? v3_000 : (double) (((short[]) data[k + 1])[index] & 0xFFFF)); double v3_011 = (fk ? (fj ? v3_000 : v3_010) : (double) (((short[]) data[k + 1])[index + 1] & 0xFFFF)); double v3_101 = (fk ? (fi ? v3_000 : v3_100) : (double) (((short[]) data[k + 1])[index + nx] & 0xFFFF)); double v3_111 = (fk ? (fj ? (fi ? v3_000 : v3_100) : v3_110) : (double) (((short[]) data[k + 1])[index + 1 + nx] & 0xFFFF)); double dx3 = x - (double) i; double dy3 = y - (double) j; double dz3 = z - (double) k; double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0))); double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0))); return z2 * dz3 - z1 * (dz3 - 1.0); } return output; } /** * Get a interpolated pixel value at specific position with specific * boundary conditions. * * If the positions is not on the pixel grid, the method return a * interpolated value of the pixel (linear interpolation). If the positions * is outside of this imageware, the method apply the boundary conditions to * return a value. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis * @param boundaryConditions * MIRROR or PERIODIC boundary conditions * @return a interpolated value */ public double getInterpolatedPixel(double x, double y, double z, byte boundaryConditions) { double output = 0.0; int i = (x >= 0.0 ? ((int) x) : ((int) x - 1)); int j = (y >= 0.0 ? ((int) y) : ((int) y - 1)); int k = (z >= 0.0 ? ((int) z) : ((int) z - 1)); switch (getDimension()) { case 1: double v1_0 = getPixel(i, j, k, boundaryConditions); double v1_1 = getPixel(i + 1, j, k, boundaryConditions); double dx1 = x - (double) i; return v1_1 * dx1 - v1_0 * (dx1 - 1.0); case 2: double v2_00 = getPixel(i, j, k, boundaryConditions); double v2_10 = getPixel(i + 1, j, k, boundaryConditions); double v2_01 = getPixel(i, j + 1, k, boundaryConditions); double v2_11 = getPixel(i + 1, j + 1, k, boundaryConditions); double dx2 = x - (double) i; double dy2 = y - (double) j; return (dx2 * (v2_11 * dy2 - v2_10 * (dy2 - 1.0)) - (dx2 - 1.0) * (v2_01 * dy2 - v2_00 * (dy2 - 1.0))); case 3: double v3_000 = getPixel(i, j, k, boundaryConditions); double v3_100 = getPixel(i + 1, j, k, boundaryConditions); double v3_010 = getPixel(i, j + 1, k, boundaryConditions); double v3_110 = getPixel(i + 1, j + 1, k, boundaryConditions); double v3_001 = getPixel(i, j, k + 1, boundaryConditions); double v3_011 = getPixel(i + 1, j, k + 1, boundaryConditions); double v3_101 = getPixel(i, j + 1, k + 1, boundaryConditions); double v3_111 = getPixel(i + 1, j + 1, k + 1, boundaryConditions); double dx3 = x - (double) i; double dy3 = y - (double) j; double dz3 = z - (double) k; double z1 = (dx3 * (v3_110 * dy3 - v3_100 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_010 * dy3 - v3_000 * (dy3 - 1.0))); double z2 = (dx3 * (v3_111 * dy3 - v3_101 * (dy3 - 1.0)) - (dx3 - 1.0) * (v3_011 * dy3 - v3_001 * (dy3 - 1.0))); return z2 * dz3 - z1 * (dz3 - 1.0); } return output; } // ------------------------------------------------------------------ // // putPixel section // // ------------------------------------------------------------------ /** * Put a pixel at specific position * * If the positions is outside of this imageware, the method does nothing. * * @param x * position in the X axis * @param y * position in the Y axis * @param z * position in the Z axis */ public void putPixel(int x, int y, int z, double value) { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; if (x < 0) return; if (y < 0) return; if (z < 0) return; ((short[]) data[z])[x + y * nx] = (short) value; } // ------------------------------------------------------------------ // // getBounded section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (byte) (tmp[offset] & 0xFFFF); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (short) (tmp[offset] & 0xFFFF); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (float) (tmp[offset] & 0xFFFF); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to get into the imageware */ public void getBoundedX(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (double) (tmp[offset] & 0xFFFF); offset++; } } catch (Exception e) { throw_get("X", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (byte) (tmp[offset] & 0xFFFF); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (short) (tmp[offset] & 0xFFFF); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (float) (tmp[offset] & 0xFFFF); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to get into the imageware */ public void getBoundedY(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { buffer[i] = (double) (tmp[offset] & 0xFFFF); offset += nx; } } catch (Exception e) { throw_get("Y", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (byte) (((short[]) data[k])[offset] & 0xFFFF); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (short) (((short[]) data[k])[offset] & 0xFFFF); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (float) (((short[]) data[k])[offset] & 0xFFFF); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to get into the imageware */ public void getBoundedZ(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { buffer[i] = (double) (((short[]) data[k])[offset] & 0xFFFF); k++; } } catch (Exception e) { throw_get("Z", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); short[] tmp = (short[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (byte) (tmp[offset] & 0xFFFF); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); short[] tmp = (short[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (short) (tmp[offset] & 0xFFFF); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); short[] tmp = (short[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (float) (tmp[offset] & 0xFFFF); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to get into the imageware */ public void getBoundedXY(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); short[] tmp = (short[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (double) (tmp[offset] & 0xFFFF); offset++; } } } catch (Exception e) { throw_get("XY", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (byte) (((short[]) data[z])[offset] & 0xFFFF); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (short) (((short[]) data[z])[offset] & 0xFFFF); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (float) (((short[]) data[z])[offset] & 0xFFFF); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to get into the imageware */ public void getBoundedXZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (double) (((short[]) data[z])[offset] & 0xFFFF); offset++; } k++; } } catch (Exception e) { throw_get("YZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (byte) (((short[]) data[z])[offset] & 0xFFFF); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (short) (((short[]) data[z])[offset] & 0xFFFF); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (float) (((short[]) data[z])[offset] & 0xFFFF); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to get into the imageware */ public void getBoundedYZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j] = (double) (((short[]) data[z])[offset] & 0xFFFF); offset += nx; } k++; } } catch (Exception e) { throw_get("XZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, byte[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { short[] tmp = (short[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (byte) (tmp[offset] & 0xFFFF); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, short[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { short[] tmp = (short[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (short) (tmp[offset] & 0xFFFF); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, float[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { short[] tmp = (short[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (float) (tmp[offset] & 0xFFFF); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } /** * Get an array from the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 3D array to get into the imageware */ public void getBoundedXYZ(int x, int y, int z, double[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { short[] tmp = (short[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { buffer[i][j][k] = (double) (tmp[offset] & 0xFFFF); offset++; } } ko++; } } catch (Exception e) { throw_get("XYZ", "Bounded check", buffer, x, y, z); } } // ------------------------------------------------------------------ // // getBlock section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; short[] tmp = (short[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (byte) (tmp[xp + yp] & 0xFFFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; short[] tmp = (short[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (short) (tmp[xp + yp] & 0xFFFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; short[] tmp = (short[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (float) (tmp[xp + yp] & 0xFFFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockX(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; short[] tmp = (short[]) data[zp]; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (double) (tmp[xp + yp] & 0xFFFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } short[] tmp = (short[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (byte) (tmp[xp + yp * nx] & 0xFFFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } short[] tmp = (short[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (short) (tmp[xp + yp * nx] & 0xFFFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } short[] tmp = (short[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (float) (tmp[xp + yp * nx] & 0xFFFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockY(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } short[] tmp = (short[]) data[zp]; for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (double) (tmp[xp + yp * nx] & 0xFFFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (byte) (((short[]) data[zp])[xyp] & 0xFFFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (short) (((short[]) data[zp])[xyp] & 0xFFFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (float) (((short[]) data[zp])[xyp] & 0xFFFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockZ(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; for (int i = 0; i < leni; i++) { zp = z + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (double) (((short[]) data[zp])[xyp] & 0xFFFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } short[] tmp = (short[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (tmp[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } short[] tmp = (short[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (tmp[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } short[] tmp = (short[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (tmp[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the start position (x,y,z) in XY axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } short[] tmp = (short[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (tmp[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (((short[]) data[zp])[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (((short[]) data[zp])[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (((short[]) data[zp])[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (((short[]) data[zp])[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (byte) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (short) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (float) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in YZ axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } for (int j = 0; j < lenj; j++) { zp = z + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = y + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (double) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } short[] tmp = (short[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (byte) (tmp[xp + yp] & 0xFFFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } short[] tmp = (short[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (short) (tmp[xp + yp] & 0xFFFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } short[] tmp = (short[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (float) (tmp[xp + yp] & 0xFFFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the start position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getBlockXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) { int xperiod = 0; int yperiod = 0; int zperiod = 0; switch (boundaryConditions) { case ImageWare.MIRROR: xperiod = (nx <= 1 ? 1 : 2 * nx - 2); yperiod = (ny <= 1 ? 1 : 2 * ny - 2); zperiod = (nz <= 1 ? 1 : 2 * nz - 2); break; case ImageWare.PERIODIC: xperiod = nx; yperiod = ny; zperiod = nz; break; default: throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; for (int k = 0; k < lenk; k++) { zp = z + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } short[] tmp = (short[]) data[zp]; for (int j = 0; j < lenj; j++) { yp = y + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = x + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (double) (tmp[xp + yp] & 0xFFFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } // ------------------------------------------------------------------ // // getBlock section // // ------------------------------------------------------------------ /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; short[] tmp = ((short[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (byte) (tmp[xp + yp] & 0xFFFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; short[] tmp = ((short[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (short) (tmp[xp + yp] & 0xFFFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; short[] tmp = ((short[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (float) (tmp[xp + yp] & 0xFFFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in X axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodX(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; short[] tmp = ((short[]) data[zp]); for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i] = (double) (tmp[xp + yp] & 0xFFFF); } } catch (Exception e) { throw_get("X", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; short[] tmp = ((short[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (byte) (tmp[xp + yp * nx] & 0xFFFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; short[] tmp = ((short[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (short) (tmp[xp + yp * nx] & 0xFFFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; short[] tmp = ((short[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (float) (tmp[xp + yp * nx] & 0xFFFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Y axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodY(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; short[] tmp = ((short[]) data[zp]); for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i] = (double) (tmp[xp + yp * nx] & 0xFFFF); } } catch (Exception e) { throw_get("Y", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, byte[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (byte) (((short[]) data[zp])[xyp] & 0xFFFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, short[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (short) (((short[]) data[zp])[xyp] & 0xFFFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, float[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (float) (((short[]) data[zp])[xyp] & 0xFFFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in Z axis. * Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 1D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodZ(int x, int y, int z, double[] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } int xyp = xp + yp * nx; int zs = z - leni / 2; for (int i = 0; i < leni; i++) { zp = zs + i; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } buffer[i] = (double) (((short[]) data[zp])[xyp] & 0xFFFF); } } catch (Exception e) { throw_get("Z", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; short[] tmp = ((short[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (tmp[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; short[] tmp = ((short[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (tmp[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; short[] tmp = ((short[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (tmp[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * get an array into the imageware at the center position (x,y,z) in XY * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXY(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; zp = z; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } int xs = x - leni / 2; int ys = y - lenj / 2; short[] tmp = ((short[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (tmp[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XY", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (byte) (((short[]) data[zp])[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (short) (((short[]) data[zp])[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (float) (((short[]) data[zp])[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; yp = y; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; int xs = x - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - yp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j] = (double) (((short[]) data[zp])[xp + yp] & 0xFFFF); } } } catch (Exception e) { throw_get("XZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, byte[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (byte) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, short[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (short) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, float[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (float) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in YZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 2D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodYZ(int x, int y, int z, double[][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; xp = x; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } int ys = y - leni / 2; int zs = z - lenj / 2; for (int j = 0; j < lenj; j++) { zp = zs + j; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } for (int i = 0; i < leni; i++) { yp = ys + i; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } buffer[i][j] = (double) (((short[]) data[zp])[xp + yp * nx] & 0xFFFF); } } } catch (Exception e) { throw_get("YZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * byte 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, byte[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } short[] tmp = ((short[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (byte) (tmp[xp + yp] & 0xFFFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * short 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, short[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } short[] tmp = ((short[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (short) (tmp[xp + yp] & 0xFFFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * float 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, float[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } short[] tmp = ((short[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (float) (tmp[xp + yp] & 0xFFFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } /** * Get an array from the imageware at the center position (x,y,z) in XYZ * axis. Apply boundary conditions to get the outside area. * * @param x * X starting position to get the buffer * @param y * Y starting position to get the buffer * @param z * Z starting position to get the buffer * @param buffer * double 3D array to get into the imageware * @param boundaryConditions * mirror or periodic boundary conditions */ public void getNeighborhoodXYZ(int x, int y, int z, double[][][] buffer, byte boundaryConditions) { int xperiod = (boundaryConditions == ImageWare.MIRROR ? (nx <= 1 ? 1 : 2 * nx - 2) : nx); int yperiod = (boundaryConditions == ImageWare.MIRROR ? (ny <= 1 ? 1 : 2 * ny - 2) : ny); int zperiod = (boundaryConditions == ImageWare.MIRROR ? (nz <= 1 ? 1 : 2 * nz - 2) : nz); int xp, yp, zp; try { int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; int xs = x - leni / 2; int ys = y - lenj / 2; int zs = z - lenk / 2; for (int k = 0; k < lenk; k++) { zp = zs + k; while (zp < 0) zp += zperiod; while (zp >= nz) { zp = zperiod - zp; zp = (zp < 0 ? -zp : zp); } short[] tmp = ((short[]) data[zp]); for (int j = 0; j < lenj; j++) { yp = ys + j; while (yp < 0) yp += yperiod; while (yp >= ny) { yp = yperiod - yp; yp = (yp < 0 ? -yp : yp); } yp *= nx; for (int i = 0; i < leni; i++) { xp = xs + i; while (xp < 0) xp += xperiod; while (xp >= nx) { xp = xperiod - xp; xp = (xp < 0 ? -xp : xp); } buffer[i][j][k] = (double) (tmp[xp + yp] & 0xFFFF); } } } } catch (Exception e) { throw_get("XYZ", "Mirror or periodic boundaray conditions", buffer, x, y, z); } } // ------------------------------------------------------------------ // // putBounded section // // ------------------------------------------------------------------ /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i] & 0xFF); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i] & 0xFFFF); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i]); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in X axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putBoundedX(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int offset = (x + iinf) + (y) * nx; int leni = buffer.length; if (x + leni < 0) return; if (y < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i]); offset++; } } catch (Exception e) { throw_put("X", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i] & 0xFF); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i] & 0xFFFF); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i]); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Y axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putBoundedY(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int offset = (x) + (y + iinf) * nx; int leni = buffer.length; if (x < 0) return; if (y + leni < 0) return; if (z < 0) return; int isup = (y + leni >= ny ? ny - y : leni); short[] tmp = (short[]) data[z]; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i]); offset += nx; } } catch (Exception e) { throw_put("Y", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, byte[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((short[]) data[k])[offset] = (short) (buffer[i] & 0xFF); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, short[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((short[]) data[k])[offset] = (short) (buffer[i] & 0xFFFF); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, float[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((short[]) data[k])[offset] = (short) (buffer[i]); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in Z axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 1D array to put into the imageware */ public void putBoundedZ(int x, int y, int z, double[] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (z < 0 ? -z : 0); int k = z + iinf; int offset = (x) + (y) * nx; int leni = buffer.length; if (x < 0) return; if (y < 0) return; if (z + leni < 0) return; int isup = (z + leni >= nz ? nz - z : leni); for (int i = iinf; i < isup; i++) { ((short[]) data[k])[offset] = (short) (buffer[i]); k++; } } catch (Exception e) { throw_put("Z", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); short[] tmp = (short[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i][j] & 0xFF); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); short[] tmp = (short[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i][j] & 0xFFFF); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); short[] tmp = (short[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i][j]); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XY axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putBoundedXY(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); short[] tmp = (short[]) data[z]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i][j]); offset++; } } } catch (Exception e) { throw_put("XY", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((short[]) data[k])[offset] = (short) (buffer[i][j] & 0xFF); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((short[]) data[k])[offset] = (short) (buffer[i][j] & 0xFFFF); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((short[]) data[k])[offset] = (short) (buffer[i][j]); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putBoundedXZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x + leni < 0) return; if (y < 0) return; if (z + lenj < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y) * nx; for (int i = iinf; i < isup; i++) { ((short[]) data[k])[offset] = (short) (buffer[i][j]); offset++; } k++; } } catch (Exception e) { throw_put("YZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, byte[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((short[]) data[k])[offset] = (short) (buffer[i][j] & 0xFF); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, short[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((short[]) data[k])[offset] = (short) (buffer[i][j] & 0xFFFF); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, float[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((short[]) data[k])[offset] = (short) (buffer[i][j]); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in YZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 2D array to put into the imageware */ public void putBoundedYZ(int x, int y, int z, double[][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (y < 0 ? -y : 0); int jinf = (z < 0 ? -z : 0); int k = z + jinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; if (x < 0) return; if (y + leni < 0) return; if (z + lenj < 0) return; int isup = (y + leni >= ny ? ny - y : leni); int jsup = (z + lenj >= nz ? nz - z : lenj); for (int j = jinf; j < jsup; j++) { offset = x + (y + iinf) * nx; for (int i = iinf; i < isup; i++) { ((short[]) data[k])[offset] = (short) (buffer[i][j]); offset += nx; } k++; } } catch (Exception e) { throw_put("XZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * byte 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, byte[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { short[] tmp = (short[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i][j][k] & 0xFF); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * short 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, short[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { short[] tmp = (short[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i][j][k] & 0xFFFF); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * float 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, float[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { short[] tmp = (short[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i][j][k]); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } /** * Put an array into the imageware at the position (x,y,z) in XYZ axis. Copy * only the bounded area, intersection between the array and the imageware. * * @param x * X starting position to put the buffer * @param y * Y starting position to put the buffer * @param z * Z starting position to put the buffer * @param buffer * double 3D array to put into the imageware */ public void putBoundedXYZ(int x, int y, int z, double[][][] buffer) { try { if (x >= nx) return; if (y >= ny) return; if (z >= nz) return; int iinf = (x < 0 ? -x : 0); int jinf = (y < 0 ? -y : 0); int kinf = (z < 0 ? -z : 0); int ko = z + kinf; int offset = 0; int leni = buffer.length; int lenj = buffer[0].length; int lenk = buffer[0][0].length; if (x + leni < 0) return; if (y + lenj < 0) return; if (z + lenk < 0) return; int isup = (x + leni >= nx ? nx - x : leni); int jsup = (y + lenj >= ny ? ny - y : lenj); int ksup = (z + lenk >= nz ? nz - z : lenk); for (int k = kinf; k < ksup; k++) { short[] tmp = (short[]) data[ko]; for (int j = jinf; j < jsup; j++) { offset = (x + iinf) + (y + j) * nx; for (int i = iinf; i < isup; i++) { tmp[offset] = (short) (buffer[i][j][k]); offset++; } } ko++; } } catch (Exception e) { throw_put("XYZ", "Bounded check", buffer, x, y, z); } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/ShortBuffer.java b/src/bilib/src/imageware/ShortBuffer.java new file mode 100644 index 0000000..b4d24f0 --- /dev/null +++ b/src/bilib/src/imageware/ShortBuffer.java @@ -0,0 +1,2768 @@ +package imageware; + +import ij.ImageStack; +import ij.process.ByteProcessor; +import ij.process.ColorProcessor; +import ij.process.FloatProcessor; +import ij.process.ImageProcessor; +import ij.process.ShortProcessor; + +import java.awt.Image; +import java.awt.image.ImageObserver; +import java.awt.image.PixelGrabber; + +/** + * Class ShortBuffer. + * + * + * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de + * Lausanne, Lausanne, Switzerland + */ + +public class ShortBuffer implements Buffer { + + protected Object[] data = null; + protected int nx = 0; + protected int ny = 0; + protected int nz = 0; + protected int nxy = 0; + + /** + * Constructor of a empty 3D short buffer. + * + * @param nx + * size of the 3D buffer in the X axis + * @param ny + * size of the 3D buffer in the Y axis + * @param nz + * size of the 3D buffer in the Z axis + */ + protected ShortBuffer(int nx, int ny, int nz) { + this.nx = nx; + this.ny = ny; + this.nz = nz; + if (nx <= 0 || ny <= 0 || nz <= 0) + throw_constructor(nx, ny, nz); + allocate(); + } + + /** + * Constructor of a short buffer from a object Image of Java. + * + * @param image + * source to build a new imageware + */ + protected ShortBuffer(Image image, int mode) { + if (image == null) { + throw_constructor(); + } + ImageObserver observer = null; + this.nx = image.getWidth(observer); + this.ny = image.getHeight(observer); + this.nz = 1; + this.nxy = nx * ny; + byte[] pixels = new byte[nxy]; + PixelGrabber pg = new PixelGrabber(image, 0, 0, nx, ny, false); + try { + pg.grabPixels(); + pixels = (byte[]) (pg.getPixels()); + } + catch (Exception e) { + throw_constructor(); + } + allocate(); + for (int k = 0; k < nxy; k++) + ((short[]) data[0])[k] = (short) (pixels[k] & 0xFF); + } + + /** + * Constructor of a short buffer from a ImageStack. + * + * New data are allocated if the mode is CREATE, the imageware use the data + * of ImageJ if the mode is WRAP. + * + * @param stack + * source to build a new imageware + * @param mode + * WRAP or CREATE + */ + protected ShortBuffer(ImageStack stack, int mode) { + if (stack == null) { + throw_constructor(); + } + this.nx = stack.getWidth(); + this.ny = stack.getHeight(); + this.nz = stack.getSize(); + this.nxy = nx * ny; + switch (mode) { + case ImageWare.WRAP: + this.data = stack.getImageArray(); + break; + case ImageWare.CREATE: + allocate(); + ImageProcessor ip = stack.getProcessor(1); + if (ip instanceof ByteProcessor) { + Object[] vol = stack.getImageArray(); + for (int z = 0; z < nz; z++) { + byte[] slice = (byte[]) vol[z]; + for (int k = 0; k < nxy; k++) { + ((short[]) data[z])[k] = (short) (slice[k] & 0xFF); + } + } + } + else if (ip instanceof ShortProcessor) { + Object[] vol = stack.getImageArray(); + for (int z = 0; z < nz; z++) { + short[] slice = (short[]) vol[z]; + for (int k = 0; k < nxy; k++) { + ((short[]) data[z])[k] = (short) (slice[k] & 0xFFFF); + } + } + } + else if (ip instanceof FloatProcessor) { + Object[] vol = stack.getImageArray(); + for (int z = 0; z < nz; z++) { + float[] slice = (float[]) vol[z]; + for (int k = 0; k < nxy; k++) { + ((short[]) data[z])[k] = (short) slice[k]; + } + } + } + else if (ip instanceof ColorProcessor) { + double r, g, b; + int c; + ColorProcessor cp; + int[] pixels; + for (int z = 0; z < nz; z++) { + cp = (ColorProcessor) stack.getProcessor(z + 1); + pixels = (int[]) cp.getPixels(); + for (int k = 0; k < nxy; k++) { + c = pixels[k]; + r = (double) ((c & 0xFF0000) >> 16); + g = (double) ((c & 0xFF00) >> 8); + b = (double) ((c & 0xFF)); + ((short[]) data[z])[k] = (short) ((r + g + b) / 3.0); + } + } + } + else { + throw_constructor(); + } + break; + default: + throw_constructor(); + break; + } + } + + /** + * Constructor of a short buffer from a specific color channel of + * ImageStack. + * + * New data are always allocated. If it is a gray image the imageware is + * created and fill up with data of the source ImageStack. If it is a color + * image only the selected channel is used to create this imageware. + * + * @param stack + * source to build a new imageware + * @param channel + * RED, GREEN or BLUE + */ + protected ShortBuffer(ImageStack stack, byte channel) { + if (stack == null) { + throw_constructor(); + } + this.nx = stack.getWidth(); + this.ny = stack.getHeight(); + this.nz = stack.getSize(); + this.nxy = nx * ny; + allocate(); + ImageProcessor ip = stack.getProcessor(1); + if (ip instanceof ByteProcessor) { + Object[] vol = stack.getImageArray(); + for (int z = 0; z < nz; z++) { + byte[] slice = (byte[]) vol[z]; + for (int k = 0; k < nxy; k++) { + ((short[]) data[z])[k] = (short) (slice[k] & 0xFF); + } + } + } + else if (ip instanceof ShortProcessor) { + Object[] vol = stack.getImageArray(); + for (int z = 0; z < nz; z++) { + short[] slice = (short[]) vol[z]; + for (int k = 0; k < nxy; k++) { + ((short[]) data[z])[k] = (short) (slice[k] & 0xFFFF); + } + } + } + else if (ip instanceof FloatProcessor) { + Object[] vol = stack.getImageArray(); + for (int z = 0; z < nz; z++) { + float[] slice = (float[]) vol[z]; + for (int k = 0; k < nxy; k++) { + ((short[]) data[z])[k] = (short) slice[k]; + } + } + } + else if (ip instanceof ColorProcessor) { + ColorProcessor cp; + int[] pixels; + for (int z = 0; z < nz; z++) { + cp = (ColorProcessor) stack.getProcessor(z + 1); + pixels = (int[]) cp.getPixels(); + switch (channel) { + case ImageWare.RED: + for (int k = 0; k < nxy; k++) { + ((short[]) data[z])[k] = (short) ((pixels[k] & 0xFF0000) >> 16); + } + break; + case ImageWare.GREEN: + for (int k = 0; k < nxy; k++) { + ((short[]) data[z])[k] = (short) ((pixels[k] & 0xFF00) >> 8); + } + break; + case ImageWare.BLUE: + for (int k = 0; k < nxy; k++) { + ((short[]) data[z])[k] = (short) (pixels[k] & 0xFF); + } + break; + default: + throw_constructor(); + } + } + } + else { + throw_constructor(); + } + } + + /** + * Constructor of a short buffer from a byte array. + * + * @param array + * source to build this new imageware + */ + protected ShortBuffer(byte[] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = 1; + this.nz = 1; + allocate(); + putX(0, 0, 0, array); + } + + /** + * Constructor of a short buffer from a byte array. + * + * @param array + * source to build this new imageware + */ + protected ShortBuffer(byte[][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = 1; + allocate(); + putXY(0, 0, 0, array); + } + + /** + * Constructor of a short buffer from a byte array. + * + * @param array + * source to build this new imageware + */ + protected ShortBuffer(byte[][][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = array[0][0].length; + allocate(); + putXYZ(0, 0, 0, array); + } + + /** + * Constructor of a short buffer from a short array. + * + * @param array + * source to build this new imageware + */ + protected ShortBuffer(short[] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = 1; + this.nz = 1; + allocate(); + putX(0, 0, 0, array); + } + + /** + * Constructor of a short buffer from a short array. + * + * @param array + * source to build this new imageware + */ + protected ShortBuffer(short[][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = 1; + allocate(); + putXY(0, 0, 0, array); + } + + /** + * Constructor of a short buffer from a short array. + * + * @param array + * source to build this new imageware + */ + protected ShortBuffer(short[][][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = array[0][0].length; + allocate(); + putXYZ(0, 0, 0, array); + } + + /** + * Constructor of a short buffer from a float array. + * + * @param array + * source to build this new imageware + */ + protected ShortBuffer(float[] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = 1; + this.nz = 1; + allocate(); + putX(0, 0, 0, array); + } + + /** + * Constructor of a short buffer from a float array. + * + * @param array + * source to build this new imageware + */ + protected ShortBuffer(float[][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = 1; + allocate(); + putXY(0, 0, 0, array); + } + + /** + * Constructor of a short buffer from a float array. + * + * @param array + * source to build this new imageware + */ + protected ShortBuffer(float[][][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = array[0][0].length; + allocate(); + putXYZ(0, 0, 0, array); + } + + /** + * Constructor of a short buffer from a double array. + * + * @param array + * source to build this new imageware + */ + protected ShortBuffer(double[] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = 1; + this.nz = 1; + allocate(); + putX(0, 0, 0, array); + } + + /** + * Constructor of a short buffer from a double array. + * + * @param array + * source to build this new imageware + */ + protected ShortBuffer(double[][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = 1; + allocate(); + putXY(0, 0, 0, array); + } + + /** + * Constructor of a short buffer from a double array. + * + * @param array + * source to build this new imageware + */ + protected ShortBuffer(double[][][] array, int mode) { + if (array == null) { + throw_constructor(); + } + this.nx = array.length; + this.ny = array[0].length; + this.nz = array[0][0].length; + allocate(); + putXYZ(0, 0, 0, array); + } + + /** + * Return the type of this imageware. + * + * @return the type of this imageware + */ + public int getType() { + return ImageWare.SHORT; + } + + /** + * Return the type of this imageware in a string format. + * + * @return the type of this imageware translated in a string format + */ + public String getTypeToString() { + return "Short"; + } + + /** + * Return the number of dimension of this imageware (1, 2 or 3). + * + * @return the number of dimension of this imageware + */ + public int getDimension() { + int dims = 0; + dims += (nx > 1 ? 1 : 0); + dims += (ny > 1 ? 1 : 0); + dims += (nz > 1 ? 1 : 0); + return dims; + } + + /** + * Return the size of the imageware int[0] : x, int[1] : y, int[2] : z. + * + * @return an array given the size of the imageware + */ + public int[] getSize() { + int[] size = { nx, ny, nz }; + return size; + } + + /** + * Return the size in the X axis. + * + * @return the size in the X axis + */ + public int getSizeX() { + return nx; + } + + /** + * Return the size in the Y axis. + * + * @return the size in the Y axis + */ + public int getSizeY() { + return ny; + } + + /** + * Return the size in the Z axis. + * + * @return the size in the Z axis + */ + public int getSizeZ() { + return nz; + } + + /** + * Return the size in the X axis. + * + * @return the size in the X axis + */ + public int getWidth() { + return nx; + } + + /** + * Return the size in the Y axis. + * + * @return the size in the Y axis + */ + public int getHeight() { + return ny; + } + + /** + * Return the size in the Z axis. + * + * @return the size in the Z axis + */ + public int getDepth() { + return nz; + } + + /** + * Return the number of pixels in the imageware. + * + * @return number of pixels in the imageware + */ + public int getTotalSize() { + return nxy * nz; + } + + /** + * Return true is this imageware has the same size the imageware given as + * parameter. + * + * @param imageware + * imageware to be compared + * @return true if the imageware of the same size than this imageware + */ + public boolean isSameSize(ImageWare imageware) { + if (nx != imageware.getSizeX()) + return false; + if (ny != imageware.getSizeY()) + return false; + if (nz != imageware.getSizeZ()) + return false; + return true; + } + + // ------------------------------------------------------------------ + // + // put Section + // + // ------------------------------------------------------------------ + + /** + * Put an array into the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putX(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + double buf[] = new double[bnx]; + buffer.getX(0, 0, 0, buf); + putX(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putY(int x, int y, int z, ImageWare buffer) { + int bny = buffer.getSizeY(); + double buf[] = new double[bny]; + buffer.getY(0, 0, 0, buf); + putY(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putZ(int x, int y, int z, ImageWare buffer) { + int bnz = buffer.getSizeZ(); + double buf[] = new double[bnz]; + buffer.getZ(0, 0, 0, buf); + putZ(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putXY(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + int bny = buffer.getSizeY(); + double buf[][] = new double[bnx][bny]; + buffer.getXY(0, 0, 0, buf); + putXY(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putXZ(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + int bnz = buffer.getSizeZ(); + double buf[][] = new double[bnx][bnz]; + buffer.getXZ(0, 0, 0, buf); + putXZ(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putYZ(int x, int y, int z, ImageWare buffer) { + int bny = buffer.getSizeY(); + int bnz = buffer.getSizeZ(); + double buf[][] = new double[bny][bnz]; + buffer.getYZ(0, 0, 0, buf); + putYZ(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * ImageWare object to put into the imageware + */ + public void putXYZ(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + int bny = buffer.getSizeY(); + int bnz = buffer.getSizeZ(); + double buf[][][] = new double[bnx][bny][bnz]; + buffer.getXYZ(0, 0, 0, buf); + putXYZ(x, y, z, buf); + } + + /** + * Put an array into the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byte 1D array to put into the imageware + */ + public void putX(int x, int y, int z, byte[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + + for (int i = 0; i < leni; i++) { + tmp[offset] = (short) (buffer[i] & 0xFF); + offset++; + } + } + catch (Exception e) { + throw_put("X", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * short 1D array to put into the imageware + */ + public void putX(int x, int y, int z, short[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + + System.arraycopy(buffer, 0, tmp, offset, leni); + } + catch (Exception e) { + throw_put("X", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * float 1D array to put into the imageware + */ + public void putX(int x, int y, int z, float[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + + for (int i = 0; i < leni; i++) { + tmp[offset] = (short) (buffer[i]); + offset++; + } + } + catch (Exception e) { + throw_put("X", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * double 1D array to put into the imageware + */ + public void putX(int x, int y, int z, double[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + + for (int i = 0; i < leni; i++) { + tmp[offset] = (short) (buffer[i]); + offset++; + } + } + catch (Exception e) { + throw_put("X", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byte 1D array to put into the imageware + */ + public void putY(int x, int y, int z, byte[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + for (int i = 0; i < leni; i++) { + tmp[offset] = (short) (buffer[i] & 0xFF); + offset += nx; + } + } + catch (Exception e) { + throw_put("Y", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * short 1D array to put into the imageware + */ + public void putY(int x, int y, int z, short[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + for (int i = 0; i < leni; i++) { + tmp[offset] = (short) (buffer[i] & 0xFFFF); + offset += nx; + } + } + catch (Exception e) { + throw_put("Y", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * float 1D array to put into the imageware + */ + public void putY(int x, int y, int z, float[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + for (int i = 0; i < leni; i++) { + tmp[offset] = (short) (buffer[i]); + offset += nx; + } + } + catch (Exception e) { + throw_put("Y", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * double 1D array to put into the imageware + */ + public void putY(int x, int y, int z, double[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + for (int i = 0; i < leni; i++) { + tmp[offset] = (short) (buffer[i]); + offset += nx; + } + } + catch (Exception e) { + throw_put("Y", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * bybytete 1D array to put into the imageware + */ + public void putZ(int x, int y, int z, byte[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + ((short[]) data[z])[offset] = (short) (buffer[i] & 0xFF); + z++; + } + } + catch (Exception e) { + throw_put("Z", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byshortte 1D array to put into the imageware + */ + public void putZ(int x, int y, int z, short[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + ((short[]) data[z])[offset] = (short) (buffer[i] & 0xFFFF); + z++; + } + } + catch (Exception e) { + throw_put("Z", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byfloatte 1D array to put into the imageware + */ + public void putZ(int x, int y, int z, float[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + ((short[]) data[z])[offset] = (short) (buffer[i]); + z++; + } + } + catch (Exception e) { + throw_put("Z", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * bydoublete 1D array to put into the imageware + */ + public void putZ(int x, int y, int z, double[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + ((short[]) data[z])[offset] = (short) (buffer[i]); + z++; + } + } + catch (Exception e) { + throw_put("Z", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byte 2D array to put into the imageware + */ + public void putXY(int x, int y, int z, byte[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (short) (buffer[i][j] & 0xFF); + } + } + } + catch (Exception e) { + throw_put("XY", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * short 2D array to put into the imageware + */ + public void putXY(int x, int y, int z, short[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (short) (buffer[i][j] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_put("XY", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * float 2D array to put into the imageware + */ + public void putXY(int x, int y, int z, float[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (short) (buffer[i][j]); + } + } + } + catch (Exception e) { + throw_put("XY", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * double 2D array to put into the imageware + */ + public void putXY(int x, int y, int z, double[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (short) (buffer[i][j]); + } + } + } + catch (Exception e) { + throw_put("XY", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byte 2D array to put into the imageware + */ + public void putXZ(int x, int y, int z, byte[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + j * nx; + for (int i = 0; i < leni; i++, offset++) { + ((short[]) data[z])[offset] = (short) (buffer[i][j] & 0xFF); + } + } + } + catch (Exception e) { + throw_put("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * short 2D array to put into the imageware + */ + public void putXZ(int x, int y, int z, short[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + j * nx; + for (int i = 0; i < leni; i++, offset++) { + ((short[]) data[z])[offset] = (short) (buffer[i][j] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_put("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * float 2D array to put into the imageware + */ + public void putXZ(int x, int y, int z, float[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + j * nx; + for (int i = 0; i < leni; i++, offset++) { + ((short[]) data[z])[offset] = (short) (buffer[i][j]); + } + } + } + catch (Exception e) { + throw_put("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * double 2D array to put into the imageware + */ + public void putXZ(int x, int y, int z, double[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + j * nx; + for (int i = 0; i < leni; i++, offset++) { + ((short[]) data[z])[offset] = (short) (buffer[i][j]); + } + } + } + catch (Exception e) { + throw_put("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byte 2D array to put into the imageware + */ + public void putYZ(int x, int y, int z, byte[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) + for (int i = 0; i < leni; i++, offset += nx) { + ((short[]) data[z])[offset] = (short) (buffer[i][j] & 0xFF); + } + } + catch (Exception e) { + throw_put("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * short 2D array to put into the imageware + */ + public void putYZ(int x, int y, int z, short[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) + for (int i = 0; i < leni; i++, offset += nx) { + ((short[]) data[z])[offset] = (short) (buffer[i][j] & 0xFFFF); + } + } + catch (Exception e) { + throw_put("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * float 2D array to put into the imageware + */ + public void putYZ(int x, int y, int z, float[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) + for (int i = 0; i < leni; i++, offset += nx) { + ((short[]) data[z])[offset] = (short) (buffer[i][j]); + } + } + catch (Exception e) { + throw_put("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * double 2D array to put into the imageware + */ + public void putYZ(int x, int y, int z, double[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) + for (int i = 0; i < leni; i++, offset += nx) { + ((short[]) data[z])[offset] = (short) (buffer[i][j]); + } + } + catch (Exception e) { + throw_put("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * byte 3D array to put into the imageware + */ + public void putXYZ(int x, int y, int z, byte[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (short) (buffer[i][j][k] & 0xFF); + } + } + } + } + catch (Exception e) { + throw_put("XYZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * short 3D array to put into the imageware + */ + public void putXYZ(int x, int y, int z, short[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (short) (buffer[i][j][k] & 0xFFFF); + } + } + } + } + catch (Exception e) { + throw_put("XYZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * float 3D array to put into the imageware + */ + public void putXYZ(int x, int y, int z, float[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (short) (buffer[i][j][k]); + } + } + } + } + catch (Exception e) { + throw_put("XYZ", "No check", buffer, x, y, z); + } + } + + /** + * Put an array into the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to put the buffer + * @param y + * Y starting position to put the buffer + * @param z + * Z starting position to put the buffer + * @param buffer + * double 3D array to put into the imageware + */ + public void putXYZ(int x, int y, int z, double[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + tmp[offset] = (short) (buffer[i][j][k]); + } + } + } + } + catch (Exception e) { + throw_put("XYZ", "No check", buffer, x, y, z); + } + } + + // ------------------------------------------------------------------ + // + // get Section + // + // ------------------------------------------------------------------ + + /** + * Get an array from the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the imageware + */ + public void getX(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + double buf[] = new double[bnx]; + getX(x, y, z, buf); + buffer.putX(0, 0, 0, buf); + } + + /** + * Get an array from the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the imageware + */ + public void getY(int x, int y, int z, ImageWare buffer) { + int bny = buffer.getSizeY(); + double buf[] = new double[bny]; + getY(x, y, z, buf); + buffer.putY(0, 0, 0, buf); + } + + /** + * Get an array from the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the imageware + */ + public void getZ(int x, int y, int z, ImageWare buffer) { + int bnz = buffer.getSizeZ(); + double buf[] = new double[bnz]; + getZ(x, y, z, buf); + buffer.putZ(0, 0, 0, buf); + } + + /** + * get an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the imageware + */ + public void getXY(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + int bny = buffer.getSizeY(); + double buf[][] = new double[bnx][bny]; + getXY(x, y, z, buf); + buffer.putXY(0, 0, 0, buf); + } + + /** + * Get an array from the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the imageware + */ + public void getXZ(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + int bnz = buffer.getSizeZ(); + double buf[][] = new double[bnx][bnz]; + getXZ(x, y, z, buf); + buffer.putXZ(0, 0, 0, buf); + } + + /** + * Get an array from the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the datase + */ + public void getYZ(int x, int y, int z, ImageWare buffer) { + int bny = buffer.getSizeY(); + int bnz = buffer.getSizeZ(); + double buf[][] = new double[bny][bnz]; + getYZ(x, y, z, buf); + buffer.putYZ(0, 0, 0, buf); + } + + /** + * Get an array from the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * ImageWare object to get into the imageware + */ + public void getXYZ(int x, int y, int z, ImageWare buffer) { + int bnx = buffer.getSizeX(); + int bny = buffer.getSizeY(); + int bnz = buffer.getSizeZ(); + double buf[][][] = new double[bnx][bny][bnz]; + getXYZ(x, y, z, buf); + buffer.putXYZ(0, 0, 0, buf); + } + + /** + * Get an array from the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 1D array to get into the imageware + */ + public void getX(int x, int y, int z, byte[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + + for (int i = 0; i < leni; i++) { + buffer[i] = (byte) (tmp[offset] & 0xffff); + offset++; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 1D array to get into the imageware + */ + public void getX(int x, int y, int z, short[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + + System.arraycopy(tmp, offset, buffer, 0, leni); + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 1D array to get into the imageware + */ + public void getX(int x, int y, int z, float[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + + for (int i = 0; i < leni; i++) { + buffer[i] = (float) (tmp[offset] & 0xffff); + offset++; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in X axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 1D array to get into the imageware + */ + public void getX(int x, int y, int z, double[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + + for (int i = 0; i < leni; i++) { + buffer[i] = (double) (tmp[offset] & 0xffff); + offset++; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 1D array to get into the imageware + */ + public void getY(int x, int y, int z, byte[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + for (int i = 0; i < leni; i++) { + buffer[i] = (byte) (tmp[offset] & 0xFFFF); + offset += nx; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 1D array to get into the imageware + */ + public void getY(int x, int y, int z, short[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + for (int i = 0; i < leni; i++) { + buffer[i] = (short) (tmp[offset] & 0xFFFF); + offset += nx; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 1D array to get into the imageware + */ + public void getY(int x, int y, int z, float[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + for (int i = 0; i < leni; i++) { + buffer[i] = (float) (tmp[offset] & 0xFFFF); + offset += nx; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Y axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 1D array to get into the imageware + */ + public void getY(int x, int y, int z, double[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + short[] tmp = (short[]) data[z]; + for (int i = 0; i < leni; i++) { + buffer[i] = (double) (tmp[offset] & 0xFFFF); + offset += nx; + } + } + catch (Exception e) { + throw_get("X", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 1D array to get into the imageware + */ + public void getZ(int x, int y, int z, byte[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + buffer[i] = (byte) (((short[]) data[z])[offset] & 0xFFFF); + z++; + } + } + catch (Exception e) { + throw_get("Y", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 1D array to get into the imageware + */ + public void getZ(int x, int y, int z, short[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + buffer[i] = (short) (((short[]) data[z])[offset] & 0xFFFF); + z++; + } + } + catch (Exception e) { + throw_get("Y", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 1D array to get into the imageware + */ + public void getZ(int x, int y, int z, float[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + buffer[i] = (float) (((short[]) data[z])[offset] & 0xFFFF); + z++; + } + } + catch (Exception e) { + throw_get("Y", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in Z axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 1D array to get into the imageware + */ + public void getZ(int x, int y, int z, double[] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + for (int i = 0; i < leni; i++) { + buffer[i] = (double) (((short[]) data[z])[offset] & 0xFFFF); + z++; + } + } + catch (Exception e) { + throw_get("Y", "No check", buffer, x, y, z); + } + } + + /** + * get an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 2D array to get into the imageware + */ + public void getXY(int x, int y, int z, byte[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (byte) (tmp[offset] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_get("XY", "No check", buffer, x, y, z); + } + } + + /** + * get an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 2D array to get into the imageware + */ + public void getXY(int x, int y, int z, short[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (short) (tmp[offset] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_get("XY", "No check", buffer, x, y, z); + } + } + + /** + * get an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 2D array to get into the imageware + */ + public void getXY(int x, int y, int z, float[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (float) (tmp[offset] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_get("XY", "No check", buffer, x, y, z); + } + } + + /** + * get an array into the imageware at the position (x,y,z) in XY axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 2D array to get into the imageware + */ + public void getXY(int x, int y, int z, double[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (y + j) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (double) (tmp[offset] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_get("XY", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 2D array to get into the imageware + */ + public void getXZ(int x, int y, int z, byte[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + y * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (byte) (((short[]) data[z])[offset] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_get("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 2D array to get into the imageware + */ + public void getXZ(int x, int y, int z, short[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + y * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (short) (((short[]) data[z])[offset] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_get("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 2D array to get into the imageware + */ + public void getXZ(int x, int y, int z, float[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + y * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (float) (((short[]) data[z])[offset] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_get("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 2D array to get into the imageware + */ + public void getXZ(int x, int y, int z, double[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++) { + offset = x + y * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j] = (double) (((short[]) data[z])[offset] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_get("XZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 2D array to get into the datase + */ + public void getYZ(int x, int y, int z, byte[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { + for (int i = 0; i < leni; i++, offset += nx) { + buffer[i][j] = (byte) (((short[]) data[z])[offset] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_get("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 2D array to get into the datase + */ + public void getYZ(int x, int y, int z, short[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { + for (int i = 0; i < leni; i++, offset += nx) { + buffer[i][j] = (short) (((short[]) data[z])[offset] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_get("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 2D array to get into the datase + */ + public void getYZ(int x, int y, int z, float[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { + for (int i = 0; i < leni; i++, offset += nx) { + buffer[i][j] = (float) (((short[]) data[z])[offset] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_get("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in YZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 2D array to get into the datase + */ + public void getYZ(int x, int y, int z, double[][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + for (int j = 0; j < lenj; j++, z++, offset = (x + nx * y)) { + for (int i = 0; i < leni; i++, offset += nx) { + buffer[i][j] = (double) (((short[]) data[z])[offset] & 0xFFFF); + } + } + } + catch (Exception e) { + throw_get("YZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * byte 3D array to get into the imageware + */ + public void getXYZ(int x, int y, int z, byte[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j][k] = (byte) (tmp[offset] & 0xFFFF); + } + } + } + } + catch (Exception e) { + throw_get("XYZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * short 3D array to get into the imageware + */ + public void getXYZ(int x, int y, int z, short[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j][k] = (short) (tmp[offset] & 0xFFFF); + } + } + } + } + catch (Exception e) { + throw_get("XYZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * float 3D array to get into the imageware + */ + public void getXYZ(int x, int y, int z, float[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j][k] = (float) (tmp[offset] & 0xFFFF); + } + } + } + } + catch (Exception e) { + throw_get("XYZ", "No check", buffer, x, y, z); + } + } + + /** + * Get an array from the imageware at the position (x,y,z) in XYZ axis. No + * check are performed if the array is outside of the imageware. + * + * @param x + * X starting position to get the buffer + * @param y + * Y starting position to get the buffer + * @param z + * Z starting position to get the buffer + * @param buffer + * double 3D array to get into the imageware + */ + public void getXYZ(int x, int y, int z, double[][][] buffer) { + try { + int offset = x + y * nx; + int leni = buffer.length; + int lenj = buffer[0].length; + int lenk = buffer[0][0].length; + for (int k = 0; k < lenk; k++, z++) { + short[] tmp = (short[]) data[z]; + for (int j = 0; j < lenj; j++) { + offset = x + (j + y) * nx; + for (int i = 0; i < leni; i++, offset++) { + buffer[i][j][k] = (double) (tmp[offset] & 0xFFFF); + } + } + } + } + catch (Exception e) { + throw_get("XYZ", "No check", buffer, x, y, z); + } + } + + // ------------------------------------------------------------------ + // + // Private Section + // + // ------------------------------------------------------------------ + + /** + * Prepare a complete error message from the errors coming the constructors. + */ + protected void throw_constructor() { + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a short imageware.\n" + + "-------------------------------------------------------\n"); + } + + /** + * Prepare a complete error message from the errors coming the constructors. + */ + protected void throw_constructor(int nx, int ny, int nz) { + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to create a short imageware " + nx + "," + ny + + "," + nz + "].\n" + "-------------------------------------------------------\n"); + } + + /** + * Prepare a complete error message from the errors coming the get routines. + */ + protected void throw_get(String direction, String border, Object buffer, int x, int y, int z) { + int leni = 0; + int lenj = 0; + int lenk = 0; + String type = " unknown type"; + if (buffer instanceof byte[]) { + leni = ((byte[]) buffer).length; + type = " 1D byte"; + } + else if (buffer instanceof short[]) { + leni = ((short[]) buffer).length; + type = " 1D short"; + } + else if (buffer instanceof float[]) { + leni = ((float[]) buffer).length; + type = " 1D float"; + } + else if (buffer instanceof double[]) { + leni = ((double[]) buffer).length; + type = " 1D double"; + } + else if (buffer instanceof byte[][]) { + leni = ((byte[][]) buffer).length; + lenj = ((byte[][]) buffer)[0].length; + type = " 2D byte"; + } + else if (buffer instanceof short[][]) { + leni = ((short[][]) buffer).length; + lenj = ((short[][]) buffer)[0].length; + type = " 2D short"; + } + else if (buffer instanceof float[][]) { + leni = ((float[][]) buffer).length; + lenj = ((float[][]) buffer)[0].length; + type = " 2D float"; + } + else if (buffer instanceof double[][]) { + leni = ((double[][]) buffer).length; + lenj = ((double[][]) buffer)[0].length; + type = " 2D double"; + } + else if (buffer instanceof byte[][][]) { + leni = ((byte[][][]) buffer).length; + lenj = ((byte[][][]) buffer)[0].length; + lenk = ((byte[][][]) buffer)[0][0].length; + type = " 3D byte"; + } + else if (buffer instanceof short[][][]) { + leni = ((short[][][]) buffer).length; + lenj = ((short[][][]) buffer)[0].length; + lenk = ((short[][][]) buffer)[0][0].length; + type = " 3D short"; + } + else if (buffer instanceof float[][][]) { + leni = ((float[][][]) buffer).length; + lenj = ((float[][][]) buffer)[0].length; + lenk = ((float[][][]) buffer)[0][0].length; + type = " 3D float"; + } + else if (buffer instanceof double[][][]) { + leni = ((double[][][]) buffer).length; + lenj = ((double[][][]) buffer)[0].length; + lenk = ((double[][][]) buffer)[0][0].length; + type = " 3D double"; + } + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get a" + type + " buffer [" + + (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "from the short imageware [" + nx + "," + ny + "," + nz + "]\n" + + "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + "-------------------------------------------------------\n"); + } + + /** + * Prepare a complete error message from the errors coming the put routines. + */ + protected void throw_put(String direction, String border, Object buffer, int x, int y, int z) { + int leni = 0; + int lenj = 0; + int lenk = 0; + String type = " unknown type"; + if (buffer instanceof byte[]) { + leni = ((byte[]) buffer).length; + type = " 1D byte"; + } + else if (buffer instanceof short[]) { + leni = ((short[]) buffer).length; + type = " 1D short"; + } + else if (buffer instanceof float[]) { + leni = ((float[]) buffer).length; + type = " 1D float"; + } + else if (buffer instanceof double[]) { + leni = ((double[]) buffer).length; + type = " 1D double"; + } + else if (buffer instanceof byte[][]) { + leni = ((byte[][]) buffer).length; + lenj = ((byte[][]) buffer)[0].length; + type = " 2D byte"; + } + else if (buffer instanceof short[][]) { + leni = ((short[][]) buffer).length; + lenj = ((short[][]) buffer)[0].length; + type = " 2D short"; + } + else if (buffer instanceof float[][]) { + leni = ((float[][]) buffer).length; + lenj = ((float[][]) buffer)[0].length; + type = " 2D float"; + } + else if (buffer instanceof double[][]) { + leni = ((double[][]) buffer).length; + lenj = ((double[][]) buffer)[0].length; + type = " 2D double"; + } + else if (buffer instanceof byte[][][]) { + leni = ((byte[][][]) buffer).length; + lenj = ((byte[][][]) buffer)[0].length; + lenk = ((byte[][][]) buffer)[0][0].length; + type = " 3D byte"; + } + else if (buffer instanceof short[][][]) { + leni = ((short[][][]) buffer).length; + lenj = ((short[][][]) buffer)[0].length; + lenk = ((short[][][]) buffer)[0][0].length; + type = " 3D short"; + } + else if (buffer instanceof float[][][]) { + leni = ((float[][][]) buffer).length; + lenj = ((float[][][]) buffer)[0].length; + lenk = ((float[][][]) buffer)[0][0].length; + type = " 3D float"; + } + else if (buffer instanceof double[][][]) { + leni = ((double[][][]) buffer).length; + lenj = ((double[][][]) buffer)[0].length; + lenk = ((double[][][]) buffer)[0][0].length; + type = " 3D double"; + } + throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to put a" + type + " buffer [" + + (leni == 0 ? "" : ("" + leni)) + (lenj == 0 ? "" : ("," + lenj)) + (lenk == 0 ? "" : ("," + lenk)) + "] \n" + "into the short imageware [" + nx + "," + ny + "," + nz + "]\n" + + "at the position (" + x + "," + y + "," + z + ") in direction " + direction + "\n" + "using " + border + ".\n" + "-------------------------------------------------------\n"); + } + + // ------------------------------------------------------------------ + // + // Get slice fast and direct access Section + // + // ------------------------------------------------------------------ + + /** + * Get a reference of the whole volume data. + * + * @return a reference of the data of this imageware + */ + public Object[] getVolume() { + return data; + } + + /** + * Get a specific slice, fast and direct access, but only for byte + * imageware. + * + * @param z + * number of the requested slice + * @return a reference of the data of one slice of this imageware + */ + public byte[] getSliceByte(int z) { + return null; + } + + /** + * Get a specific slice, fast and direct access, but only for short + * imageware. + * + * @param z + * number of the requested slice + * @return a reference of the data of one slice of this imageware + */ + public short[] getSliceShort(int z) { + return (short[]) data[z]; + } + + /** + * Get a specific slice, fast and direct access, but only for float + * imageware. + * + * @param z + * number of the requested slice + * @return a reference of the data of one slice of this imageware + */ + public float[] getSliceFloat(int z) { + return null; + } + + /** + * Get a specific slice, fast and direct access, but only for double + * imageware. + * + * @param z + * number of the requested slice + * @return a reference of the data of one slice of this imageware + */ + public double[] getSliceDouble(int z) { + return null; + } + + /** + * Allocate a buffer of size [nx,ny,nz]. + */ + private void allocate() { + try { + this.data = new Object[nz]; + this.nxy = nx * ny; + for (int z = 0; z < nz; z++) + this.data[z] = new short[nxy]; + } + catch (Exception e) { + throw_constructor(nx, ny, nz); + } + } + +} // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/ShortPointwise.java b/src/bilib/src/imageware/ShortPointwise.java new file mode 100644 index 0000000..8b6da12 --- /dev/null +++ b/src/bilib/src/imageware/ShortPointwise.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import ij.process.ShortProcessor; import java.awt.Image; import java.util.Random; /** * Class ShortPointwise. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class ShortPointwise extends ShortAccess implements Pointwise { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected ShortPointwise(int nx, int ny, int nz) { super(nx, ny, nz); } protected ShortPointwise(Image image, int mode) { super(image, mode); } protected ShortPointwise(ImageStack stack, int mode) { super(stack, mode); } protected ShortPointwise(ImageStack stack, byte chan) { super(stack, chan); } protected ShortPointwise(byte[] array, int mode) { super(array, mode); } protected ShortPointwise(byte[][] array, int mode) { super(array, mode); } protected ShortPointwise(byte[][][] array, int mode) { super(array, mode); } protected ShortPointwise(short[] array, int mode) { super(array, mode); } protected ShortPointwise(short[][] array, int mode) { super(array, mode); } protected ShortPointwise(short[][][] array, int mode) { super(array, mode); } protected ShortPointwise(float[] array, int mode) { super(array, mode); } protected ShortPointwise(float[][] array, int mode) { super(array, mode); } protected ShortPointwise(float[][][] array, int mode) { super(array, mode); } protected ShortPointwise(double[] array, int mode) { super(array, mode); } protected ShortPointwise(double[][] array, int mode) { super(array, mode); } protected ShortPointwise(double[][][] array, int mode) { super(array, mode); } /** * Fill this imageware with a constant value. * * @param value * the constant value */ public void fillConstant(double value) { short typedValue = (short) value; short[] slice = null; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) slice[k] = typedValue; } } /** * Fill this imageware with ramp. */ public void fillRamp() { int off = 0; short[] slice = null; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) slice[k] = (short) (off + k); off += nxy; } } /** * Generate a gaussian noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void fillGaussianNoise(double amplitude) { Random rnd = new Random(); short[] slice = null; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (short) ((rnd.nextGaussian()) * amplitude); } } } /** * Generate a uniform noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void fillUniformNoise(double amplitude) { Random rnd = new Random(); short[] slice = null; amplitude *= 2.0; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (short) ((rnd.nextDouble() - 0.5) * amplitude); } } } /** * Generate a salt and pepper noise. * * @param amplitudeSalt * amplitude of the salt noise * @param amplitudePepper * amplitude of the pepper noise * @param percentageSalt * percentage of the salt noise * @param percentagePepper * percentage of the pepper noise */ public void fillSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) { Random rnd = new Random(); int index, z; if (percentageSalt > 0) { double nbSalt = nxy * nz / percentageSalt; for (int k = 0; k < nbSalt; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((short[]) data[z])[index] = (short) (rnd.nextDouble() * amplitudeSalt); } } if (percentagePepper > 0) { double nbPepper = nxy * nz / percentagePepper; for (int k = 0; k < nbPepper; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((short[]) data[z])[index] = (short) (-rnd.nextDouble() * amplitudeSalt); } } } /** * Build an ImageStack of ImageJ. */ public ImageStack buildImageStack() { ImageStack imagestack = new ImageStack(nx, ny); for (int z = 0; z < nz; z++) { ShortProcessor ip = new ShortProcessor(nx, ny); short pix[] = (short[]) ip.getPixels(); for (int k = 0; k < nxy; k++) pix[k] = (short) (((short[]) data[z])[k]); imagestack.addSlice("" + z, ip); } return imagestack; } /** * Invert the pixel intensity. */ public void invert() { double max = -Double.MAX_VALUE; short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k] & 0xFFFF) > max) max = slice[k] & 0xFFFF; } } for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (short) (max - ((double) (slice[k] & 0xFFFF))); } } } /** * Negate the pixel intensity. */ public void negate() { short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (short) (-((double) (slice[k] & 0xFFFF))); } } } /** * Clip the pixel intensity into [0..255]. */ public void clip() { clip(0.0, 255.0); } /** * Clip the pixel intensity into [minLevel..maxLevel]. * * @param minLevel * double value given the threshold * @param maxLevel * double value given the threshold */ public void clip(double minLevel, double maxLevel) { short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; short value; short min = (short) minLevel; short max = (short) maxLevel; for (int k = 0; k < nxy; k++) { value = (short) (slice[k] & 0xFFFF); if (value < min) slice[k] = min; if (value > max) slice[k] = max; } } } /** * Rescale the pixel intensity into [0..255]. */ public void rescale() { double maxImage = -Double.MAX_VALUE; double minImage = Double.MAX_VALUE; short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k] & 0xFFFF) > maxImage) maxImage = slice[k] & 0xFFFF; if ((slice[k] & 0xFFFF) < minImage) minImage = slice[k] & 0xFFFF; } } double a; if (minImage - maxImage == 0) { a = 1.0; minImage = 128.0; } else { a = 255.0 / (maxImage - minImage); } for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (short) (a * (((double) (slice[k] & 0xFFFF)) - minImage)); } } } /** * Rescale the pixel intensity into [minLevel..maxLevel]. * * @param minLevel * double value given the threshold * @param maxLevel * double value given the threshold */ public void rescale(double minLevel, double maxLevel) { double maxImage = -Double.MAX_VALUE; double minImage = Double.MAX_VALUE; short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k] & 0xFFFF) > maxImage) maxImage = slice[k] & 0xFFFF; if ((slice[k] & 0xFFFF) < minImage) minImage = slice[k] & 0xFFFF; } } double a; if (minImage - maxImage == 0) { a = 1.0; minImage = (maxLevel - minLevel) / 2.0; } else { a = (maxLevel - minLevel) / (maxImage - minImage); } for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (short) (a * (((double) (slice[k] & 0xFFFF)) - minImage) + minLevel); } } } /** * Rescale the pixel intensity with a linear curve passing through * (maxLevel-minLevel)/2 at the 0 input intensity. * * @param minLevel * double value given the threshold * @param maxLevel * double value given the threshold */ public void rescaleCenter(double minLevel, double maxLevel) { double maxImage = -Double.MAX_VALUE; double minImage = Double.MAX_VALUE; short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { if ((slice[k] & 0xFFFF) > maxImage) maxImage = slice[k] & 0xFFFF; if ((slice[k] & 0xFFFF) < minImage) minImage = slice[k] & 0xFFFF; } } double center = (maxLevel + minLevel) / 2.0; double a; if (minImage - maxImage == 0) { a = 1.0; minImage = (maxLevel - minLevel) / 2.0; } else { if (Math.abs(maxImage) > Math.abs(minImage)) a = (maxLevel - center) / Math.abs(maxImage); else a = (center - minLevel) / Math.abs(minImage); } for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (short) (a * (((double) (slice[k] & 0xFFFF)) - minImage) + center); } } } /** * Compute the absolute value of this imageware. */ public void abs() { } /** * Compute the log of this imageware. */ public void log() { short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (short) Math.log(slice[k]); } } } /** * Compute the exponential of this imageware. */ public void exp() { short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (short) Math.exp(slice[k]); } } } /** * Compute the square root of this imageware. */ public void sqrt() { short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (short) Math.sqrt(slice[k]); } } } /** * Compute the square of this imageware. */ public void sqr() { short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] *= slice[k]; } } } /** * Compute the power of a of this imageware. * * @param a * exponent */ public void pow(double a) { short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = (short) Math.pow(slice[k], a); } } } /** * Add a constant value to this imageware. */ public void add(double constant) { short cst = (short) constant; short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] += cst; } } } /** * Multiply a constant value to this imageware. * * @param constant * the constant value */ public void multiply(double constant) { short cst = (short) constant; short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] *= cst; } } } /** * Subtract a constant value to this imageware. * * @param constant * the constant value */ public void subtract(double constant) { short cst = (short) constant; short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] -= cst; } } } /** * Divide by a constant value to this imageware. * * @param constant * the constant value */ public void divide(double constant) { if (constant == 0.0) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to divide because the constant is 0.\n" + "-------------------------------------------------------\n"); short cst = (short) constant; short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] /= cst; } } } /** * Threshold a imageware in two levels 0 and 255. * * All the pixels values strictly greater than 'thresholdValue' and are set * to 0. The remaining values are set to 255. * * @param thresholdValue * double value given the threshold */ public void threshold(double thresholdValue) { threshold(thresholdValue, 0.0, 255.0); } /** * Threshold a imageware in two levels minLevel and maxLevel. * * All the pixels values strictly greater than 'thresholdValue' and are set * to maxLevel. The remaining values are set to minLevel. * * @param thresholdValue * double value given the threshold * @param minLevel * double value given the minimum level * @param maxLevel * double value given the maximum level */ public void threshold(double thresholdValue, double minLevel, double maxLevel) { short low = (short) (minLevel); short high = (short) (maxLevel); short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] = ((double) (slice[k] & 0xFFFF) > thresholdValue ? high : low); } } } /** * Apply a soft thresholding. * * All the pixels values strictly greater than '-thresholdValue' and stricty * lower than 'thresholdValue' set to 0. The remaining positive values are * reduced by 'thresholdvalue'; the remaining negative values are augmented * by 'thresholdValue'. * * @param thresholdValue * double value given the threshold */ public void thresholdSoft(double thresholdValue) { short zero = (short) (0.0); double pixel; short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { pixel = (double) (slice[k] & 0xFFFF); slice[k] = (pixel <= -thresholdValue ? (short) (pixel + thresholdValue) : (pixel > thresholdValue ? (short) (pixel - thresholdValue) : zero)); } } } /** * Apply a hard thresholding. * * All the pixels values strictly greater than '-thresholdValue' and stricty * lower than 'thresholdValue' are set to 0. The remaining values are * unchanged. * * @param thresholdValue * double value given the threshold */ public void thresholdHard(double thresholdValue) { short zero = (short) (0.0); double pixel; short[] slice; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { pixel = (double) (slice[k] & 0xFFFF); if (pixel > -thresholdValue && pixel < thresholdValue) slice[k] = zero; } } } /** * Add a gaussian noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void addGaussianNoise(double amplitude) { Random rnd = new Random(); short[] slice = null; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] += (short) ((rnd.nextGaussian()) * amplitude); } } } /** * Add a uniform noise with a range [-amplitude..amplitude]. * * @param amplitude * amplitude of the noise */ public void addUniformNoise(double amplitude) { Random rnd = new Random(); short[] slice = null; amplitude *= 2.0; for (int z = 0; z < nz; z++) { slice = (short[]) data[z]; for (int k = 0; k < nxy; k++) { slice[k] += (short) ((rnd.nextDouble() - 0.5) * amplitude); } } } /** * Add a salt and pepper noise. * * @param amplitudeSalt * amplitude of the salt noise * @param amplitudePepper * amplitude of the pepper noise * @param percentageSalt * percentage of the salt noise * @param percentagePepper * percentage of the pepper noise */ public void addSaltPepper(double amplitudeSalt, double amplitudePepper, double percentageSalt, double percentagePepper) { Random rnd = new Random(); int index, z; if (percentageSalt > 0) { double nbSalt = nxy * nz / percentageSalt; for (int k = 0; k < nbSalt; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((short[]) data[z])[index] += (short) (rnd.nextDouble() * amplitudeSalt); } } if (percentagePepper > 0) { double nbPepper = nxy * nz / percentagePepper; for (int k = 0; k < nbPepper; k++) { index = (int) (rnd.nextDouble() * nxy); z = (int) (rnd.nextDouble() * nz); ((short[]) data[z])[index] -= (short) (rnd.nextDouble() * amplitudeSalt); } } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/ShortProcess.java b/src/bilib/src/imageware/ShortProcess.java new file mode 100644 index 0000000..1a627c5 --- /dev/null +++ b/src/bilib/src/imageware/ShortProcess.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import java.awt.Image; /** * Class ShortProcess. * * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class ShortProcess extends ShortPointwise implements Process { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected ShortProcess(int nx, int ny, int nz) { super(nx, ny, nz); } protected ShortProcess(Image image, int mode) { super(image, mode); } protected ShortProcess(ImageStack stack, int mode) { super(stack, mode); } protected ShortProcess(ImageStack stack, byte chan) { super(stack, chan); } protected ShortProcess(byte[] array, int mode) { super(array, mode); } protected ShortProcess(byte[][] array, int mode) { super(array, mode); } protected ShortProcess(byte[][][] array, int mode) { super(array, mode); } protected ShortProcess(short[] array, int mode) { super(array, mode); } protected ShortProcess(short[][] array, int mode) { super(array, mode); } protected ShortProcess(short[][][] array, int mode) { super(array, mode); } protected ShortProcess(float[] array, int mode) { super(array, mode); } protected ShortProcess(float[][] array, int mode) { super(array, mode); } protected ShortProcess(float[][][] array, int mode) { super(array, mode); } protected ShortProcess(double[] array, int mode) { super(array, mode); } protected ShortProcess(double[][] array, int mode) { super(array, mode); } protected ShortProcess(double[][][] array, int mode) { super(array, mode); } /** * Apply a separable gaussian smoothing over the image with the same * strengthness in all directions. To have a smmothing effect the * strengthness should be strictly greater than 0 and the size in the * considered directions should be greater strictly than 1. * * @param sigma * Strengthness of the smoothing */ public void smoothGaussian(double sigma) { smoothGaussian(sigma, sigma, sigma); } /** * Apply a separablegaussian smoothing over the image with an independant * strengthness in the different directions. To have a smmothing effect the * strengthness should be strictly greater than 0 and the size in the * considered directions should be greater strictly than 1. * * @param sigmaX * Strengthness of the smoothing in X axis * @param sigmaY * Strengthness of the smoothing in X axis * @param sigmaZ * Strengthness of the smoothing in X axis */ public void smoothGaussian(double sigmaX, double sigmaY, double sigmaZ) { int n = 3; double N = (double) n; double poles[] = new double[n]; if (nx > 1 && sigmaX > 0.0) { double s2 = sigmaX * sigmaX; double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2); poles[0] = poles[1] = poles[2] = alpha; double line[] = new double[nx]; for (int z = 0; z < nz; z++) { for (int y = 0; y < ny; y++) { getX(0, y, z, line); putX(0, y, z, Convolver.convolveIIR(line, poles)); } } } if (ny > 1 && sigmaY > 0.0) { double s2 = sigmaY * sigmaY; double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2); poles[0] = poles[1] = poles[2] = alpha; double line[] = new double[ny]; for (int x = 0; x < nx; x++) { for (int z = 0; z < nz; z++) { getY(x, 0, z, line); putY(x, 0, z, Convolver.convolveIIR(line, poles)); } } } if (nz > 1 && sigmaZ > 0.0) { double s2 = sigmaZ * sigmaZ; double alpha = 1.0 + (N / s2) - (Math.sqrt(N * N + 2 * N * s2) / s2); poles[0] = poles[1] = poles[2] = alpha; double line[] = new double[nz]; for (int y = 0; y < ny; y++) { for (int x = 0; x < nx; x++) { getZ(x, y, 0, line); putZ(x, y, 0, Convolver.convolveIIR(line, poles)); } } } } /** * Get the maximum of this imageware and a imageware. * * @param imageware * imageware to max */ public void max(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get the maximum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { if (((short[]) data[z])[k] < (short) tmp[k]) ((short[]) data[z])[k] = (short) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { if (((short[]) data[z])[k] < (short) tmp[k]) ((short[]) data[z])[k] = (short) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { if (((short[]) data[z])[k] < (short) tmp[k]) ((short[]) data[z])[k] = (short) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { if (((short[]) data[z])[k] < (short) tmp[k]) ((short[]) data[z])[k] = (short) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Get the minimum of this imageware and a imageware. * * @param imageware * imageware to min */ public void min(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to get the minimum because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { if (((short[]) data[z])[k] > (short) tmp[k]) ((short[]) data[z])[k] = (short) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { if (((short[]) data[z])[k] > (short) tmp[k]) ((short[]) data[z])[k] = (short) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { if (((short[]) data[z])[k] > (short) tmp[k]) ((short[]) data[z])[k] = (short) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { if (((short[]) data[z])[k] > (short) tmp[k]) ((short[]) data[z])[k] = (short) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Add a imageware to the current imageware. * * @param imageware * imageware to add */ public void add(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to add because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] += (short) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] += (short) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] += (short) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] += (short) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Multiply a imageware to the current imageware. * * @param imageware * imageware to multiply */ public void multiply(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to multiply because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] *= (short) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] *= (short) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] *= (short) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] *= (short) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Subtract a imageware to the current imageware. * * @param imageware * imageware to subtract */ public void subtract(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to subtract because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] -= (short) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] -= (short) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] -= (short) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] -= (short) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } /** * Divide a imageware to the current imageware. * * @param imageware * imageware to divide */ public void divide(ImageWare imageware) { if (!isSameSize(imageware)) { throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to divide because the two operands are not the same size.\n" + "[" + nx + "," + ny + "," + "," + nz + "] != " + "[" + imageware.getSizeX() + "," + imageware.getSizeY() + "," + imageware.getSizeZ() + "].\n" + "-------------------------------------------------------\n"); } switch (imageware.getType()) { case ImageWare.BYTE: for (int z = 0; z < nz; z++) { byte[] tmp = ((ByteSet) imageware).getSliceByte(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] /= (short) tmp[k]; } } break; case ImageWare.SHORT: for (int z = 0; z < nz; z++) { short[] tmp = ((ShortSet) imageware).getSliceShort(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] /= (short) tmp[k]; } } break; case ImageWare.FLOAT: for (int z = 0; z < nz; z++) { float[] tmp = ((FloatSet) imageware).getSliceFloat(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] /= (short) tmp[k]; } } break; case ImageWare.DOUBLE: for (int z = 0; z < nz; z++) { double[] tmp = ((DoubleSet) imageware).getSliceDouble(z); for (int k = 0; k < nxy; k++) { ((short[]) data[z])[k] /= (short) tmp[k]; } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + imageware.getType() + "].\n" + "-------------------------------------------------------\n"); } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/imageware/ShortSet.java b/src/bilib/src/imageware/ShortSet.java new file mode 100644 index 0000000..9e60122 --- /dev/null +++ b/src/bilib/src/imageware/ShortSet.java @@ -0,0 +1 @@ +package imageware; import ij.ImageStack; import java.awt.Image; /** * Class ShortSet. * * @author Daniel Sage Biomedical Imaging Group Ecole Polytechnique Federale de * Lausanne, Lausanne, Switzerland */ public class ShortSet extends ShortProcess implements ImageWare { // ------------------------------------------------------------------ // // Constructors section // // ------------------------------------------------------------------ protected ShortSet(int nx, int ny, int nz) { super(nx, ny, nz); } protected ShortSet(Image image, int mode) { super(image, mode); } protected ShortSet(ImageStack stack, int mode) { super(stack, mode); } protected ShortSet(ImageStack stack, byte chan) { super(stack, chan); } protected ShortSet(byte[] array, int mode) { super(array, mode); } protected ShortSet(byte[][] array, int mode) { super(array, mode); } protected ShortSet(byte[][][] array, int mode) { super(array, mode); } protected ShortSet(short[] array, int mode) { super(array, mode); } protected ShortSet(short[][] array, int mode) { super(array, mode); } protected ShortSet(short[][][] array, int mode) { super(array, mode); } protected ShortSet(float[] array, int mode) { super(array, mode); } protected ShortSet(float[][] array, int mode) { super(array, mode); } protected ShortSet(float[][][] array, int mode) { super(array, mode); } protected ShortSet(double[] array, int mode) { super(array, mode); } protected ShortSet(double[][] array, int mode) { super(array, mode); } protected ShortSet(double[][][] array, int mode) { super(array, mode); } /** * Duplicate the imageware. * * Create a new imageware with the same size, same type and same data than * the calling one. * * @return a duplicated version of this imageware */ public ImageWare duplicate() { ImageWare out = new ShortSet(nx, ny, nz); short[] outdata; for (int z = 0; z < nz; z++) { outdata = (short[]) (((ShortSet) out).data[z]); System.arraycopy(data[z], 0, outdata, 0, nxy); } return out; } /** * Replicate the imageware. * * Create a new imageware with the same size, same type than the calling * one. The data are not copied. * * @return a replicated version of this imageware */ public ImageWare replicate() { return new ShortSet(nx, ny, nz); } /** * Replicate the imageware. * * Create a new imageware with the same size and a specified type than the * calling one. The data are not copied. * * @param type * requested type * @return a replicated version of this imageware */ public ImageWare replicate(int type) { switch (type) { case ImageWare.BYTE: return new ByteSet(nx, ny, nz); case ImageWare.SHORT: return new ShortSet(nx, ny, nz); case ImageWare.FLOAT: return new FloatSet(nx, ny, nz); case ImageWare.DOUBLE: return new DoubleSet(nx, ny, nz); default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n" + "-------------------------------------------------------\n"); } } /** * Copy all the data of source in the current imageware. The source should * have the same size and same type than the calling one. * * @param source * a source imageware */ public void copy(ImageWare source) { if (nx != source.getSizeX()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size (" + nx + " != " + source.getSizeX() + ").\n" + "-------------------------------------------------------\n"); if (ny != source.getSizeY()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size (" + ny + " != " + source.getSizeY() + ").\n" + "-------------------------------------------------------\n"); if (nz != source.getSizeZ()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same size (" + nz + " != " + source.getSizeZ() + ").\n" + "-------------------------------------------------------\n"); if (getType() != source.getType()) throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unable to copy because it is not the same type (" + getType() + " != " + source.getType() + ").\n" + "-------------------------------------------------------\n"); short[] src; for (int z = 0; z < nz; z++) { src = (short[]) (((ShortSet) source).data[z]); System.arraycopy(src, 0, data[z], 0, nxy); } } /** * convert the imageware in a specified type. * * Create a new imageware with the same size and converted data than the * calling one. * * @param type * indicates the type of the output * @return a converted version of this imageware */ public ImageWare convert(int type) { if (type == ImageWare.SHORT) return duplicate(); ImageWare out = null; switch (type) { case ImageWare.BYTE: { short[] slice; out = new ByteSet(nx, ny, nz); byte[] outslice; for (int z = 0; z < nz; z++) { slice = ((short[]) data[z]); outslice = ((byte[]) ((ByteSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (byte) (slice[k] & 0xFFFF); } } } break; case ImageWare.SHORT: { short[] slice; out = new ShortSet(nx, ny, nz); short[] outslice; for (int z = 0; z < nz; z++) { slice = ((short[]) data[z]); outslice = ((short[]) ((ShortSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (short) (slice[k] & 0xFFFF); } } } break; case ImageWare.FLOAT: { short[] slice; out = new FloatSet(nx, ny, nz); float[] outslice; for (int z = 0; z < nz; z++) { slice = ((short[]) data[z]); outslice = ((float[]) ((FloatSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (float) (slice[k] & 0xFFFF); } } } break; case ImageWare.DOUBLE: { short[] slice; out = new DoubleSet(nx, ny, nz); double[] outslice; for (int z = 0; z < nz; z++) { slice = ((short[]) data[z]); outslice = ((double[]) ((DoubleSet) out).data[z]); for (int k = 0; k < nxy; k++) { outslice[k] = (double) (slice[k] & 0xFFFF); } } } break; default: throw new ArrayStoreException("\n-------------------------------------------------------\n" + "Error in imageware package\n" + "Unknown type " + type + "].\n" + "-------------------------------------------------------\n"); } return out; } /** * Print information of this ImageWare object. */ public void printInfo() { System.out.println("ImageWare object information"); System.out.println("Dimension: " + getDimension()); System.out.println("Size: [" + nx + ", " + ny + ", " + nz + "]"); System.out.println("TotalSize: " + getTotalSize()); System.out.println("Type: " + getTypeToString()); System.out.println("Maximun: " + getMaximum()); System.out.println("Minimun: " + getMinimum()); System.out.println("Mean: " + getMean()); System.out.println("Norm1: " + getNorm1()); System.out.println("Norm2: " + getNorm2()); System.out.println("Total: " + getTotal()); System.out.println(""); } /** * Show this ImageWare object. */ public void show() { String title = getTypeToString(); switch (getDimension()) { case 1: title += " line"; break; case 2: title += " image"; break; case 3: title += " volume"; break; } Display.show(title, this); // ImagePlus imp = new ImagePlus(title, buildImageStack()); // imp.show(); } /** * Show the data in ImagePlus object with a specify title. * * @param title * a string given the title of the window */ public void show(String title) { Display.show(title, this); // ImagePlus imp = new ImagePlus(title, buildImageStack()); // imp.show(); } /** * Return the minimum value of this imageware. * * @return the min value of this imageware */ public double getMinimum() { double min = Double.MAX_VALUE; short[] slice; for (int z = 0; z < nz; z++) { slice = ((short[]) data[z]); for (int k = 0; k < nxy; k++) if ((slice[k] & 0xFFFF) < min) min = slice[k] & 0xFFFF; } return min; } /** * Return the maximum value of this imageware. * * @return the max value of this imageware */ public double getMaximum() { double max = -Double.MAX_VALUE; short[] slice; for (int z = 0; z < nz; z++) { slice = ((short[]) data[z]); for (int k = 0; k < nxy; k++) if ((slice[k] & 0xFFFF) > max) max = slice[k] & 0xFFFF; } return max; } /** * Return the mean value of this imageware. * * @return the mean value of this imageware */ public double getMean() { return getTotal() / (nz * nxy); } /** * Return the norm value of order 1. * * @return the norm value of this imageware in L1 sense */ public double getNorm1() { double norm = 0.0; double value = 0; short[] slice; for (int z = 0; z < nz; z++) { slice = ((short[]) data[z]); for (int k = 0; k < nxy; k++) { value = (double) (slice[k] & 0xFFFF); norm += (value > 0.0 ? value : -value); } } return norm; } /** * Return the norm value of order 2. * * @return the norm value of this imageware in L2 sense */ public double getNorm2() { double norm = 0.0; short[] slice; for (int z = 0; z < nz; z++) { slice = ((short[]) data[z]); for (int k = 0; k < nxy; k++) norm += (slice[k] & 0xFFFF) * (slice[k] & 0xFFFF); } return norm; } /** * Return the sum of all pixel in this imageware. * * @return the total sum of all pixel in this imageware */ public double getTotal() { double total = 0.0; short[] slice; for (int z = 0; z < nz; z++) { slice = ((short[]) data[z]); for (int k = 0; k < nxy; k++) total += slice[k] & 0xFFFF; } return total; } /** * Return the the minumum [0] and the maximum [1] value of this imageware. * Faster routine than call one getMinimum() and then one getMaximum(). * * @return an array of two values, the min and the max values of the images */ public double[] getMinMax() { double max = -Double.MAX_VALUE; double min = Double.MAX_VALUE; short[] slice; for (int z = 0; z < nz; z++) { slice = ((short[]) data[z]); for (int k = 0; k < nxy; k++) { if ((slice[k] & 0xFFFF) > max) max = slice[k] & 0xFFFF; if ((slice[k] & 0xFFFF) < min) min = slice[k] & 0xFFFF; } } double minmax[] = { min, max }; return minmax; } } // end of class \ No newline at end of file diff --git a/src/bilib/src/jama/CholeskyDecomposition.java b/src/bilib/src/jama/CholeskyDecomposition.java new file mode 100644 index 0000000..a9befbe --- /dev/null +++ b/src/bilib/src/jama/CholeskyDecomposition.java @@ -0,0 +1,195 @@ +package jama; + +/** + * Cholesky Decomposition. + * <P> + * For a symmetric, positive definite matrix A, the Cholesky decomposition is an + * lower triangular matrix L so that A = L*L'. + * <P> + * If the matrix is not symmetric or positive definite, the constructor returns + * a partial decomposition and sets an internal flag that may be queried by the + * isSPD() method. + */ + +public class CholeskyDecomposition implements java.io.Serializable { + + /* + * ------------------------ Class variables ------------------------ + */ + + /** + * Array for internal storage of decomposition. + * + * @serial internal array storage. + */ + private double[][] L; + + /** + * Row and column dimension (square matrix). + * + * @serial matrix dimension. + */ + private int n; + + /** + * Symmetric and positive definite flag. + * + * @serial is symmetric and positive definite flag. + */ + private boolean isspd; + + /* + * ------------------------ Constructor ------------------------ + */ + + /** + * Cholesky algorithm for symmetric and positive definite matrix. + * + * @param Arg + * Square, symmetric matrix. + */ + + public CholeskyDecomposition(Matrix Arg) { + + // Initialize. + double[][] A = Arg.getArray(); + n = Arg.getRowDimension(); + L = new double[n][n]; + isspd = (Arg.getColumnDimension() == n); + // Main loop. + for (int j = 0; j < n; j++) { + double[] Lrowj = L[j]; + double d = 0.0; + for (int k = 0; k < j; k++) { + double[] Lrowk = L[k]; + double s = 0.0; + for (int i = 0; i < k; i++) { + s += Lrowk[i] * Lrowj[i]; + } + Lrowj[k] = s = (A[j][k] - s) / L[k][k]; + d = d + s * s; + isspd = isspd & (A[k][j] == A[j][k]); + } + d = A[j][j] - d; + isspd = isspd & (d > 0.0); + L[j][j] = Math.sqrt(Math.max(d, 0.0)); + for (int k = j + 1; k < n; k++) { + L[j][k] = 0.0; + } + } + } + + /* + * ------------------------ Temporary, experimental code. + * ------------------------ *\ + * + * \** Right Triangular Cholesky Decomposition. <P> For a symmetric, + * positive definite matrix A, the Right Cholesky decomposition is an upper + * triangular matrix R so that A = R'*R. This constructor computes R with + * the Fortran inspired column oriented algorithm used in LINPACK and + * MATLAB. In Java, we suspect a row oriented, lower triangular + * decomposition is faster. We have temporarily included this constructor + * here until timing experiments confirm this suspicion.\ + * + * \** Array for internal storage of right triangular decomposition. **\ + * private transient double[][] R; + * + * \** Cholesky algorithm for symmetric and positive definite matrix. + * + * @param A Square, symmetric matrix. + * + * @param rightflag Actual value ignored. + * + * @return Structure to access R and isspd flag.\ + * + * public CholeskyDecomposition (Matrix Arg, int rightflag) { // Initialize. + * double[][] A = Arg.getArray(); n = Arg.getColumnDimension(); R = new + * double[n][n]; isspd = (Arg.getColumnDimension() == n); // Main loop. for + * (int j = 0; j < n; j++) { double d = 0.0; for (int k = 0; k < j; k++) { + * double s = A[k][j]; for (int i = 0; i < k; i++) { s = s - + * R[i][k]*R[i][j]; } R[k][j] = s = s/R[k][k]; d = d + s*s; isspd = isspd & + * (A[k][j] == A[j][k]); } d = A[j][j] - d; isspd = isspd & (d > 0.0); + * R[j][j] = Math.sqrt(Math.max(d,0.0)); for (int k = j+1; k < n; k++) { + * R[k][j] = 0.0; } } } + * + * \** Return upper triangular factor. + * + * @return R\ + * + * public Matrix getR () { return new Matrix(R,n,n); } + * + * \* ------------------------ End of temporary code. + * ------------------------ + */ + + /* + * ------------------------ Public Methods ------------------------ + */ + + /** + * Is the matrix symmetric and positive definite? + * + * @return true if A is symmetric and positive definite. + */ + + public boolean isSPD() { + return isspd; + } + + /** + * Return triangular factor. + * + * @return L + */ + + public Matrix getL() { + return new Matrix(L, n, n); + } + + /** + * Solve A*X = B + * + * @param B + * A Matrix with as many rows as A and any number of columns. + * @return X so that L*L'*X = B + * @exception IllegalArgumentException + * Matrix row dimensions must agree. + * @exception RuntimeException + * Matrix is not symmetric positive definite. + */ + + public Matrix solve(Matrix B) { + if (B.getRowDimension() != n) { + throw new IllegalArgumentException("Matrix row dimensions must agree."); + } + if (!isspd) { + throw new RuntimeException("Matrix is not symmetric positive definite."); + } + + // Copy right hand side. + double[][] X = B.getArrayCopy(); + int nx = B.getColumnDimension(); + + // Solve L*Y = B; + for (int k = 0; k < n; k++) { + for (int j = 0; j < nx; j++) { + for (int i = 0; i < k; i++) { + X[k][j] -= X[i][j] * L[k][i]; + } + X[k][j] /= L[k][k]; + } + } + + // Solve L'*X = Y; + for (int k = n - 1; k >= 0; k--) { + for (int j = 0; j < nx; j++) { + for (int i = k + 1; i < n; i++) { + X[k][j] -= X[i][j] * L[i][k]; + } + X[k][j] /= L[k][k]; + } + } + + return new Matrix(X, n, nx); + } +} diff --git a/src/bilib/src/jama/EigenvalueDecomposition.java b/src/bilib/src/jama/EigenvalueDecomposition.java new file mode 100644 index 0000000..8f5dff0 --- /dev/null +++ b/src/bilib/src/jama/EigenvalueDecomposition.java @@ -0,0 +1,990 @@ +package jama; + +/** + * Eigenvalues and eigenvectors of a real matrix. + * <P> + * If A is symmetric, then A = V*D*V' where the eigenvalue matrix D is diagonal + * and the eigenvector matrix V is orthogonal. I.e. A = + * V.times(D.times(V.transpose())) and V.times(V.transpose()) equals the + * identity matrix. + * <P> + * If A is not symmetric, then the eigenvalue matrix D is block diagonal with + * the real eigenvalues in 1-by-1 blocks and any complex eigenvalues, lambda + + * i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The columns of V represent + * the eigenvectors in the sense that A*V = V*D, i.e. A.times(V) equals + * V.times(D). The matrix V may be badly conditioned, or even singular, so the + * validity of the equation A = V*D*inverse(V) depends upon V.cond(). + **/ + +public class EigenvalueDecomposition implements java.io.Serializable { + + /* + * ------------------------ Class variables ------------------------ + */ + + /** + * Row and column dimension (square matrix). + * + * @serial matrix dimension. + */ + private int n; + + /** + * Symmetry flag. + * + * @serial internal symmetry flag. + */ + private boolean issymmetric; + + /** + * Arrays for internal storage of eigenvalues. + * + * @serial internal storage of eigenvalues. + */ + private double[] d, e; + + /** + * Array for internal storage of eigenvectors. + * + * @serial internal storage of eigenvectors. + */ + private double[][] V; + + /** + * Array for internal storage of nonsymmetric Hessenberg form. + * + * @serial internal storage of nonsymmetric Hessenberg form. + */ + private double[][] H; + + /** + * Working storage for nonsymmetric algorithm. + * + * @serial working storage for nonsymmetric algorithm. + */ + private double[] ort; + + /* + * ------------------------ Private Methods ------------------------ + */ + + // Symmetric Householder reduction to tridiagonal form. + + private void tred2() { + + // This is derived from the Algol procedures tred2 by + // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for + // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + + for (int j = 0; j < n; j++) { + d[j] = V[n - 1][j]; + } + + // Householder reduction to tridiagonal form. + + for (int i = n - 1; i > 0; i--) { + + // Scale to avoid under/overflow. + + double scale = 0.0; + double h = 0.0; + for (int k = 0; k < i; k++) { + scale = scale + Math.abs(d[k]); + } + if (scale == 0.0) { + e[i] = d[i - 1]; + for (int j = 0; j < i; j++) { + d[j] = V[i - 1][j]; + V[i][j] = 0.0; + V[j][i] = 0.0; + } + } + else { + + // Generate Householder vector. + + for (int k = 0; k < i; k++) { + d[k] /= scale; + h += d[k] * d[k]; + } + double f = d[i - 1]; + double g = Math.sqrt(h); + if (f > 0) { + g = -g; + } + e[i] = scale * g; + h = h - f * g; + d[i - 1] = f - g; + for (int j = 0; j < i; j++) { + e[j] = 0.0; + } + + // Apply similarity transformation to remaining columns. + + for (int j = 0; j < i; j++) { + f = d[j]; + V[j][i] = f; + g = e[j] + V[j][j] * f; + for (int k = j + 1; k <= i - 1; k++) { + g += V[k][j] * d[k]; + e[k] += V[k][j] * f; + } + e[j] = g; + } + f = 0.0; + for (int j = 0; j < i; j++) { + e[j] /= h; + f += e[j] * d[j]; + } + double hh = f / (h + h); + for (int j = 0; j < i; j++) { + e[j] -= hh * d[j]; + } + for (int j = 0; j < i; j++) { + f = d[j]; + g = e[j]; + for (int k = j; k <= i - 1; k++) { + V[k][j] -= (f * e[k] + g * d[k]); + } + d[j] = V[i - 1][j]; + V[i][j] = 0.0; + } + } + d[i] = h; + } + + // Accumulate transformations. + + for (int i = 0; i < n - 1; i++) { + V[n - 1][i] = V[i][i]; + V[i][i] = 1.0; + double h = d[i + 1]; + if (h != 0.0) { + for (int k = 0; k <= i; k++) { + d[k] = V[k][i + 1] / h; + } + for (int j = 0; j <= i; j++) { + double g = 0.0; + for (int k = 0; k <= i; k++) { + g += V[k][i + 1] * V[k][j]; + } + for (int k = 0; k <= i; k++) { + V[k][j] -= g * d[k]; + } + } + } + for (int k = 0; k <= i; k++) { + V[k][i + 1] = 0.0; + } + } + for (int j = 0; j < n; j++) { + d[j] = V[n - 1][j]; + V[n - 1][j] = 0.0; + } + V[n - 1][n - 1] = 1.0; + e[0] = 0.0; + } + + // Symmetric tridiagonal QL algorithm. + + private void tql2() { + + // This is derived from the Algol procedures tql2, by + // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for + // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + + for (int i = 1; i < n; i++) { + e[i - 1] = e[i]; + } + e[n - 1] = 0.0; + + double f = 0.0; + double tst1 = 0.0; + double eps = Math.pow(2.0, -52.0); + for (int l = 0; l < n; l++) { + + // Find small subdiagonal element + + tst1 = Math.max(tst1, Math.abs(d[l]) + Math.abs(e[l])); + int m = l; + while (m < n) { + if (Math.abs(e[m]) <= eps * tst1) { + break; + } + m++; + } + + // If m == l, d[l] is an eigenvalue, + // otherwise, iterate. + + if (m > l) { + int iter = 0; + do { + iter = iter + 1; // (Could check iteration count here.) + + // Compute implicit shift + + double g = d[l]; + double p = (d[l + 1] - g) / (2.0 * e[l]); + double r = Maths.hypot(p, 1.0); + if (p < 0) { + r = -r; + } + d[l] = e[l] / (p + r); + d[l + 1] = e[l] * (p + r); + double dl1 = d[l + 1]; + double h = g - d[l]; + for (int i = l + 2; i < n; i++) { + d[i] -= h; + } + f = f + h; + + // Implicit QL transformation. + + p = d[m]; + double c = 1.0; + double c2 = c; + double c3 = c; + double el1 = e[l + 1]; + double s = 0.0; + double s2 = 0.0; + for (int i = m - 1; i >= l; i--) { + c3 = c2; + c2 = c; + s2 = s; + g = c * e[i]; + h = c * p; + r = Maths.hypot(p, e[i]); + e[i + 1] = s * r; + s = e[i] / r; + c = p / r; + p = c * d[i] - s * g; + d[i + 1] = h + s * (c * g + s * d[i]); + + // Accumulate transformation. + + for (int k = 0; k < n; k++) { + h = V[k][i + 1]; + V[k][i + 1] = s * V[k][i] + c * h; + V[k][i] = c * V[k][i] - s * h; + } + } + p = -s * s2 * c3 * el1 * e[l] / dl1; + e[l] = s * p; + d[l] = c * p; + + // Check for convergence. + + } + while (Math.abs(e[l]) > eps * tst1); + } + d[l] = d[l] + f; + e[l] = 0.0; + } + + // Sort eigenvalues and corresponding vectors. + + for (int i = 0; i < n - 1; i++) { + int k = i; + double p = d[i]; + for (int j = i + 1; j < n; j++) { + if (d[j] < p) { + k = j; + p = d[j]; + } + } + if (k != i) { + d[k] = d[i]; + d[i] = p; + for (int j = 0; j < n; j++) { + p = V[j][i]; + V[j][i] = V[j][k]; + V[j][k] = p; + } + } + } + } + + // Nonsymmetric reduction to Hessenberg form. + + private void orthes() { + + // This is derived from the Algol procedures orthes and ortran, + // by Martin and Wilkinson, Handbook for Auto. Comp., + // Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutines in EISPACK. + + int low = 0; + int high = n - 1; + + for (int m = low + 1; m <= high - 1; m++) { + + // Scale column. + + double scale = 0.0; + for (int i = m; i <= high; i++) { + scale = scale + Math.abs(H[i][m - 1]); + } + if (scale != 0.0) { + + // Compute Householder transformation. + + double h = 0.0; + for (int i = high; i >= m; i--) { + ort[i] = H[i][m - 1] / scale; + h += ort[i] * ort[i]; + } + double g = Math.sqrt(h); + if (ort[m] > 0) { + g = -g; + } + h = h - ort[m] * g; + ort[m] = ort[m] - g; + + // Apply Householder similarity transformation + // H = (I-u*u'/h)*H*(I-u*u')/h) + + for (int j = m; j < n; j++) { + double f = 0.0; + for (int i = high; i >= m; i--) { + f += ort[i] * H[i][j]; + } + f = f / h; + for (int i = m; i <= high; i++) { + H[i][j] -= f * ort[i]; + } + } + + for (int i = 0; i <= high; i++) { + double f = 0.0; + for (int j = high; j >= m; j--) { + f += ort[j] * H[i][j]; + } + f = f / h; + for (int j = m; j <= high; j++) { + H[i][j] -= f * ort[j]; + } + } + ort[m] = scale * ort[m]; + H[m][m - 1] = scale * g; + } + } + + // Accumulate transformations (Algol's ortran). + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + V[i][j] = (i == j ? 1.0 : 0.0); + } + } + + for (int m = high - 1; m >= low + 1; m--) { + if (H[m][m - 1] != 0.0) { + for (int i = m + 1; i <= high; i++) { + ort[i] = H[i][m - 1]; + } + for (int j = m; j <= high; j++) { + double g = 0.0; + for (int i = m; i <= high; i++) { + g += ort[i] * V[i][j]; + } + // Double division avoids possible underflow + g = (g / ort[m]) / H[m][m - 1]; + for (int i = m; i <= high; i++) { + V[i][j] += g * ort[i]; + } + } + } + } + } + + // Complex scalar division. + + private transient double cdivr, cdivi; + + private void cdiv(double xr, double xi, double yr, double yi) { + double r, d; + if (Math.abs(yr) > Math.abs(yi)) { + r = yi / yr; + d = yr + r * yi; + cdivr = (xr + r * xi) / d; + cdivi = (xi - r * xr) / d; + } + else { + r = yr / yi; + d = yi + r * yr; + cdivr = (r * xr + xi) / d; + cdivi = (r * xi - xr) / d; + } + } + + // Nonsymmetric reduction from Hessenberg to real Schur form. + + private void hqr2() { + + // This is derived from the Algol procedure hqr2, + // by Martin and Wilkinson, Handbook for Auto. Comp., + // Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + + // Initialize + + int nn = this.n; + int n = nn - 1; + int low = 0; + int high = nn - 1; + double eps = Math.pow(2.0, -52.0); + double exshift = 0.0; + double p = 0, q = 0, r = 0, s = 0, z = 0, t, w, x, y; + + // Store roots isolated by balanc and compute matrix norm + + double norm = 0.0; + for (int i = 0; i < nn; i++) { + if (i < low | i > high) { + d[i] = H[i][i]; + e[i] = 0.0; + } + for (int j = Math.max(i - 1, 0); j < nn; j++) { + norm = norm + Math.abs(H[i][j]); + } + } + + // Outer loop over eigenvalue index + + int iter = 0; + while (n >= low) { + + // Look for single small sub-diagonal element + + int l = n; + while (l > low) { + s = Math.abs(H[l - 1][l - 1]) + Math.abs(H[l][l]); + if (s == 0.0) { + s = norm; + } + if (Math.abs(H[l][l - 1]) < eps * s) { + break; + } + l--; + } + + // Check for convergence + // One root found + + if (l == n) { + H[n][n] = H[n][n] + exshift; + d[n] = H[n][n]; + e[n] = 0.0; + n--; + iter = 0; + + // Two roots found + + } + else if (l == n - 1) { + w = H[n][n - 1] * H[n - 1][n]; + p = (H[n - 1][n - 1] - H[n][n]) / 2.0; + q = p * p + w; + z = Math.sqrt(Math.abs(q)); + H[n][n] = H[n][n] + exshift; + H[n - 1][n - 1] = H[n - 1][n - 1] + exshift; + x = H[n][n]; + + // Real pair + + if (q >= 0) { + if (p >= 0) { + z = p + z; + } + else { + z = p - z; + } + d[n - 1] = x + z; + d[n] = d[n - 1]; + if (z != 0.0) { + d[n] = x - w / z; + } + e[n - 1] = 0.0; + e[n] = 0.0; + x = H[n][n - 1]; + s = Math.abs(x) + Math.abs(z); + p = x / s; + q = z / s; + r = Math.sqrt(p * p + q * q); + p = p / r; + q = q / r; + + // Row modification + + for (int j = n - 1; j < nn; j++) { + z = H[n - 1][j]; + H[n - 1][j] = q * z + p * H[n][j]; + H[n][j] = q * H[n][j] - p * z; + } + + // Column modification + + for (int i = 0; i <= n; i++) { + z = H[i][n - 1]; + H[i][n - 1] = q * z + p * H[i][n]; + H[i][n] = q * H[i][n] - p * z; + } + + // Accumulate transformations + + for (int i = low; i <= high; i++) { + z = V[i][n - 1]; + V[i][n - 1] = q * z + p * V[i][n]; + V[i][n] = q * V[i][n] - p * z; + } + + // Complex pair + + } + else { + d[n - 1] = x + p; + d[n] = x + p; + e[n - 1] = z; + e[n] = -z; + } + n = n - 2; + iter = 0; + + // No convergence yet + + } + else { + + // Form shift + + x = H[n][n]; + y = 0.0; + w = 0.0; + if (l < n) { + y = H[n - 1][n - 1]; + w = H[n][n - 1] * H[n - 1][n]; + } + + // Wilkinson's original ad hoc shift + + if (iter == 10) { + exshift += x; + for (int i = low; i <= n; i++) { + H[i][i] -= x; + } + s = Math.abs(H[n][n - 1]) + Math.abs(H[n - 1][n - 2]); + x = y = 0.75 * s; + w = -0.4375 * s * s; + } + + // MATLAB's new ad hoc shift + + if (iter == 30) { + s = (y - x) / 2.0; + s = s * s + w; + if (s > 0) { + s = Math.sqrt(s); + if (y < x) { + s = -s; + } + s = x - w / ((y - x) / 2.0 + s); + for (int i = low; i <= n; i++) { + H[i][i] -= s; + } + exshift += s; + x = y = w = 0.964; + } + } + + iter = iter + 1; // (Could check iteration count here.) + + // Look for two consecutive small sub-diagonal elements + + int m = n - 2; + while (m >= l) { + z = H[m][m]; + r = x - z; + s = y - z; + p = (r * s - w) / H[m + 1][m] + H[m][m + 1]; + q = H[m + 1][m + 1] - z - r - s; + r = H[m + 2][m + 1]; + s = Math.abs(p) + Math.abs(q) + Math.abs(r); + p = p / s; + q = q / s; + r = r / s; + if (m == l) { + break; + } + if (Math.abs(H[m][m - 1]) * (Math.abs(q) + Math.abs(r)) < eps * (Math.abs(p) * (Math.abs(H[m - 1][m - 1]) + Math.abs(z) + Math.abs(H[m + 1][m + 1])))) { + break; + } + m--; + } + + for (int i = m + 2; i <= n; i++) { + H[i][i - 2] = 0.0; + if (i > m + 2) { + H[i][i - 3] = 0.0; + } + } + + // Double QR step involving rows l:n and columns m:n + + for (int k = m; k <= n - 1; k++) { + boolean notlast = (k != n - 1); + if (k != m) { + p = H[k][k - 1]; + q = H[k + 1][k - 1]; + r = (notlast ? H[k + 2][k - 1] : 0.0); + x = Math.abs(p) + Math.abs(q) + Math.abs(r); + if (x != 0.0) { + p = p / x; + q = q / x; + r = r / x; + } + } + if (x == 0.0) { + break; + } + s = Math.sqrt(p * p + q * q + r * r); + if (p < 0) { + s = -s; + } + if (s != 0) { + if (k != m) { + H[k][k - 1] = -s * x; + } + else if (l != m) { + H[k][k - 1] = -H[k][k - 1]; + } + p = p + s; + x = p / s; + y = q / s; + z = r / s; + q = q / p; + r = r / p; + + // Row modification + + for (int j = k; j < nn; j++) { + p = H[k][j] + q * H[k + 1][j]; + if (notlast) { + p = p + r * H[k + 2][j]; + H[k + 2][j] = H[k + 2][j] - p * z; + } + H[k][j] = H[k][j] - p * x; + H[k + 1][j] = H[k + 1][j] - p * y; + } + + // Column modification + + for (int i = 0; i <= Math.min(n, k + 3); i++) { + p = x * H[i][k] + y * H[i][k + 1]; + if (notlast) { + p = p + z * H[i][k + 2]; + H[i][k + 2] = H[i][k + 2] - p * r; + } + H[i][k] = H[i][k] - p; + H[i][k + 1] = H[i][k + 1] - p * q; + } + + // Accumulate transformations + + for (int i = low; i <= high; i++) { + p = x * V[i][k] + y * V[i][k + 1]; + if (notlast) { + p = p + z * V[i][k + 2]; + V[i][k + 2] = V[i][k + 2] - p * r; + } + V[i][k] = V[i][k] - p; + V[i][k + 1] = V[i][k + 1] - p * q; + } + } // (s != 0) + } // k loop + } // check convergence + } // while (n >= low) + + // Backsubstitute to find vectors of upper triangular form + + if (norm == 0.0) { + return; + } + + for (n = nn - 1; n >= 0; n--) { + p = d[n]; + q = e[n]; + + // Real vector + + if (q == 0) { + int l = n; + H[n][n] = 1.0; + for (int i = n - 1; i >= 0; i--) { + w = H[i][i] - p; + r = 0.0; + for (int j = l; j <= n; j++) { + r = r + H[i][j] * H[j][n]; + } + if (e[i] < 0.0) { + z = w; + s = r; + } + else { + l = i; + if (e[i] == 0.0) { + if (w != 0.0) { + H[i][n] = -r / w; + } + else { + H[i][n] = -r / (eps * norm); + } + + // Solve real equations + + } + else { + x = H[i][i + 1]; + y = H[i + 1][i]; + q = (d[i] - p) * (d[i] - p) + e[i] * e[i]; + t = (x * s - z * r) / q; + H[i][n] = t; + if (Math.abs(x) > Math.abs(z)) { + H[i + 1][n] = (-r - w * t) / x; + } + else { + H[i + 1][n] = (-s - y * t) / z; + } + } + + // Overflow control + + t = Math.abs(H[i][n]); + if ((eps * t) * t > 1) { + for (int j = i; j <= n; j++) { + H[j][n] = H[j][n] / t; + } + } + } + } + + // Complex vector + + } + else if (q < 0) { + int l = n - 1; + + // Last vector component imaginary so matrix is triangular + + if (Math.abs(H[n][n - 1]) > Math.abs(H[n - 1][n])) { + H[n - 1][n - 1] = q / H[n][n - 1]; + H[n - 1][n] = -(H[n][n] - p) / H[n][n - 1]; + } + else { + cdiv(0.0, -H[n - 1][n], H[n - 1][n - 1] - p, q); + H[n - 1][n - 1] = cdivr; + H[n - 1][n] = cdivi; + } + H[n][n - 1] = 0.0; + H[n][n] = 1.0; + for (int i = n - 2; i >= 0; i--) { + double ra, sa, vr, vi; + ra = 0.0; + sa = 0.0; + for (int j = l; j <= n; j++) { + ra = ra + H[i][j] * H[j][n - 1]; + sa = sa + H[i][j] * H[j][n]; + } + w = H[i][i] - p; + + if (e[i] < 0.0) { + z = w; + r = ra; + s = sa; + } + else { + l = i; + if (e[i] == 0) { + cdiv(-ra, -sa, w, q); + H[i][n - 1] = cdivr; + H[i][n] = cdivi; + } + else { + + // Solve complex equations + + x = H[i][i + 1]; + y = H[i + 1][i]; + vr = (d[i] - p) * (d[i] - p) + e[i] * e[i] - q * q; + vi = (d[i] - p) * 2.0 * q; + if (vr == 0.0 & vi == 0.0) { + vr = eps * norm * (Math.abs(w) + Math.abs(q) + Math.abs(x) + Math.abs(y) + Math.abs(z)); + } + cdiv(x * r - z * ra + q * sa, x * s - z * sa - q * ra, vr, vi); + H[i][n - 1] = cdivr; + H[i][n] = cdivi; + if (Math.abs(x) > (Math.abs(z) + Math.abs(q))) { + H[i + 1][n - 1] = (-ra - w * H[i][n - 1] + q * H[i][n]) / x; + H[i + 1][n] = (-sa - w * H[i][n] - q * H[i][n - 1]) / x; + } + else { + cdiv(-r - y * H[i][n - 1], -s - y * H[i][n], z, q); + H[i + 1][n - 1] = cdivr; + H[i + 1][n] = cdivi; + } + } + + // Overflow control + + t = Math.max(Math.abs(H[i][n - 1]), Math.abs(H[i][n])); + if ((eps * t) * t > 1) { + for (int j = i; j <= n; j++) { + H[j][n - 1] = H[j][n - 1] / t; + H[j][n] = H[j][n] / t; + } + } + } + } + } + } + + // Vectors of isolated roots + + for (int i = 0; i < nn; i++) { + if (i < low | i > high) { + for (int j = i; j < nn; j++) { + V[i][j] = H[i][j]; + } + } + } + + // Back transformation to get eigenvectors of original matrix + + for (int j = nn - 1; j >= low; j--) { + for (int i = low; i <= high; i++) { + z = 0.0; + for (int k = low; k <= Math.min(j, high); k++) { + z = z + V[i][k] * H[k][j]; + } + V[i][j] = z; + } + } + } + + /* + * ------------------------ Constructor ------------------------ + */ + + /** + * Check for symmetry, then construct the eigenvalue decomposition + * + * @param Arg + * Square matrix + */ + + public EigenvalueDecomposition(Matrix Arg) { + double[][] A = Arg.getArray(); + n = Arg.getColumnDimension(); + V = new double[n][n]; + d = new double[n]; + e = new double[n]; + + issymmetric = true; + for (int j = 0; (j < n) & issymmetric; j++) { + for (int i = 0; (i < n) & issymmetric; i++) { + issymmetric = (A[i][j] == A[j][i]); + } + } + + if (issymmetric) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + V[i][j] = A[i][j]; + } + } + + // Tridiagonalize. + tred2(); + + // Diagonalize. + tql2(); + + } + else { + H = new double[n][n]; + ort = new double[n]; + + for (int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + H[i][j] = A[i][j]; + } + } + + // Reduce to Hessenberg form. + orthes(); + + // Reduce Hessenberg to real Schur form. + hqr2(); + } + } + + /* + * ------------------------ Public Methods ------------------------ + */ + + /** + * Return the eigenvector matrix + * + * @return V + */ + + public Matrix getV() { + return new Matrix(V, n, n); + } + + /** + * Return the real parts of the eigenvalues + * + * @return real(diag(D)) + */ + + public double[] getRealEigenvalues() { + return d; + } + + /** + * Return the imaginary parts of the eigenvalues + * + * @return imag(diag(D)) + */ + + public double[] getImagEigenvalues() { + return e; + } + + /** + * Return the block diagonal eigenvalue matrix + * + * @return D + */ + + public Matrix getD() { + Matrix X = new Matrix(n, n); + double[][] D = X.getArray(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + D[i][j] = 0.0; + } + D[i][i] = d[i]; + if (e[i] > 0) { + D[i][i + 1] = e[i]; + } + else if (e[i] < 0) { + D[i][i - 1] = e[i]; + } + } + return X; + } +} diff --git a/src/bilib/src/jama/LUDecomposition.java b/src/bilib/src/jama/LUDecomposition.java new file mode 100644 index 0000000..cc18e7b --- /dev/null +++ b/src/bilib/src/jama/LUDecomposition.java @@ -0,0 +1,318 @@ +package jama; + +/** + * LU Decomposition. + * <P> + * For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n unit + * lower triangular matrix L, an n-by-n upper triangular matrix U, and a + * permutation vector piv of length m so that A(piv,:) = L*U. If m < n, then L + * is m-by-m and U is m-by-n. + * <P> + * The LU decompostion with pivoting always exists, even if the matrix is + * singular, so the constructor will never fail. The primary use of the LU + * decomposition is in the solution of square systems of simultaneous linear + * equations. This will fail if isNonsingular() returns false. + */ + +public class LUDecomposition implements java.io.Serializable { + + /* + * ------------------------ Class variables ------------------------ + */ + + /** + * Array for internal storage of decomposition. + * + * @serial internal array storage. + */ + private double[][] LU; + + /** + * Row and column dimensions, and pivot sign. + * + * @serial column dimension. + * @serial row dimension. + * @serial pivot sign. + */ + private int m, n, pivsign; + + /** + * Internal storage of pivot vector. + * + * @serial pivot vector. + */ + private int[] piv; + + /* + * ------------------------ Constructor ------------------------ + */ + + /** + * LU Decomposition + * + * @param A + * Rectangular matrix + */ + + public LUDecomposition(Matrix A) { + + // Use a "left-looking", dot-product, Crout/Doolittle algorithm. + + LU = A.getArrayCopy(); + m = A.getRowDimension(); + n = A.getColumnDimension(); + piv = new int[m]; + for (int i = 0; i < m; i++) { + piv[i] = i; + } + pivsign = 1; + double[] LUrowi; + double[] LUcolj = new double[m]; + + // Outer loop. + + for (int j = 0; j < n; j++) { + + // Make a copy of the j-th column to localize references. + + for (int i = 0; i < m; i++) { + LUcolj[i] = LU[i][j]; + } + + // Apply previous transformations. + + for (int i = 0; i < m; i++) { + LUrowi = LU[i]; + + // Most of the time is spent in the following dot product. + + int kmax = Math.min(i, j); + double s = 0.0; + for (int k = 0; k < kmax; k++) { + s += LUrowi[k] * LUcolj[k]; + } + + LUrowi[j] = LUcolj[i] -= s; + } + + // Find pivot and exchange if necessary. + + int p = j; + for (int i = j + 1; i < m; i++) { + if (Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])) { + p = i; + } + } + if (p != j) { + for (int k = 0; k < n; k++) { + double t = LU[p][k]; + LU[p][k] = LU[j][k]; + LU[j][k] = t; + } + int k = piv[p]; + piv[p] = piv[j]; + piv[j] = k; + pivsign = -pivsign; + } + + // Compute multipliers. + + if (j < m & LU[j][j] != 0.0) { + for (int i = j + 1; i < m; i++) { + LU[i][j] /= LU[j][j]; + } + } + } + } + + /* + * ------------------------ Temporary, experimental code. + * ------------------------ *\ + * + * \** LU Decomposition, computed by Gaussian elimination. <P> This + * constructor computes L and U with the "daxpy"-based elimination algorithm + * used in LINPACK and MATLAB. In Java, we suspect the dot-product, Crout + * algorithm will be faster. We have temporarily included this constructor + * until timing experiments confirm this suspicion. <P> + * + * @param A Rectangular matrix + * + * @param linpackflag Use Gaussian elimination. Actual value ignored. + * + * @return Structure to access L, U and piv.\ + * + * public LUDecomposition (Matrix A, int linpackflag) { // Initialize. LU = + * A.getArrayCopy(); m = A.getRowDimension(); n = A.getColumnDimension(); + * piv = new int[m]; for (int i = 0; i < m; i++) { piv[i] = i; } pivsign = + * 1; // Main loop. for (int k = 0; k < n; k++) { // Find pivot. int p = k; + * for (int i = k+1; i < m; i++) { if (Math.abs(LU[i][k]) > + * Math.abs(LU[p][k])) { p = i; } } // Exchange if necessary. if (p != k) { + * for (int j = 0; j < n; j++) { double t = LU[p][j]; LU[p][j] = LU[k][j]; + * LU[k][j] = t; } int t = piv[p]; piv[p] = piv[k]; piv[k] = t; pivsign = + * -pivsign; } // Compute multipliers and eliminate k-th column. if + * (LU[k][k] != 0.0) { for (int i = k+1; i < m; i++) { LU[i][k] /= LU[k][k]; + * for (int j = k+1; j < n; j++) { LU[i][j] -= LU[i][k]*LU[k][j]; } } } } } + * + * \* ------------------------ End of temporary code. + * ------------------------ + */ + + /* + * ------------------------ Public Methods ------------------------ + */ + + /** + * Is the matrix nonsingular? + * + * @return true if U, and hence A, is nonsingular. + */ + + public boolean isNonsingular() { + for (int j = 0; j < n; j++) { + if (LU[j][j] == 0) + return false; + } + return true; + } + + /** + * Return lower triangular factor + * + * @return L + */ + + public Matrix getL() { + Matrix X = new Matrix(m, n); + double[][] L = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (i > j) { + L[i][j] = LU[i][j]; + } + else if (i == j) { + L[i][j] = 1.0; + } + else { + L[i][j] = 0.0; + } + } + } + return X; + } + + /** + * Return upper triangular factor + * + * @return U + */ + + public Matrix getU() { + Matrix X = new Matrix(n, n); + double[][] U = X.getArray(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i <= j) { + U[i][j] = LU[i][j]; + } + else { + U[i][j] = 0.0; + } + } + } + return X; + } + + /** + * Return pivot permutation vector + * + * @return piv + */ + + public int[] getPivot() { + int[] p = new int[m]; + for (int i = 0; i < m; i++) { + p[i] = piv[i]; + } + return p; + } + + /** + * Return pivot permutation vector as a one-dimensional double array + * + * @return (double) piv + */ + + public double[] getDoublePivot() { + double[] vals = new double[m]; + for (int i = 0; i < m; i++) { + vals[i] = (double) piv[i]; + } + return vals; + } + + /** + * Determinant + * + * @return det(A) + * @exception IllegalArgumentException + * Matrix must be square + */ + + public double det() { + if (m != n) { + throw new IllegalArgumentException("Matrix must be square."); + } + double d = (double) pivsign; + for (int j = 0; j < n; j++) { + d *= LU[j][j]; + } + return d; + } + + /** + * Solve A*X = B + * + * @param B + * A Matrix with as many rows as A and any number of columns. + * @return X so that L*U*X = B(piv,:) + * @exception IllegalArgumentException + * Matrix row dimensions must agree. + * @exception RuntimeException + * Matrix is singular. + */ + + public Matrix solve(Matrix B) { + if (B.getRowDimension() != m) { + throw new IllegalArgumentException("Matrix row dimensions must agree."); + } + if (!this.isNonsingular()) { + throw new RuntimeException("Matrix is singular."); + } + + // Copy right hand side with pivoting + int nx = B.getColumnDimension(); + Matrix Xmat = B.getMatrix(piv, 0, nx - 1); + double[][] X = Xmat.getArray(); + + // Solve L*Y = B(piv,:) + for (int k = 0; k < n; k++) { + for (int i = k + 1; i < n; i++) { + for (int j = 0; j < nx; j++) { + X[i][j] -= X[k][j] * LU[i][k]; + } + } + } + // Solve U*X = Y; + for (int k = n - 1; k >= 0; k--) { + for (int j = 0; j < nx; j++) { + X[k][j] /= LU[k][k]; + } + for (int i = 0; i < k; i++) { + for (int j = 0; j < nx; j++) { + X[i][j] -= X[k][j] * LU[i][k]; + } + } + } + return Xmat; + } +} diff --git a/src/bilib/src/jama/Maths.java b/src/bilib/src/jama/Maths.java new file mode 100644 index 0000000..f58071d --- /dev/null +++ b/src/bilib/src/jama/Maths.java @@ -0,0 +1 @@ +package jama; public class Maths { /** sqrt(a^2 + b^2) without under/overflow. **/ public static double hypot(double a, double b) { double r; if (Math.abs(a) > Math.abs(b)) { r = b / a; r = Math.abs(a) * Math.sqrt(1 + r * r); } else if (b != 0) { r = a / b; r = Math.abs(b) * Math.sqrt(1 + r * r); } else { r = 0.0; } return r; } } \ No newline at end of file diff --git a/src/bilib/src/jama/Matrix.java b/src/bilib/src/jama/Matrix.java new file mode 100644 index 0000000..c42f4ff --- /dev/null +++ b/src/bilib/src/jama/Matrix.java @@ -0,0 +1,1276 @@ +package jama; + +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.Locale; + +/** + * Jama = Java Matrix class. + * <P> + * The Java Matrix Class provides the fundamental operations of numerical linear + * algebra. Various constructors create Matrices from two dimensional arrays of + * double precision floating point numbers. Various "gets" and "sets" provide + * access to submatrices and matrix elements. Several methods implement basic + * matrix arithmetic, including matrix addition and multiplication, matrix + * norms, and element-by-element array operations. Methods for reading and + * printing matrices are also included. All the operations in this version of + * the Matrix Class involve real matrices. Complex matrices may be handled in a + * future version. + * <P> + * Five fundamental matrix decompositions, which consist of pairs or triples of + * matrices, permutation vectors, and the like, produce results in five + * decomposition classes. These decompositions are accessed by the Matrix class + * to compute solutions of simultaneous linear equations, determinants, inverses + * and other matrix functions. The five decompositions are: + * <P> + * <UL> + * <LI>Cholesky Decomposition of symmetric, positive definite matrices. + * <LI>LU Decomposition of rectangular matrices. + * <LI>QR Decomposition of rectangular matrices. + * <LI>Singular Value Decomposition of rectangular matrices. + * <LI>Eigenvalue Decomposition of both symmetric and nonsymmetric square + * matrices. + * </UL> + * <DL> + * <DT><B>Example of use:</B></DT> + * <P> + * <DD>Solve a linear system A x = b and compute the residual norm, ||b - A x||. + * <P> + * + * <PRE> + * double[][] vals = { { 1., 2., 3 }, { 4., 5., 6. }, { 7., 8., 10. } }; + * Matrix A = new Matrix(vals); + * Matrix b = Matrix.random(3, 1); + * Matrix x = A.solve(b); + * Matrix r = A.times( + * x) + * .minus(b); + * double rnorm = r.normInf(); + * </PRE> + * + * </DD> + * </DL> + * + * @author The MathWorks, Inc. and the National Institute of Standards and + * Technology. + * @version 5 August 1998 + */ + +public class Matrix implements Cloneable, java.io.Serializable { + + /* + * ------------------------ Class variables ------------------------ + */ + + /** + * Array for internal storage of elements. + * + * @serial internal array storage. + */ + private double[][] A; + + /** + * Row and column dimensions. + * + * @serial row dimension. + * @serial column dimension. + */ + private int m, n; + + /* + * ------------------------ Constructors ------------------------ + */ + + /** + * Construct an m-by-n matrix of zeros. + * + * @param m + * Number of rows. + * @param n + * Number of colums. + */ + + public Matrix(int m, int n) { + this.m = m; + this.n = n; + A = new double[m][n]; + } + + /** + * Construct an m-by-n constant matrix. + * + * @param m + * Number of rows. + * @param n + * Number of colums. + * @param s + * Fill the matrix with this scalar value. + */ + + public Matrix(int m, int n, double s) { + this.m = m; + this.n = n; + A = new double[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = s; + } + } + } + + /** + * Construct a matrix from a 2-D array. + * + * @param A + * Two-dimensional array of doubles. + * @exception IllegalArgumentException + * All rows must have the same length + * @see #constructWithCopy + */ + + public Matrix(double[][] A) { + m = A.length; + n = A[0].length; + for (int i = 0; i < m; i++) { + if (A[i].length != n) { + throw new IllegalArgumentException("All rows must have the same length."); + } + } + this.A = A; + } + + /** + * Construct a matrix quickly without checking arguments. + * + * @param A + * Two-dimensional array of doubles. + * @param m + * Number of rows. + * @param n + * Number of colums. + */ + + public Matrix(double[][] A, int m, int n) { + this.A = A; + this.m = m; + this.n = n; + } + + /** + * Construct a matrix from a one-dimensional packed array + * + * @param vals + * One-dimensional array of doubles, packed by columns (ala + * Fortran). + * @param m + * Number of rows. + * @exception IllegalArgumentException + * Array length must be a multiple of m. + */ + + public Matrix(double vals[], int m) { + this.m = m; + n = (m != 0 ? vals.length / m : 0); + if (m * n != vals.length) { + throw new IllegalArgumentException("Array length must be a multiple of m."); + } + A = new double[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = vals[i + j * m]; + } + } + } + + /* + * ------------------------ Public Methods ------------------------ + */ + + /** + * Construct a matrix from a copy of a 2-D array. + * + * @param A + * Two-dimensional array of doubles. + * @exception IllegalArgumentException + * All rows must have the same length + */ + + public static Matrix constructWithCopy(double[][] A) { + int m = A.length; + int n = A[0].length; + Matrix X = new Matrix(m, n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + if (A[i].length != n) { + throw new IllegalArgumentException("All rows must have the same length."); + } + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j]; + } + } + return X; + } + + /** + * Make a deep copy of a matrix + */ + + public Matrix copy() { + Matrix X = new Matrix(m, n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j]; + } + } + return X; + } + + /** + * Clone the Matrix object. + */ + + public Object clone() { + return this.copy(); + } + + /** + * Access the internal two-dimensional array. + * + * @return Pointer to the two-dimensional array of matrix elements. + */ + + public double[][] getArray() { + return A; + } + + /** + * Copy the internal two-dimensional array. + * + * @return Two-dimensional array copy of matrix elements. + */ + + public double[][] getArrayCopy() { + double[][] C = new double[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j]; + } + } + return C; + } + + /** + * Make a one-dimensional column packed copy of the internal array. + * + * @return Matrix elements packed in a one-dimensional array by columns. + */ + + public double[] getColumnPackedCopy() { + double[] vals = new double[m * n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + vals[i + j * m] = A[i][j]; + } + } + return vals; + } + + /** + * Make a one-dimensional row packed copy of the internal array. + * + * @return Matrix elements packed in a one-dimensional array by rows. + */ + + public double[] getRowPackedCopy() { + double[] vals = new double[m * n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + vals[i * n + j] = A[i][j]; + } + } + return vals; + } + + /** + * Get row dimension. + * + * @return m, the number of rows. + */ + + public int getRowDimension() { + return m; + } + + /** + * Get column dimension. + * + * @return n, the number of columns. + */ + + public int getColumnDimension() { + return n; + } + + /** + * Get a single element. + * + * @param i + * Row index. + * @param j + * Column index. + * @return A(i,j) + * @exception ArrayIndexOutOfBoundsException + */ + + public double get(int i, int j) { + return A[i][j]; + } + + /** + * Get a submatrix. + * + * @param i0 + * Initial row index + * @param i1 + * Final row index + * @param j0 + * Initial column index + * @param j1 + * Final column index + * @return A(i0:i1,j0:j1) + * @exception ArrayIndexOutOfBoundsException + * Submatrix indices + */ + + public Matrix getMatrix(int i0, int i1, int j0, int j1) { + Matrix X = new Matrix(i1 - i0 + 1, j1 - j0 + 1); + double[][] B = X.getArray(); + try { + for (int i = i0; i <= i1; i++) { + for (int j = j0; j <= j1; j++) { + B[i - i0][j - j0] = A[i][j]; + } + } + } + catch (ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + return X; + } + + /** + * Get a submatrix. + * + * @param r + * Array of row indices. + * @param c + * Array of column indices. + * @return A(r(:),c(:)) + * @exception ArrayIndexOutOfBoundsException + * Submatrix indices + */ + + public Matrix getMatrix(int[] r, int[] c) { + Matrix X = new Matrix(r.length, c.length); + double[][] B = X.getArray(); + try { + for (int i = 0; i < r.length; i++) { + for (int j = 0; j < c.length; j++) { + B[i][j] = A[r[i]][c[j]]; + } + } + } + catch (ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + return X; + } + + /** + * Get a submatrix. + * + * @param i0 + * Initial row index + * @param i1 + * Final row index + * @param c + * Array of column indices. + * @return A(i0:i1,c(:)) + * @exception ArrayIndexOutOfBoundsException + * Submatrix indices + */ + + public Matrix getMatrix(int i0, int i1, int[] c) { + Matrix X = new Matrix(i1 - i0 + 1, c.length); + double[][] B = X.getArray(); + try { + for (int i = i0; i <= i1; i++) { + for (int j = 0; j < c.length; j++) { + B[i - i0][j] = A[i][c[j]]; + } + } + } + catch (ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + return X; + } + + /** + * Get a submatrix. + * + * @param r + * Array of row indices. + * @param j0 + * Initial column index + * @param j1 + * Final column index + * @return A(r(:),j0:j1) + * @exception ArrayIndexOutOfBoundsException + * Submatrix indices + */ + + public Matrix getMatrix(int[] r, int j0, int j1) { + Matrix X = new Matrix(r.length, j1 - j0 + 1); + double[][] B = X.getArray(); + try { + for (int i = 0; i < r.length; i++) { + for (int j = j0; j <= j1; j++) { + B[i][j - j0] = A[r[i]][j]; + } + } + } + catch (ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + return X; + } + + /** + * Set a single element. + * + * @param i + * Row index. + * @param j + * Column index. + * @param s + * A(i,j). + * @exception ArrayIndexOutOfBoundsException + */ + + public void set(int i, int j, double s) { + A[i][j] = s; + } + + /** + * Set a submatrix. + * + * @param i0 + * Initial row index + * @param i1 + * Final row index + * @param j0 + * Initial column index + * @param j1 + * Final column index + * @param X + * A(i0:i1,j0:j1) + * @exception ArrayIndexOutOfBoundsException + * Submatrix indices + */ + + public void setMatrix(int i0, int i1, int j0, int j1, Matrix X) { + try { + for (int i = i0; i <= i1; i++) { + for (int j = j0; j <= j1; j++) { + A[i][j] = X.get(i - i0, j - j0); + } + } + } + catch (ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + } + + /** + * Set a submatrix. + * + * @param r + * Array of row indices. + * @param c + * Array of column indices. + * @param X + * A(r(:),c(:)) + * @exception ArrayIndexOutOfBoundsException + * Submatrix indices + */ + + public void setMatrix(int[] r, int[] c, Matrix X) { + try { + for (int i = 0; i < r.length; i++) { + for (int j = 0; j < c.length; j++) { + A[r[i]][c[j]] = X.get(i, j); + } + } + } + catch (ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + } + + /** + * Set a submatrix. + * + * @param r + * Array of row indices. + * @param j0 + * Initial column index + * @param j1 + * Final column index + * @param X + * A(r(:),j0:j1) + * @exception ArrayIndexOutOfBoundsException + * Submatrix indices + */ + + public void setMatrix(int[] r, int j0, int j1, Matrix X) { + try { + for (int i = 0; i < r.length; i++) { + for (int j = j0; j <= j1; j++) { + A[r[i]][j] = X.get(i, j - j0); + } + } + } + catch (ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + } + + /** + * Set a submatrix. + * + * @param i0 + * Initial row index + * @param i1 + * Final row index + * @param c + * Array of column indices. + * @param X + * A(i0:i1,c(:)) + * @exception ArrayIndexOutOfBoundsException + * Submatrix indices + */ + + public void setMatrix(int i0, int i1, int[] c, Matrix X) { + try { + for (int i = i0; i <= i1; i++) { + for (int j = 0; j < c.length; j++) { + A[i][c[j]] = X.get(i - i0, j); + } + } + } + catch (ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException("Submatrix indices"); + } + } + + /** + * Matrix transpose. + * + * @return A' + */ + + public Matrix transpose() { + Matrix X = new Matrix(n, m); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[j][i] = A[i][j]; + } + } + return X; + } + + /** + * One norm + * + * @return maximum column sum. + */ + + public double norm1() { + double f = 0; + for (int j = 0; j < n; j++) { + double s = 0; + for (int i = 0; i < m; i++) { + s += Math.abs(A[i][j]); + } + f = Math.max(f, s); + } + return f; + } + + /** + * Two norm + * + * @return maximum singular value. + */ + + public double norm2() { + return (new SingularValueDecomposition(this).norm2()); + } + + /** + * Infinity norm + * + * @return maximum row sum. + */ + + public double normInf() { + double f = 0; + for (int i = 0; i < m; i++) { + double s = 0; + for (int j = 0; j < n; j++) { + s += Math.abs(A[i][j]); + } + f = Math.max(f, s); + } + return f; + } + + /** + * Frobenius norm + * + * @return sqrt of sum of squares of all elements. + */ + + public double normF() { + double f = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + f = Maths.hypot(f, A[i][j]); + } + } + return f; + } + + /** + * Unary minus + * + * @return -A + */ + + public Matrix uminus() { + Matrix X = new Matrix(m, n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = -A[i][j]; + } + } + return X; + } + + /** + * C = A + B + * + * @param B + * another matrix + * @return A + B + */ + + public Matrix plus(Matrix B) { + checkMatrixDimensions(B); + Matrix X = new Matrix(m, n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j] + B.A[i][j]; + } + } + return X; + } + + /** + * A = A + B + * + * @param B + * another matrix + * @return A + B + */ + + public Matrix plusEquals(Matrix B) { + checkMatrixDimensions(B); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = A[i][j] + B.A[i][j]; + } + } + return this; + } + + /** + * C = A - B + * + * @param B + * another matrix + * @return A - B + */ + + public Matrix minus(Matrix B) { + checkMatrixDimensions(B); + Matrix X = new Matrix(m, n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j] - B.A[i][j]; + } + } + return X; + } + + /** + * A = A - B + * + * @param B + * another matrix + * @return A - B + */ + + public Matrix minusEquals(Matrix B) { + checkMatrixDimensions(B); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = A[i][j] - B.A[i][j]; + } + } + return this; + } + + /** + * Element-by-element multiplication, C = A.*B + * + * @param B + * another matrix + * @return A.*B + */ + + public Matrix arrayTimes(Matrix B) { + checkMatrixDimensions(B); + Matrix X = new Matrix(m, n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j] * B.A[i][j]; + } + } + return X; + } + + /** + * Element-by-element multiplication in place, A = A.*B + * + * @param B + * another matrix + * @return A.*B + */ + + public Matrix arrayTimesEquals(Matrix B) { + checkMatrixDimensions(B); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = A[i][j] * B.A[i][j]; + } + } + return this; + } + + /** + * Element-by-element right division, C = A./B + * + * @param B + * another matrix + * @return A./B + */ + + public Matrix arrayRightDivide(Matrix B) { + checkMatrixDimensions(B); + Matrix X = new Matrix(m, n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = A[i][j] / B.A[i][j]; + } + } + return X; + } + + /** + * Element-by-element right division in place, A = A./B + * + * @param B + * another matrix + * @return A./B + */ + + public Matrix arrayRightDivideEquals(Matrix B) { + checkMatrixDimensions(B); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = A[i][j] / B.A[i][j]; + } + } + return this; + } + + /** + * Element-by-element left division, C = A.\B + * + * @param B + * another matrix + * @return A.\B + */ + + public Matrix arrayLeftDivide(Matrix B) { + checkMatrixDimensions(B); + Matrix X = new Matrix(m, n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = B.A[i][j] / A[i][j]; + } + } + return X; + } + + /** + * Element-by-element left division in place, A = A.\B + * + * @param B + * another matrix + * @return A.\B + */ + + public Matrix arrayLeftDivideEquals(Matrix B) { + checkMatrixDimensions(B); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = B.A[i][j] / A[i][j]; + } + } + return this; + } + + /** + * Multiply a matrix by a scalar, C = s*A + * + * @param s + * scalar + * @return s*A + */ + + public Matrix times(double s) { + Matrix X = new Matrix(m, n); + double[][] C = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + C[i][j] = s * A[i][j]; + } + } + return X; + } + + /** + * Multiply a matrix by a scalar in place, A = s*A + * + * @param s + * scalar + * @return replace A by s*A + */ + + public Matrix timesEquals(double s) { + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + A[i][j] = s * A[i][j]; + } + } + return this; + } + + /** + * Linear algebraic matrix multiplication, A * B + * + * @param B + * another matrix + * @return Matrix product, A * B + * @exception IllegalArgumentException + * Matrix inner dimensions must agree. + */ + + public Matrix times(Matrix B) { + if (B.m != n) { + throw new IllegalArgumentException("Matrix inner dimensions must agree."); + } + Matrix X = new Matrix(m, B.n); + double[][] C = X.getArray(); + double[] Bcolj = new double[n]; + for (int j = 0; j < B.n; j++) { + for (int k = 0; k < n; k++) { + Bcolj[k] = B.A[k][j]; + } + for (int i = 0; i < m; i++) { + double[] Arowi = A[i]; + double s = 0; + for (int k = 0; k < n; k++) { + s += Arowi[k] * Bcolj[k]; + } + C[i][j] = s; + } + } + return X; + } + + /** + * LU Decomposition + * + * @return LUDecomposition + * @see LUDecomposition + */ + + public LUDecomposition lu() { + return new LUDecomposition(this); + } + + /** + * QR Decomposition + * + * @return QRDecomposition + * @see QRDecomposition + */ + + public QRDecomposition qr() { + return new QRDecomposition(this); + } + + /** + * Cholesky Decomposition + * + * @return CholeskyDecomposition + * @see CholeskyDecomposition + */ + + public CholeskyDecomposition chol() { + return new CholeskyDecomposition(this); + } + + /** + * Singular Value Decomposition + * + * @return SingularValueDecomposition + * @see SingularValueDecomposition + */ + + public SingularValueDecomposition svd() { + return new SingularValueDecomposition(this); + } + + /** + * Eigenvalue Decomposition + * + * @return EigenvalueDecomposition + * @see EigenvalueDecomposition + */ + + public EigenvalueDecomposition eig() { + return new EigenvalueDecomposition(this); + } + + /** + * Solve A*X = B + * + * @param B + * right hand side + * @return solution if A is square, least squares solution otherwise + */ + + public Matrix solve(Matrix B) { + return (m == n ? (new LUDecomposition(this)).solve(B) : (new QRDecomposition(this)).solve(B)); + } + + /** + * Solve X*A = B, which is also A'*X' = B' + * + * @param B + * right hand side + * @return solution if A is square, least squares solution otherwise. + */ + + public Matrix solveTranspose(Matrix B) { + return transpose().solve(B.transpose()); + } + + /** + * Matrix inverse or pseudoinverse + * + * @return inverse(A) if A is square, pseudoinverse otherwise. + */ + + public Matrix inverse() { + return solve(identity(m, m)); + } + + /** + * Matrix determinant + * + * @return determinant + */ + + public double det() { + return new LUDecomposition(this).det(); + } + + /** + * Matrix rank + * + * @return effective numerical rank, obtained from SVD. + */ + + public int rank() { + return new SingularValueDecomposition(this).rank(); + } + + /** + * Matrix condition (2 norm) + * + * @return ratio of largest to smallest singular value. + */ + + public double cond() { + return new SingularValueDecomposition(this).cond(); + } + + /** + * Matrix trace. + * + * @return sum of the diagonal elements. + */ + + public double trace() { + double t = 0; + for (int i = 0; i < Math.min(m, n); i++) { + t += A[i][i]; + } + return t; + } + + /** + * Generate matrix with random elements + * + * @param m + * Number of rows. + * @param n + * Number of colums. + * @return An m-by-n matrix with uniformly distributed random elements. + */ + + public static Matrix random(int m, int n) { + Matrix A = new Matrix(m, n); + double[][] X = A.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + X[i][j] = Math.random(); + } + } + return A; + } + + /** + * Generate identity matrix + * + * @param m + * Number of rows. + * @param n + * Number of colums. + * @return An m-by-n matrix with ones on the diagonal and zeros elsewhere. + */ + + public static Matrix identity(int m, int n) { + Matrix A = new Matrix(m, n); + double[][] X = A.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + X[i][j] = (i == j ? 1.0 : 0.0); + } + } + return A; + } + + /** + * Print the matrix to stdout. Line the elements up in columns with a + * Fortran-like 'Fw.d' style format. + * + * @param w + * Column width. + * @param d + * Number of digits after the decimal. + */ + + public void print(int w, int d) { + print(new PrintWriter(System.out, true), w, d); + } + + /** + * Print the matrix to the output stream. Line the elements up in columns + * with a Fortran-like 'Fw.d' style format. + * + * @param output + * Output stream. + * @param w + * Column width. + * @param d + * Number of digits after the decimal. + */ + + public void print(PrintWriter output, int w, int d) { + DecimalFormat format = new DecimalFormat(); + format.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US)); + format.setMinimumIntegerDigits(1); + format.setMaximumFractionDigits(d); + format.setMinimumFractionDigits(d); + format.setGroupingUsed(false); + print(output, format, w + 2); + } + + /** + * Print the matrix to stdout. Line the elements up in columns. Use the + * format object, and right justify within columns of width characters. Note + * that is the matrix is to be read back in, you probably will want to use a + * NumberFormat that is set to US Locale. + * + * @param format + * A Formatting object for individual elements. + * @param width + * Field width for each column. + * @see java.text.DecimalFormat#setDecimalFormatSymbols + */ + + public void print(NumberFormat format, int width) { + print(new PrintWriter(System.out, true), format, width); + } + + // DecimalFormat is a little disappointing coming from Fortran or C's + // printf. + // Since it doesn't pad on the left, the elements will come out different + // widths. Consequently, we'll pass the desired column width in as an + // argument and do the extra padding ourselves. + + /** + * Print the matrix to the output stream. Line the elements up in columns. + * Use the format object, and right justify within columns of width + * characters. Note that is the matrix is to be read back in, you probably + * will want to use a NumberFormat that is set to US Locale. + * + * @param output + * the output stream. + * @param format + * A formatting object to format the matrix elements + * @param width + * Column width. + * @see java.text.DecimalFormat#setDecimalFormatSymbols + */ + + public void print(PrintWriter output, NumberFormat format, int width) { + output.println(); // start on new line. + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + String s = format.format(A[i][j]); // format the number + int padding = Math.max(1, width - s.length()); // At _least_ 1 + // space + for (int k = 0; k < padding; k++) + output.print(' '); + output.print(s); + } + output.println(); + } + output.println(); // end with blank line. + } + + /** + * Read a matrix from a stream. The format is the same the print method, so + * printed matrices can be read back in (provided they were printed using US + * Locale). Elements are separated by whitespace, all the elements for each + * row appear on a single line, the last row is followed by a blank line. + * + * @param input + * the input stream. + */ + + public static Matrix read(BufferedReader input) throws java.io.IOException { + StreamTokenizer tokenizer = new StreamTokenizer(input); + + // Although StreamTokenizer will parse numbers, it doesn't recognize + // scientific notation (E or D); however, Double.valueOf does. + // The strategy here is to disable StreamTokenizer's number parsing. + // We'll only get whitespace delimited words, EOL's and EOF's. + // These words should all be numbers, for Double.valueOf to parse. + + tokenizer.resetSyntax(); + tokenizer.wordChars(0, 255); + tokenizer.whitespaceChars(0, ' '); + tokenizer.eolIsSignificant(true); + java.util.Vector v = new java.util.Vector(); + + // Ignore initial empty lines + while (tokenizer.nextToken() == StreamTokenizer.TT_EOL) + ; + if (tokenizer.ttype == StreamTokenizer.TT_EOF) + throw new java.io.IOException("Unexpected EOF on matrix read."); + do { + v.addElement(Double.valueOf(tokenizer.sval)); // Read & store 1st + // row. + } + while (tokenizer.nextToken() == StreamTokenizer.TT_WORD); + + int n = v.size(); // Now we've got the number of columns! + double row[] = new double[n]; + for (int j = 0; j < n; j++) + // extract the elements of the 1st row. + row[j] = ((Double) v.elementAt(j)).doubleValue(); + v.removeAllElements(); + v.addElement(row); // Start storing rows instead of columns. + while (tokenizer.nextToken() == StreamTokenizer.TT_WORD) { + // While non-empty lines + v.addElement(row = new double[n]); + int j = 0; + do { + if (j >= n) + throw new java.io.IOException("Row " + v.size() + " is too long."); + row[j++] = Double.valueOf(tokenizer.sval).doubleValue(); + } + while (tokenizer.nextToken() == StreamTokenizer.TT_WORD); + if (j < n) + throw new java.io.IOException("Row " + v.size() + " is too short."); + } + int m = v.size(); // Now we've got the number of rows. + double[][] A = new double[m][]; + v.copyInto(A); // copy the rows out of the vector + return new Matrix(A); + } + + /* + * ------------------------ Private Methods ------------------------ + */ + + /** Check if size(A) == size(B) **/ + + private void checkMatrixDimensions(Matrix B) { + if (B.m != m || B.n != n) { + throw new IllegalArgumentException("Matrix dimensions must agree."); + } + } + +} diff --git a/src/bilib/src/jama/QRDecomposition.java b/src/bilib/src/jama/QRDecomposition.java new file mode 100644 index 0000000..bfa3ef5 --- /dev/null +++ b/src/bilib/src/jama/QRDecomposition.java @@ -0,0 +1,240 @@ +package jama; + +/** + * QR Decomposition. + * <P> + * For an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n + * orthogonal matrix Q and an n-by-n upper triangular matrix R so that A = Q*R. + * <P> + * The QR decompostion always exists, even if the matrix does not have full + * rank, so the constructor will never fail. The primary use of the QR + * decomposition is in the least squares solution of nonsquare systems of + * simultaneous linear equations. This will fail if isFullRank() returns false. + */ + +public class QRDecomposition implements java.io.Serializable { + + /* + * ------------------------ Class variables ------------------------ + */ + + /** + * Array for internal storage of decomposition. + * + * @serial internal array storage. + */ + private double[][] QR; + + /** + * Row and column dimensions. + * + * @serial column dimension. + * @serial row dimension. + */ + private int m, n; + + /** + * Array for internal storage of diagonal of R. + * + * @serial diagonal of R. + */ + private double[] Rdiag; + + /* + * ------------------------ Constructor ------------------------ + */ + + /** + * QR Decomposition, computed by Householder reflections. + * + * @param A + * Rectangular matrix + */ + + public QRDecomposition(Matrix A) { + // Initialize. + QR = A.getArrayCopy(); + m = A.getRowDimension(); + n = A.getColumnDimension(); + Rdiag = new double[n]; + + // Main loop. + for (int k = 0; k < n; k++) { + // Compute 2-norm of k-th column without under/overflow. + double nrm = 0; + for (int i = k; i < m; i++) { + nrm = Maths.hypot(nrm, QR[i][k]); + } + + if (nrm != 0.0) { + // Form k-th Householder vector. + if (QR[k][k] < 0) { + nrm = -nrm; + } + for (int i = k; i < m; i++) { + QR[i][k] /= nrm; + } + QR[k][k] += 1.0; + + // Apply transformation to remaining columns. + for (int j = k + 1; j < n; j++) { + double s = 0.0; + for (int i = k; i < m; i++) { + s += QR[i][k] * QR[i][j]; + } + s = -s / QR[k][k]; + for (int i = k; i < m; i++) { + QR[i][j] += s * QR[i][k]; + } + } + } + Rdiag[k] = -nrm; + } + } + + /* + * ------------------------ Public Methods ------------------------ + */ + + /** + * Is the matrix full rank? + * + * @return true if R, and hence A, has full rank. + */ + + public boolean isFullRank() { + for (int j = 0; j < n; j++) { + if (Rdiag[j] == 0) + return false; + } + return true; + } + + /** + * Return the Householder vectors + * + * @return Lower trapezoidal matrix whose columns define the reflections + */ + + public Matrix getH() { + Matrix X = new Matrix(m, n); + double[][] H = X.getArray(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (i >= j) { + H[i][j] = QR[i][j]; + } + else { + H[i][j] = 0.0; + } + } + } + return X; + } + + /** + * Return the upper triangular factor + * + * @return R + */ + + public Matrix getR() { + Matrix X = new Matrix(n, n); + double[][] R = X.getArray(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i < j) { + R[i][j] = QR[i][j]; + } + else if (i == j) { + R[i][j] = Rdiag[i]; + } + else { + R[i][j] = 0.0; + } + } + } + return X; + } + + /** + * Generate and return the (economy-sized) orthogonal factor + * + * @return Q + */ + + public Matrix getQ() { + Matrix X = new Matrix(m, n); + double[][] Q = X.getArray(); + for (int k = n - 1; k >= 0; k--) { + for (int i = 0; i < m; i++) { + Q[i][k] = 0.0; + } + Q[k][k] = 1.0; + for (int j = k; j < n; j++) { + if (QR[k][k] != 0) { + double s = 0.0; + for (int i = k; i < m; i++) { + s += QR[i][k] * Q[i][j]; + } + s = -s / QR[k][k]; + for (int i = k; i < m; i++) { + Q[i][j] += s * QR[i][k]; + } + } + } + } + return X; + } + + /** + * Least squares solution of A*X = B + * + * @param B + * A Matrix with as many rows as A and any number of columns. + * @return X that minimizes the two norm of Q*R*X-B. + * @exception IllegalArgumentException + * Matrix row dimensions must agree. + * @exception RuntimeException + * Matrix is rank deficient. + */ + + public Matrix solve(Matrix B) { + if (B.getRowDimension() != m) { + throw new IllegalArgumentException("Matrix row dimensions must agree."); + } + if (!this.isFullRank()) { + throw new RuntimeException("Matrix is rank deficient."); + } + + // Copy right hand side + int nx = B.getColumnDimension(); + double[][] X = B.getArrayCopy(); + + // Compute Y = transpose(Q)*B + for (int k = 0; k < n; k++) { + for (int j = 0; j < nx; j++) { + double s = 0.0; + for (int i = k; i < m; i++) { + s += QR[i][k] * X[i][j]; + } + s = -s / QR[k][k]; + for (int i = k; i < m; i++) { + X[i][j] += s * QR[i][k]; + } + } + } + // Solve R*X = Y; + for (int k = n - 1; k >= 0; k--) { + for (int j = 0; j < nx; j++) { + X[k][j] /= Rdiag[k]; + } + for (int i = 0; i < k; i++) { + for (int j = 0; j < nx; j++) { + X[i][j] -= X[k][j] * QR[i][k]; + } + } + } + return (new Matrix(X, n, nx).getMatrix(0, n - 1, 0, nx - 1)); + } +} diff --git a/src/bilib/src/jama/SingularValueDecomposition.java b/src/bilib/src/jama/SingularValueDecomposition.java new file mode 100644 index 0000000..267c123 --- /dev/null +++ b/src/bilib/src/jama/SingularValueDecomposition.java @@ -0,0 +1,573 @@ +package jama; + +/** + * Singular Value Decomposition. + * <P> + * For an m-by-n matrix A with m >= n, the singular value decomposition is an + * m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and an n-by-n + * orthogonal matrix V so that A = U*S*V'. + * <P> + * The singular values, sigma[k] = S[k][k], are ordered so that sigma[0] >= + * sigma[1] >= ... >= sigma[n-1]. + * <P> + * The singular value decompostion always exists, so the constructor will never + * fail. The matrix condition number and the effective numerical rank can be + * computed from this decomposition. + */ + +public class SingularValueDecomposition implements java.io.Serializable { + + /* + * ------------------------ Class variables ------------------------ + */ + + /** + * Arrays for internal storage of U and V. + * + * @serial internal storage of U. + * @serial internal storage of V. + */ + private double[][] U, V; + + /** + * Array for internal storage of singular values. + * + * @serial internal storage of singular values. + */ + private double[] s; + + /** + * Row and column dimensions. + * + * @serial row dimension. + * @serial column dimension. + */ + private int m, n; + + /* + * ------------------------ Constructor ------------------------ + */ + + /** + * Construct the singular value decomposition + * + * @param Arg + * Rectangular matrix + */ + + public SingularValueDecomposition(Matrix Arg) { + + // Derived from LINPACK code. + // Initialize. + double[][] A = Arg.getArrayCopy(); + m = Arg.getRowDimension(); + n = Arg.getColumnDimension(); + + /* + * Apparently the failing cases are only a proper subset of (m<n), so + * let's not throw error. Correct fix to come later? if (m<n) { throw + * new IllegalArgumentException("Jama SVD only works for m >= n"); } + */ + int nu = Math.min(m, n); + s = new double[Math.min(m + 1, n)]; + U = new double[m][nu]; + V = new double[n][n]; + double[] e = new double[n]; + double[] work = new double[m]; + boolean wantu = true; + boolean wantv = true; + + // Reduce A to bidiagonal form, storing the diagonal elements + // in s and the super-diagonal elements in e. + + int nct = Math.min(m - 1, n); + int nrt = Math.max(0, Math.min(n - 2, m)); + for (int k = 0; k < Math.max(nct, nrt); k++) { + if (k < nct) { + + // Compute the transformation for the k-th column and + // place the k-th diagonal in s[k]. + // Compute 2-norm of k-th column without under/overflow. + s[k] = 0; + for (int i = k; i < m; i++) { + s[k] = Maths.hypot(s[k], A[i][k]); + } + if (s[k] != 0.0) { + if (A[k][k] < 0.0) { + s[k] = -s[k]; + } + for (int i = k; i < m; i++) { + A[i][k] /= s[k]; + } + A[k][k] += 1.0; + } + s[k] = -s[k]; + } + for (int j = k + 1; j < n; j++) { + if ((k < nct) & (s[k] != 0.0)) { + + // Apply the transformation. + + double t = 0; + for (int i = k; i < m; i++) { + t += A[i][k] * A[i][j]; + } + t = -t / A[k][k]; + for (int i = k; i < m; i++) { + A[i][j] += t * A[i][k]; + } + } + + // Place the k-th row of A into e for the + // subsequent calculation of the row transformation. + + e[j] = A[k][j]; + } + if (wantu & (k < nct)) { + + // Place the transformation in U for subsequent back + // multiplication. + + for (int i = k; i < m; i++) { + U[i][k] = A[i][k]; + } + } + if (k < nrt) { + + // Compute the k-th row transformation and place the + // k-th super-diagonal in e[k]. + // Compute 2-norm without under/overflow. + e[k] = 0; + for (int i = k + 1; i < n; i++) { + e[k] = Maths.hypot(e[k], e[i]); + } + if (e[k] != 0.0) { + if (e[k + 1] < 0.0) { + e[k] = -e[k]; + } + for (int i = k + 1; i < n; i++) { + e[i] /= e[k]; + } + e[k + 1] += 1.0; + } + e[k] = -e[k]; + if ((k + 1 < m) & (e[k] != 0.0)) { + + // Apply the transformation. + + for (int i = k + 1; i < m; i++) { + work[i] = 0.0; + } + for (int j = k + 1; j < n; j++) { + for (int i = k + 1; i < m; i++) { + work[i] += e[j] * A[i][j]; + } + } + for (int j = k + 1; j < n; j++) { + double t = -e[j] / e[k + 1]; + for (int i = k + 1; i < m; i++) { + A[i][j] += t * work[i]; + } + } + } + if (wantv) { + + // Place the transformation in V for subsequent + // back multiplication. + + for (int i = k + 1; i < n; i++) { + V[i][k] = e[i]; + } + } + } + } + + // Set up the final bidiagonal matrix or order p. + + int p = Math.min(n, m + 1); + if (nct < n) { + s[nct] = A[nct][nct]; + } + if (m < p) { + s[p - 1] = 0.0; + } + if (nrt + 1 < p) { + e[nrt] = A[nrt][p - 1]; + } + e[p - 1] = 0.0; + + // If required, generate U. + + if (wantu) { + for (int j = nct; j < nu; j++) { + for (int i = 0; i < m; i++) { + U[i][j] = 0.0; + } + U[j][j] = 1.0; + } + for (int k = nct - 1; k >= 0; k--) { + if (s[k] != 0.0) { + for (int j = k + 1; j < nu; j++) { + double t = 0; + for (int i = k; i < m; i++) { + t += U[i][k] * U[i][j]; + } + t = -t / U[k][k]; + for (int i = k; i < m; i++) { + U[i][j] += t * U[i][k]; + } + } + for (int i = k; i < m; i++) { + U[i][k] = -U[i][k]; + } + U[k][k] = 1.0 + U[k][k]; + for (int i = 0; i < k - 1; i++) { + U[i][k] = 0.0; + } + } + else { + for (int i = 0; i < m; i++) { + U[i][k] = 0.0; + } + U[k][k] = 1.0; + } + } + } + + // If required, generate V. + + if (wantv) { + for (int k = n - 1; k >= 0; k--) { + if ((k < nrt) & (e[k] != 0.0)) { + for (int j = k + 1; j < nu; j++) { + double t = 0; + for (int i = k + 1; i < n; i++) { + t += V[i][k] * V[i][j]; + } + t = -t / V[k + 1][k]; + for (int i = k + 1; i < n; i++) { + V[i][j] += t * V[i][k]; + } + } + } + for (int i = 0; i < n; i++) { + V[i][k] = 0.0; + } + V[k][k] = 1.0; + } + } + + // Main iteration loop for the singular values. + + int pp = p - 1; + int iter = 0; + double eps = Math.pow(2.0, -52.0); + double tiny = Math.pow(2.0, -966.0); + while (p > 0) { + int k, kase; + + // Here is where a test for too many iterations would go. + + // This section of the program inspects for + // negligible elements in the s and e arrays. On + // completion the variables kase and k are set as follows. + + // kase = 1 if s(p) and e[k-1] are negligible and k<p + // kase = 2 if s(k) is negligible and k<p + // kase = 3 if e[k-1] is negligible, k<p, and + // s(k), ..., s(p) are not negligible (qr step). + // kase = 4 if e(p-1) is negligible (convergence). + + for (k = p - 2; k >= -1; k--) { + if (k == -1) { + break; + } + if (Math.abs(e[k]) <= tiny + eps * (Math.abs(s[k]) + Math.abs(s[k + 1]))) { + e[k] = 0.0; + break; + } + } + if (k == p - 2) { + kase = 4; + } + else { + int ks; + for (ks = p - 1; ks >= k; ks--) { + if (ks == k) { + break; + } + double t = (ks != p ? Math.abs(e[ks]) : 0.) + (ks != k + 1 ? Math.abs(e[ks - 1]) : 0.); + if (Math.abs(s[ks]) <= tiny + eps * t) { + s[ks] = 0.0; + break; + } + } + if (ks == k) { + kase = 3; + } + else if (ks == p - 1) { + kase = 1; + } + else { + kase = 2; + k = ks; + } + } + k++; + + // Perform the task indicated by kase. + + switch (kase) { + + // Deflate negligible s(p). + + case 1: { + double f = e[p - 2]; + e[p - 2] = 0.0; + for (int j = p - 2; j >= k; j--) { + double t = Maths.hypot(s[j], f); + double cs = s[j] / t; + double sn = f / t; + s[j] = t; + if (j != k) { + f = -sn * e[j - 1]; + e[j - 1] = cs * e[j - 1]; + } + if (wantv) { + for (int i = 0; i < n; i++) { + t = cs * V[i][j] + sn * V[i][p - 1]; + V[i][p - 1] = -sn * V[i][j] + cs * V[i][p - 1]; + V[i][j] = t; + } + } + } + } + break; + + // Split at negligible s(k). + + case 2: { + double f = e[k - 1]; + e[k - 1] = 0.0; + for (int j = k; j < p; j++) { + double t = Maths.hypot(s[j], f); + double cs = s[j] / t; + double sn = f / t; + s[j] = t; + f = -sn * e[j]; + e[j] = cs * e[j]; + if (wantu) { + for (int i = 0; i < m; i++) { + t = cs * U[i][j] + sn * U[i][k - 1]; + U[i][k - 1] = -sn * U[i][j] + cs * U[i][k - 1]; + U[i][j] = t; + } + } + } + } + break; + + // Perform one qr step. + + case 3: { + + // Calculate the shift. + + double scale = Math.max(Math.max(Math.max(Math.max(Math.abs(s[p - 1]), Math.abs(s[p - 2])), Math.abs(e[p - 2])), Math.abs(s[k])), Math.abs(e[k])); + double sp = s[p - 1] / scale; + double spm1 = s[p - 2] / scale; + double epm1 = e[p - 2] / scale; + double sk = s[k] / scale; + double ek = e[k] / scale; + double b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2.0; + double c = (sp * epm1) * (sp * epm1); + double shift = 0.0; + if ((b != 0.0) | (c != 0.0)) { + shift = Math.sqrt(b * b + c); + if (b < 0.0) { + shift = -shift; + } + shift = c / (b + shift); + } + double f = (sk + sp) * (sk - sp) + shift; + double g = sk * ek; + + // Chase zeros. + + for (int j = k; j < p - 1; j++) { + double t = Maths.hypot(f, g); + double cs = f / t; + double sn = g / t; + if (j != k) { + e[j - 1] = t; + } + f = cs * s[j] + sn * e[j]; + e[j] = cs * e[j] - sn * s[j]; + g = sn * s[j + 1]; + s[j + 1] = cs * s[j + 1]; + if (wantv) { + for (int i = 0; i < n; i++) { + t = cs * V[i][j] + sn * V[i][j + 1]; + V[i][j + 1] = -sn * V[i][j] + cs * V[i][j + 1]; + V[i][j] = t; + } + } + t = Maths.hypot(f, g); + cs = f / t; + sn = g / t; + s[j] = t; + f = cs * e[j] + sn * s[j + 1]; + s[j + 1] = -sn * e[j] + cs * s[j + 1]; + g = sn * e[j + 1]; + e[j + 1] = cs * e[j + 1]; + if (wantu && (j < m - 1)) { + for (int i = 0; i < m; i++) { + t = cs * U[i][j] + sn * U[i][j + 1]; + U[i][j + 1] = -sn * U[i][j] + cs * U[i][j + 1]; + U[i][j] = t; + } + } + } + e[p - 2] = f; + iter = iter + 1; + } + break; + + // Convergence. + + case 4: { + + // Make the singular values positive. + + if (s[k] <= 0.0) { + s[k] = (s[k] < 0.0 ? -s[k] : 0.0); + if (wantv) { + for (int i = 0; i <= pp; i++) { + V[i][k] = -V[i][k]; + } + } + } + + // Order the singular values. + + while (k < pp) { + if (s[k] >= s[k + 1]) { + break; + } + double t = s[k]; + s[k] = s[k + 1]; + s[k + 1] = t; + if (wantv && (k < n - 1)) { + for (int i = 0; i < n; i++) { + t = V[i][k + 1]; + V[i][k + 1] = V[i][k]; + V[i][k] = t; + } + } + if (wantu && (k < m - 1)) { + for (int i = 0; i < m; i++) { + t = U[i][k + 1]; + U[i][k + 1] = U[i][k]; + U[i][k] = t; + } + } + k++; + } + iter = 0; + p--; + } + break; + } + } + } + + /* + * ------------------------ Public Methods ------------------------ + */ + + /** + * Return the left singular vectors + * + * @return U + */ + + public Matrix getU() { + return new Matrix(U, m, Math.min(m + 1, n)); + } + + /** + * Return the right singular vectors + * + * @return V + */ + + public Matrix getV() { + return new Matrix(V, n, n); + } + + /** + * Return the one-dimensional array of singular values + * + * @return diagonal of S. + */ + + public double[] getSingularValues() { + return s; + } + + /** + * Return the diagonal matrix of singular values + * + * @return S + */ + + public Matrix getS() { + Matrix X = new Matrix(n, n); + double[][] S = X.getArray(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + S[i][j] = 0.0; + } + S[i][i] = this.s[i]; + } + return X; + } + + /** + * Two norm + * + * @return max(S) + */ + + public double norm2() { + return s[0]; + } + + /** + * Two norm condition number + * + * @return max(S)/min(S) + */ + + public double cond() { + return s[0] / s[Math.min(m, n) - 1]; + } + + /** + * Effective numerical matrix rank + * + * @return Number of nonnegligible singular values. + */ + + public int rank() { + double eps = Math.pow(2.0, -52.0); + double tol = Math.max(m, n) * s[0] * eps; + int r = 0; + for (int i = 0; i < s.length; i++) { + if (s[i] > tol) { + r++; + } + } + return r; + } +} diff --git a/src/bilib/src/levenbergmarquardt/Cholesky.java b/src/bilib/src/levenbergmarquardt/Cholesky.java new file mode 100644 index 0000000..79ed3c3 --- /dev/null +++ b/src/bilib/src/levenbergmarquardt/Cholesky.java @@ -0,0 +1,111 @@ +package levenbergmarquardt; + +/** + * + * <p> + * Title: Cholesky Decomposition + * </p> + * <p> + * Description: Performs a Cholesky decomposition of a matrix and solve a linear + * system using this decomposition. Ported to Java from the Numerical Recipes in + * C. Press, Teukolsky, Vetterling,and Flannery. 2nd edition. Cambridge + * University Press, 1992. + * </p> + */ + +public class Cholesky { + + /** + * Given a positive-definite symmetric matrix A[1..n][1..n], this method + * constructs its Cholesky decomposition, A = L � L' . On input, only the + * upper triangle of a need be given; it is not modified. The Cholesky + * factor L is returned in the lower triangle of a, except for its diagonal + * elements which are returned in p[1..n]. + * + * @param A + * double[][] Input matrix. + * @param p + * double[] + * @return boolean Returns false if the decomposition is not possible. + */ + public static boolean decomp(double[][] A, double[] p) { + int n = A.length; + int i, j, k; + double sum; + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + sum = A[i][j]; + for (k = i - 1; k >= 0; k--) { + sum -= (A[i][k] * A[j][k]); + } + if (i == j) { + if (sum <= 0.) { + return false; // not positive definite + } + p[i] = Math.sqrt(sum); + } + else { + A[j][i] = sum / p[i]; + } + } + } + return true; + } // decomp + + /** + * Solves a the linear system Ax=b. + * + * @param A + * double[][] Is the result of decomp(A) + * @param p + * double[] The resulting diagonal vector. + * @param b + * double[] + * @param x + * double[] + */ + private static void solve(double[][] A, double[] p, double[] b, double[] x) { + int n = A.length; + int i, k; + double sum; + // Solve L � y = b, storing y in x. + for (i = 0; i < n; i++) { + sum = b[i]; + for (k = i - 1; k >= 0; k--) { + sum -= (A[i][k] * x[k]); + } + x[i] = sum / p[i]; + } + + // Solve L' � x = y. + for (i = n - 1; i >= 0; i--) { + sum = x[i]; + for (k = i + 1; k < n; k++) { + sum -= (A[k][i] * x[k]); + } + x[i] = sum / p[i]; + } + } // solve + + /** + * Solves the linear system Ax=b. + * + * @param A + * double[][] + * @param x + * double[] + * @param b + * double[] + * @return boolean returns false if the system can not be solved using + * Cholesky decomposition. + */ + public static boolean solve(double[][] A, double[] x, double[] b) { + double[] p = new double[A.length]; + if (!decomp(A, p)) { + return false; + } + solve(A, p, b, x); + return true; + } // solve + +} // Cholesky diff --git a/src/bilib/src/levenbergmarquardt/Function.java b/src/bilib/src/levenbergmarquardt/Function.java new file mode 100644 index 0000000..1ade3c6 --- /dev/null +++ b/src/bilib/src/levenbergmarquardt/Function.java @@ -0,0 +1,32 @@ +package levenbergmarquardt; + +/** + */ +public interface Function { + + /** + * Evaluates the model at point x (may be mulidimensional). + * + * @param x + * double[] Point where we evaluate the model function. + * @param a + * double[] Model estimators. + * @return double + */ + public abstract double eval(double[] x, double[] a); + + /** + * Returns the kth component of the gradient df(x,a)/da_k + * + * @param x + * double[] + * @param a + * double[] + * @param ak + * int + * @return double + */ + public abstract double grad(double[] x, double[] a, int ak); + + public abstract void setDebug(boolean debug); +} diff --git a/src/bilib/src/levenbergmarquardt/LevenbergMarquardt.java b/src/bilib/src/levenbergmarquardt/LevenbergMarquardt.java new file mode 100644 index 0000000..3d32bab --- /dev/null +++ b/src/bilib/src/levenbergmarquardt/LevenbergMarquardt.java @@ -0,0 +1,297 @@ +package levenbergmarquardt; + +/** + * + * <p> + * Title: Levenberg-Marquardt + * </p> + * <p> + * Description: Perfoms data fitting to a non linear model using the + * Levenberg-Marquadrt method. Ported to Java from the Numerical Recipes in C. + * Press, Teukolsky, Vetterling,and Flannery. 2nd edition. Cambridge University + * Press, 1992. + * </p> + */ + +public class LevenbergMarquardt { + + private Function f; + private double lambdaInitial = 0.0001; + private int itmax = 1000; + private boolean print = false; + private int iter; + private double tolerance = 0.001; + + /** + * Levenberg-Marquardt constructor. Supply a Function object, f, that + * evaluates the fitting function y, and its derivatives dyda[1..ma] with + * respect to the fitting parameters a at x. On the first call provide an + * initial guess for the parameters a, and set alamda to some small value, + * e.g. alambda=0.001. If a step succeeds chisq becomes smaller and alamda + * decreases by a factor of 10. If a step fails alamda grows by a factor of + * 10. + * + * @param f + * Function + * @param lambdaInitial + * double + * @param itmax + * int + */ + public LevenbergMarquardt(Function f, double lambdaInitial, int itmax, boolean print) { + this.f = f; + this.lambdaInitial = lambdaInitial; + this.itmax = itmax; + this.print = print; + if (print) { + System.out.print("CONSTRUCTOR \tlambda:" + lambdaInitial + " max iterations: " + itmax); + } + } + + public LevenbergMarquardt(Function f, int itmax, double tolerance) { + this.f = f; + this.itmax = itmax; + this.tolerance = tolerance; + } + + public LevenbergMarquardt(Function f, int itmax) { + this.f = f; + this.itmax = itmax; + } + + public LevenbergMarquardt(Function f, boolean print) { + this.f = f; + this.print = print; + } + + public LevenbergMarquardt(Function f) { + this.f = f; + } + + public void setPrint(boolean print) { + this.print = print; + } + + /** + * Levenberg-Marquardt method, attempting to reduce the value chi2 of a fit + * between a set of data points x[1..ndata], y[1..ndata] with individual + * standard deviations sig[1..ndata], and a nonlinear function dependent on + * ma coefficients a[1..ma]. The input array ia[1..ma] indicates by true, + * entries those components of a that should be fitted for, and by false, + * entries those components that should be held fixed at their input values. + * The program returns current best-fit values for the parameters a[1..ma], + * and chi2 = chisq. + * + * @param x + * double[] + * @param y + * double[] + * @param sig + * double[] + * @param a + * double[] + * @return double + * + */ + public double minimize(double x[], double y[], double sig[], double a[]) { + iter = 0; + double lambda = lambdaInitial; + + boolean ia[] = new boolean[a.length]; + for (int i = 0; i < a.length; i++) + ia[i] = true; + + int rep = 0; + boolean done = false; + double eps = 0; + int mfit = 0; + int j, k, l; + int ma = a.length; + double ochisq = 0, chisq; + + double[][] covar = new double[ma][ma]; + double[][] alpha = new double[ma][ma]; + double[] beta = new double[ma]; + double[] atry = new double[ma]; + double[] da = new double[ma]; + + double[] oneda; + + // initialization + for (mfit = 0, j = 0; j < ma; j++) { + if (ia[j]) { + mfit++; + } + } + oneda = new double[mfit]; + + chisq = mrqcof(x, y, sig, a, ia, alpha, beta); + ochisq = chisq; + for (j = 0; j < ma; j++) { + atry[j] = a[j]; + } + + do { + // Alter linearized fitting matrix, by augmenting diagonal elements. + for (j = 0; j < mfit; j++) { + for (k = 0; k < mfit; k++) { + covar[j][k] = alpha[j][k]; + } + covar[j][j] = alpha[j][j] * (1.0 + lambda); + oneda[j] = beta[j]; + } + + Cholesky.solve(covar, oneda, oneda); // Matrix solution. + + for (j = 0; j < mfit; j++) { + da[j] = oneda[j]; + } + + for (j = 0, l = 0; l < ma; l++) { + if (ia[l]) { + atry[l] = a[l] + da[j++]; + } + } + chisq = mrqcof(x, y, sig, atry, ia, covar, da); + eps = Math.abs(chisq - ochisq); + if (print) { + System.out.print("#" + iter + "\t chi:" + Math.round(Math.sqrt(chisq) * 1000) / 1000.0 + " \tlambda:" + lambda + " eps:" + eps); + for (int i = 0; i < a.length; i++) + System.out.print("\t a[" + i + "]=" + atry[i]); + System.out.println(";"); + } + if (chisq < ochisq) { + // Success, accept the new solution. + lambda *= 0.1; + ochisq = chisq; + for (j = 0; j < mfit; j++) { + for (k = 0; k < mfit; k++) { + alpha[j][k] = covar[j][k]; + } + beta[j] = da[j]; + } + for (l = 0; l < ma; l++) { + a[l] = atry[l]; + } + } + else { + // Failure, increase alamda and return. + lambda *= 10.0; + chisq = ochisq; + } + iter++; + if (eps > tolerance) { + rep = 0; + } + else { + rep++; + if (rep == 4) { + done = true; + } + } + + } + while (iter < itmax && !done); + if (print) + System.out.println("Final iter" + iter + "\t rep:" + rep + " \tdone:" + done + " eps:" + eps + " tolerance:" + tolerance); + + return Math.sqrt(chisq); + } + + /** + * Return the number of iterations after minimization. + * + * @return number of iteration + */ + public int getIteration() { + return iter; + } + + /** + * Used by mrqmin to evaluate the linearized fitting matrix alpha, and + * vector beta as in "NR in C"(15.5.8), and calculate chi2. + * + * @param x + * double[] + * @param y + * double[] + * @param sig + * double[] + * @param a + * double[] + * @param ia + * boolean[] + * @param alpha + * double[][] + * @param beta + * double[] + * @param f + * LMfunc + * @return double + */ + private double mrqcof(double x[], double y[], double sig[], double a[], boolean ia[], double alpha[][], double beta[]) { + + int ndata = x.length; + int ma = a.length; + double chisq; + int i, j, k, l, m, mfit = 0; + double ymod, wt, sig2i, dy; + double[] dyda = new double[ma]; + + for (j = 0; j < ma; j++) { + if (ia[j]) { + mfit++; + } + } + + // Initialize(symmetric) alpha, beta. + for (j = 0; j < mfit; j++) { + for (k = 0; k <= j; k++) { + alpha[j][k] = 0; + } + beta[j] = 0; + } + + chisq = 0; + + // Summation loop over all data. + for (i = 0; i < ndata; i++) { + double[] xi = new double[1]; + xi[0] = x[i]; + ymod = f.eval(xi, a); + for (k = 0; k < a.length; k++) { + dyda[k] = f.grad(xi, a, k); + } + + /* + * if (print) { System.out.print("D" + iter); for(int p=0; + * p<dyda.length; p++) System.out.print("\t da["+p+"]=" + dyda[p]); + * System.out.println(";"); } + */ + sig2i = 1.0 / (sig[i] * sig[i]); + dy = y[i] - ymod; + for (j = 0, l = 0; l < ma; l++) { + if (ia[l]) { + wt = dyda[l] * sig2i; + for (k = 0, m = 0; m <= l; m++) { + if (ia[m]) { + alpha[j][k++] += wt * dyda[m]; + } + } + beta[j] += dy * wt; + j++; + } + } + chisq += dy * dy * sig2i; // And find chi2. + } + + // Fill in the symmetric side of alpha + for (j = 1; j < mfit; j++) { + for (k = 0; k < j; k++) { + alpha[k][j] = alpha[j][k]; + } + } + return chisq; + } + +} diff --git a/src/bilib/src/polyharmonicwavelets/Autocorrelation.java b/src/bilib/src/polyharmonicwavelets/Autocorrelation.java new file mode 100644 index 0000000..2bbedf2 --- /dev/null +++ b/src/bilib/src/polyharmonicwavelets/Autocorrelation.java @@ -0,0 +1,291 @@ +package polyharmonicwavelets; + +// +// Autocorrelation.java +// PolyharmonicWavelets +// +// Created by Biomedical Imaging Group on 2/13/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +import java.util.*; +import java.text.DecimalFormat; + +/** + * This class computes the autocorrelation of the polyharmonic B-spline + * function. Two methods can be used, the Gamma function method [1] or the + * iterative algorithm [2]. <br> + * References: <br> + * [1] Yan Barbotin semmester project <br> + * [2] T. Blu, D. Van De Ville, M. Unser, ''Numerical methods for the + * computation of wavelet correlation sequences,'' SIAM Numerical Analysis. <br> + * [3] Matlab documentation + * + * @author Katarina Balac, EPFL. + */ + +public class Autocorrelation { + + /** + * Returns the Fourier domain autocorrelation of any scaling function given + * the squared modulus of the refinement filter. <br> + * Reference: T. Blu, D. Van De Ville, M. Unser, ''Numerical methods for the + * computation of wavelet correlation sequences,'' SIAM Numerical Analysis. + * + * @param HH + * the squared modulus of the refinement filter. + * @return the autocorreltion of scaling function. + */ + + public static ComplexImage autocorrIterative(ComplexImage HH) { + int nx = HH.nx; + int ny = HH.ny; + int lx = nx / 2; + int ly = ny / 2; + int lyx = ly * lx; + ComplexImage A0 = new ComplexImage(lx, ly, true); + ComplexImage Af = new ComplexImage(lx, ly); + ComplexImage Afe = new ComplexImage(lx + 1, ly + 1); + ComplexImage Ad = new ComplexImage(lx, ly, true); + ComplexImage Aq = new ComplexImage(lx, ly, true); + ComplexImage A1 = new ComplexImage(lx, ly, true); + ComplexImage At = new ComplexImage(nx, ny, true); // Does not change + // size + ComplexImage Ai = new ComplexImage(nx, ny); + for (int i = 0; i < lx * ly; i++) { + A0.real[i] = 1.0; + } + final double crit = 0.00000001; // stop criterion + double improvement; + int lx2 = lx / 2; + int lx231 = 3 * lx2 - 1; + int ly2 = ly / 2; + int ly231 = 3 * ly2 - 1; + int ly32 = 3 * ly / 2; + int k1 = nx * 3 * ly / 2 + lx / 2; + int k3 = nx * ly / 2 + 3 * lx / 2; + int k2 = k3 - 1; + int k4 = nx * (ly32 - 1) + lx / 2; + int count = 0; + int maxit = 100; + do { + count++; + Af.copyImageContent(A0); + Af.iFFT2D(); + Af.shift(); + for (int x = 0; x < lx; x++) { + Af.real[x] /= 2.0; + Af.imag[x] /= 2.0; + } + for (int y = 0; y < lyx; y += lx) { + Af.real[y] /= 2.0; + Af.imag[y] /= 2.0; + } + Afe.extend(Af); + Ai.putZeros(); + Ai.putSubimage(lx2, ly2, Afe); + Ai.shift(); + Ai.FFT2D(); // Ai is real + // recursion + A1.putZeros(); + At.copyImageContent(HH); + At.multiply(Ai); + Aq.getSubimageContent(0, Aq.nx - 1, 0, Aq.ny - 1, At); + A1.add(Aq); + Aq.getSubimageContent(0, Aq.nx - 1, ly, ly + Aq.ny - 1, At); + A1.add(Aq); + Aq.getSubimageContent(lx, lx + Aq.nx - 1, 0, Aq.ny - 1, At); + A1.add(Aq); + Aq.getSubimageContent(lx, lx + Aq.nx - 1, ly, ly + Aq.ny - 1, At); + A1.add(Aq); + Ad.copyImageContent(A1); + Ad.subtract(A0); + improvement = Ad.meanModulus(); + A0 = A1.copyImage(); + } + while ((improvement > crit) && (count < maxit)); + System.out.println("The autocoorelation has been computed in " + count + " iterations."); + if (count == maxit) { + System.out.println("The autocorrelation does not converge!"); + } + return A0; + } + + /** + * Returns the autocorrelation of the polyharmonic B-spline function given + * the squared modulus of its localisation. <br> + * Reference: Yan Barbotin semmester project + * + * @param loc + * the polyharmonic B-spline localisation + * @param order + * the order of polyharmonic B-spline + * @return the polyharmonic B-spline autocorrelation + */ + + public static ComplexImage autocorrGamma(ComplexImage loc, double order) { + final double PI = Math.PI; + final double PI2 = 2.0 * PI; + double[][] d = { { 0.0, 1.0 }, { 0.0, 2.0 }, { 1.0, 0.0 }, { 1.0, 1.0 }, { 1.0, 2.0 }, { 2.0, 0.0 }, { 2.0, 1.0 }, { -1.0, 0.0 }, { -1.0, 1.0 }, { -1.0, 2.0 }, { -2.0, 0.0 }, + { -2.0, 1.0 }, { 0.0, -1.0 }, { 0.0, -2.0 }, { 1.0, -1.0 }, { 1.0, -2.0 }, { 2.0, -1.0 }, { -1.0, -1.0 }, { -1.0, -2.0 }, { -2.0, -1.0 }, { 0.0, 0.0 } }; // 21 + // pair + int nx = loc.nx; + int ny = loc.ny; + int nxy = nx * ny; + ComplexImage ac = new ComplexImage(nx, ny, true); + GammaFunction gm = new GammaFunction(); + double gammanorm = Math.exp(gm.lnGamma(order)); + for (int kx = 0, nx2 = nx / 2; kx <= nx2; kx++) { + for (int ky = 0, ny2 = ny / 2; ky <= ny2; ky++) { + int kynx = ky * nx; + if (ac.real[kynx + kx] == 0.0) { + int kxny = kx * ny; + double x = (double) kx / (double) nx; + double y = (double) ky / (double) ny; + double res = 1.0 / (order - 1.0); + for (int i = 0; i < 21; i++) { + double sqn = PI * ((x - d[i][0]) * (x - d[i][0]) + (y - d[i][1]) * (y - d[i][1])); + res += gm.incompleteGammaQ(order, sqn) * gammanorm / Math.pow(sqn, order); + sqn = PI * (d[i][0] * d[i][0] + d[i][1] * d[i][1]); + if (sqn > 0.0) { + res += incompleteGammaGeneral(sqn, 1.0 - order) * Math.cos(PI2 * (d[i][0] * x + d[i][1] * y)) / Math.pow(sqn, 1.0 - order); + } + } + ac.real[kynx + kx] = res; + if (kx > 0) { + ac.real[kynx + nx - kx] = res; + } + if (ky > 0) { + ac.real[nxy - kynx + kx] = res; + } + if ((kx > 0) && (ky > 0)) { + ac.real[nxy - kynx + nx - kx] = res; + } + if ((((kynx / ny) * ny) == kynx) && (((kxny / nx) * nx) == kxny)) { + int kx1 = ky * nx / ny; + int ky1 = kx * ny / nx; + kynx = ky1 * nx; + kxny = kx1 * ny; + ac.real[kynx + kx1] = res; + if (kx1 > 0) { + ac.real[kynx + nx - kx1] = res; + } + if (ky1 > 0) { + ac.real[nxy - kynx + kx1] = res; + } + if ((kx1 > 0) && (ky1 > 0)) { + ac.real[nxy - kynx + nx - kx1] = res; + } + } + } + } + } + ac.multiply(Math.pow(PI, order) / (gammanorm * Math.pow(PI2, 2.0 * order))); + ac.multiply(loc); + ac.real[0] = 1.0; + return ac; + } + + /* + * Solves the incomplete gamma function even for negative a, regularised + * integral from x to infinity. Not normalised. x has to be positive. + * Reference: Yan Barbotin semmester project + */ + + private static double incompleteGammaGeneral(double x, double a) { + double res = 0; + GammaFunction gm = new GammaFunction(); + if (a < 0) { + double a0 = a; + int iter = 0; + while (a < 0) { + a += 1.0; + iter++; + } + if (a == 0.0) { + res = expInt(x); + } + else { + res = gm.incompleteGammaQ(a, x) * Math.exp(gm.lnGamma(a)); + } + res *= Math.exp(x - a * Math.log(x)); + for (int k = 1; k <= iter; k++) { + res = (x * res - 1.0) / (a - (double) k); + } + res *= Math.exp(a0 * Math.log(x) - x); + } + else { + if (a == 0.0) { + res = expInt(x); + } + else { + res = gm.incompleteGammaQ(a, x) * Math.exp(gm.lnGamma(a)); + } + } + return res; + } + + /* + * Computes the exponential integral for a real positive argument x. Copied + * from Matlab. + */ + + private static double expInt(double x) { + double[] p = { -3.602693626336023e-09, -4.819538452140960e-07, -2.569498322115933e-05, -6.973790859534190e-04, -1.019573529845792e-02, -7.811863559248197e-02, -3.012432892762715e-01, + -7.773807325735529e-01, 8.267661952366478e+00 }; + double d = 0.0; + double y = 0.0; + for (int j = 0; j < 9; j++) { + d *= x; + d += p[j]; + } + if (d > 0.0) { + double egamma = 0.57721566490153286061; + y = -egamma - Math.log(x); + double term = x; + double pterm = x; + double eps = 0.00000000000000000000000000001; + for (double j = 2.0; Math.abs(term) > eps; j += 1.0) { + y += term; + pterm = -x * pterm / j; + term = pterm / j; + } + } + else { + double n = 1.0; + double am2 = 0.0; + double bm2 = 1.0; + double am1 = 1.0; + double bm1 = x; + double f = am1 / bm1; + double oldf = f + 100.0; + double j = 2.0; + double eps = 0.000000000000000000000000001; + while (Math.abs(f - oldf) > eps) { + double alpha = n - 1.0 + j / 2.0; + double a = am1 + alpha * am2; + double b = bm1 + alpha * bm2; + am2 = am1 / b; + bm2 = bm1 / b; + am1 = a / b; + bm1 = 1.0; + oldf = f; + f = am1; + j += 1.0; + alpha = (j - 1.0) / 2.0; + double beta = x; + a = beta * am1 + alpha * am2; + b = beta * bm1 + alpha * bm2; + am2 = am1 / b; + bm2 = bm1 / b; + am1 = a / b; + bm1 = 1.0; + oldf = f; + f = am1; + j += 1.0; + } + y = Math.exp(-x) * f; + } + return y; + } +} diff --git a/src/bilib/src/polyharmonicwavelets/CoeffProcessing.java b/src/bilib/src/polyharmonicwavelets/CoeffProcessing.java new file mode 100644 index 0000000..3229509 --- /dev/null +++ b/src/bilib/src/polyharmonicwavelets/CoeffProcessing.java @@ -0,0 +1,371 @@ +package polyharmonicwavelets; + +// +// CoeffProcessing.java +// PolyharmonicWavelets +// +// Created by Biomedical Imaging Group on 2/13/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +import java.util.*; +import ij.*; +import java.text.DecimalFormat; +import ij.text.*; + +/** + * This class is used to proccess the wavelet transform coefficients. + * + * @author Katarina Balac, EPFL. + */ + +final public class CoeffProcessing { + + /** + * Do your own processing. + * + * @param param + * the transform parameters + * @param transform + * the array of transform coefficients + */ + + static final public void doMyProcessing(Parameters param, ComplexImage[] transform) { + if (param.redundancy == param.PYRAMID) { + for (int j = 0; j < param.J; j++) { + // ................... + // process highpass transform[j] + // .................... + } + // ................... + // process lowpass transform[J] + // .................... + } + else { // nonredundant + if (param.lattice == param.DYADIC) { + int dx = transform[0].nx; + int dy = transform[0].ny; + dx /= 2; + dy /= 2; + for (int j = 0; j < param.J; j++) { + ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1); + // ................... + // process highpass subband + // .................... + transform[0].putSubimage(0, dy, subband); + subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1); + // ................... + // process highpass subband + // .................... + transform[0].putSubimage(dx, 0, subband); + subband = transform[0].getSubimage(dx, 2 * dx - 1, dy, 2 * dy - 1); + // ................... + // process highpass subband + // .................... + transform[0].putSubimage(dx, dy, subband); + dx /= 2; + dy /= 2; + } + ComplexImage subband = transform[0].getSubimage(0, dx - 1, 0, dy - 1); + // ................... + // process lowpass subband + // .................... + transform[0].putSubimage(0, 0, subband); + } + else { // quincunx + int dx = transform[0].nx; + int dy = transform[0].ny; + dx /= 2; + for (int j = 0; j < param.J; j++) { + if (j % 2 == 0) { + ComplexImage subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1); + // ................... + // process highpass subband + // .................... + transform[0].putSubimage(dx, 0, subband); + dy /= 2; + } + else { + dy /= 2; + ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1); + // ................... + // process highpass subband + // .................... + transform[0].putSubimage(0, dy, subband); + dx /= 2; + } + ComplexImage subband = transform[0].getSubimage(0, dx - 1, 0, dy - 1); + // ................... + // process lowpass subband + // .................... + transform[0].putSubimage(0, 0, subband); + } + } + } + } + + /** + * Implements hard threshold on transform coefficients. + * + * @param param + * the transform parameters + * @param transform + * the array of transform coefficients + * @param t + * the threshold + */ + + static final public void doHardThreshold(Parameters param, ComplexImage[] transform, double t) { + if (param.redundancy == param.PYRAMID) { + for (int j = 0; j < param.J; j++) { + transform[j].hardThreshold(t); + } + } + else { // nonredundant + if (param.lattice == param.DYADIC) { + int dx = transform[0].nx; + int dy = transform[0].ny; + dx /= 2; + dy /= 2; + for (int j = 0; j < param.J; j++) { + ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1); + subband.hardThreshold(t); + transform[0].putSubimage(0, dy, subband); + subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1); + subband.hardThreshold(t); + transform[0].putSubimage(dx, 0, subband); + subband = transform[0].getSubimage(dx, 2 * dx - 1, dy, 2 * dy - 1); + subband.hardThreshold(t); + transform[0].putSubimage(dx, dy, subband); + dx /= 2; + dy /= 2; + } + } + else { // quincunx + int dx = transform[0].nx; + int dy = transform[0].ny; + for (int j = 0; j < param.J; j++) { + if (j % 2 == 0) { + dx /= 2; + ComplexImage subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1); + subband.hardThreshold(t); + transform[0].putSubimage(dx, 0, subband); + } + else { + dy /= 2; + ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1); + subband.hardThreshold(t); + transform[0].putSubimage(0, dy, subband); + } + } + } + } + } + + /** + * Implements soft threshold on transform coefficients. + * + * @param param + * the transform parameters + * @param transform + * the array of transform coefficients + * @param t + * the threshold + */ + + static final public void doSoftThreshold(Parameters param, ComplexImage[] transform, double t) { + if (param.redundancy == param.PYRAMID) { + for (int j = 0; j < param.J; j++) { + transform[j].softThreshold(t); + } + } + else { // nonredundant + if (param.lattice == param.DYADIC) { + int dx = transform[0].nx; + int dy = transform[0].ny; + dx /= 2; + dy /= 2; + for (int j = 0; j < param.J; j++) { + ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1); + subband.softThreshold(t); + transform[0].putSubimage(0, dy, subband); + subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1); + subband.softThreshold(t); + transform[0].putSubimage(dx, 0, subband); + subband = transform[0].getSubimage(dx, 2 * dx - 1, dy, 2 * dy - 1); + subband.softThreshold(t); + transform[0].putSubimage(dx, dy, subband); + dx /= 2; + dy /= 2; + } + } + else { // quincunx + int dx = transform[0].nx; + int dy = transform[0].ny; + for (int j = 0; j < param.J; j++) { + if (j % 2 == 0) { + dx /= 2; + ComplexImage subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1); + subband.softThreshold(t); + transform[0].putSubimage(dx, 0, subband); + } + else { + dy /= 2; + ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1); + subband.softThreshold(t); + transform[0].putSubimage(0, dy, subband); + } + } + } + } + } + + /** + * Applies the soft threshold to each subband where threshold is computed + * from the image features. + * + * @param param + * the transform parameters + * @param transform + * the array of transform coefficients. + */ + + static final public void softThresholdAdaptive(Parameters param, ComplexImage[] transform) { // take + // care + // of + // redundant! + int J = param.J; + double[] scale = { 1.0, 0.6, 0.34, 0.26, 0.23, 0.21, 0.20, 0.20 }; + double[] threshold = new double[J]; + double coef = 1.0; + double c = 0.1 * coef; + ComplexImage firstsubband = transform[0].copyImage(); + int nx = firstsubband.nx; + int ny = firstsubband.ny; + if (param.redundancy == param.BASIS) { + if (param.lattice == param.DYADIC) { + firstsubband = firstsubband.getSubimage(nx / 2, nx - 1, ny / 2, ny - 1); + } + else { + firstsubband = firstsubband.getSubimage(nx / 2, nx - 1, 0, ny - 1); + } + } + threshold[0] = firstsubband.deviation() * c; + if (J > 8) { + for (int k = 1; k < 8; k++) { + threshold[k] = threshold[0] * scale[k]; + } + for (int k = 8; k < J; k++) { + threshold[k] = threshold[8]; + } + } + else { + for (int k = 1; k < J; k++) { + threshold[k] = threshold[0] * scale[k]; + } + } + if (param.redundancy == param.PYRAMID) { + for (int j = 0; j < param.J; j++) { + transform[j].softThreshold(threshold[j]); + } + } + else { // nonredundant + if (param.lattice == param.DYADIC) { + int dx = transform[0].nx; + int dy = transform[0].ny; + dx /= 2; + dy /= 2; + for (int j = 0; j < param.J; j++) { + ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1); + subband.softThreshold(threshold[j]); + transform[0].putSubimage(0, dy, subband); + subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1); + subband.softThreshold(threshold[j]); + transform[0].putSubimage(dx, 0, subband); + subband = transform[0].getSubimage(dx, 2 * dx - 1, dy, 2 * dy - 1); + subband.softThreshold(threshold[j]); + transform[0].putSubimage(dx, dy, subband); + dx /= 2; + dy /= 2; + } + } + else { // quincunx + int dx = transform[0].nx; + int dy = transform[0].ny; + for (int j = 0; j < param.J; j++) { + if (j % 2 == 0) { + dx /= 2; + ComplexImage subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1); + subband.softThreshold(threshold[j]); + transform[0].putSubimage(dx, 0, subband); + } + else { + dy /= 2; + ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1); + subband.softThreshold(threshold[j]); + transform[0].putSubimage(0, dy, subband); + } + } + } + } + } + + /** + * Multiplies the coefficients in all highpass subbands with the given + * enhancement parameter. + * + * @param param + * the transform parameters + * @param transform + * the array of transform coefficients + * @param t + * the enhancement parameter + */ + + static final public void doEnhancement(Parameters param, ComplexImage[] transform, double t) { + if (param.redundancy == param.PYRAMID) { + for (int j = 0; j < param.J; j++) { + transform[j].multiply(t); + } + } + else { // nonredundant + if (param.lattice == param.DYADIC) { + int dx = transform[0].nx; + int dy = transform[0].ny; + dx /= 2; + dy /= 2; + for (int j = 0; j < param.J; j++) { + ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1); + subband.multiply(t); + transform[0].putSubimage(0, dy, subband); + subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1); + subband.multiply(t); + transform[0].putSubimage(dx, 0, subband); + subband = transform[0].getSubimage(dx, 2 * dx - 1, dy, 2 * dy - 1); + subband.multiply(t); + transform[0].putSubimage(dx, dy, subband); + dx /= 2; + dy /= 2; + } + } + else { // quincunx + int dx = transform[0].nx; + int dy = transform[0].ny; + for (int j = 0; j < param.J; j++) { + if (j % 2 == 0) { + dx /= 2; + ComplexImage subband = transform[0].getSubimage(dx, 2 * dx - 1, 0, dy - 1); + subband.multiply(t); + transform[0].putSubimage(dx, 0, subband); + } + else { + dy /= 2; + ComplexImage subband = transform[0].getSubimage(0, dx - 1, dy, 2 * dy - 1); + subband.multiply(t); + transform[0].putSubimage(0, dy, subband); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/bilib/src/polyharmonicwavelets/ComplexImage.java b/src/bilib/src/polyharmonicwavelets/ComplexImage.java new file mode 100644 index 0000000..a33473b --- /dev/null +++ b/src/bilib/src/polyharmonicwavelets/ComplexImage.java @@ -0,0 +1,2706 @@ +// +// ComplexImage.java +// PolyharmonicWavelets +// +// Created by Biomedical Imaging Group on 2/13/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +package polyharmonicwavelets; + +import java.util.*; +import java.awt.*; +import java.awt.event.*; +import ij.process.*; +import ij.*; +import java.text.DecimalFormat; +import ij.plugin.filter.PlugInFilter; +import ij.text.*; + +/** + * A ComplexImage object is a two-dimensional array of complex numbers. + * + * @author Katarina Balac, EPFL. + */ + +public class ComplexImage { + + /** + * The real part of the ComplexImage object. + */ + public double real[]; + + /** + * The imaginary part of the ComplexImage object. + */ + public double imag[]; + + /** + * The number of columns in the ComplexImage object. + */ + public int nx; + + /** + * The number of rows in the ComplexImage object. + */ + public int ny; + + /** + * The total number of elements in the ComplexImage object. + */ + public int nxy; + + private final double PI2 = 2.0 * Math.PI; + private final double sqrt2 = Math.sqrt(2.0); + + /** + * Creates a ComplexImage object from an ImagePlus image. + * + * @param imp + * the ImagePlus image used to create the ComplexImage object + */ + + public ComplexImage(ImagePlus imp) { + nx = imp.getWidth(); + ny = imp.getHeight(); + nxy = nx * ny; + real = new double[nxy]; + imag = new double[nxy]; + if (imp.getType() == ImagePlus.GRAY8) { + byte[] pixels = (byte[]) imp.getProcessor().getPixels(); + for (int k = 0; k < nx * ny; k++) + real[k] = (double) (pixels[k] & 0x00FF); + } + if (imp.getType() == ImagePlus.GRAY16) { + short[] pixels = (short[]) imp.getProcessor().getPixels(); + for (int k = 0; k < nx * ny; k++) + real[k] = (double) (pixels[k]); + } + if (imp.getType() == ImagePlus.GRAY32) { + float[] pixels = (float[]) imp.getProcessor().getPixels(); + for (int k = 0; k < nx * ny; k++) + real[k] = (double) (pixels[k]); + } + } + + /** + * Creates a Compleximage object of a given size filed with zeros. + * + * @param sizex + * the number of columns in the ComplexImage + * @param sizey + * the number of rows in the ComplexImage + */ + + public ComplexImage(int sizex, int sizey) { + nx = sizex; + ny = sizey; + nxy = nx * ny; + real = new double[nxy]; + imag = new double[nxy]; + } + + /** + * Creates a Compleximage object of a given size filed with zeros, enables + * not to reserve space for the imaginary part. + * + * @param sizex + * the number of columns in the ComplexImage + * @param sizey + * the number of rows in the ComplexImage + * @param r + * if true the image is real, the imaginary part is set to null + */ + + public ComplexImage(int sizex, int sizey, boolean r) { + nx = sizex; + ny = sizey; + nxy = nx * ny; + real = new double[nxy]; + imag = null; + if (!r) { + imag = new double[nxy]; + } + } + + /** + * If necessary, this ComplexImage is cropped by keeping its central part so + * that the quincunx wavelet analysis can be performed. + * + * @param J + * number of decomposition levels + */ + + public void cropQuincunx(int J) { + int ind = 1; + int lx = nx; + int ly = ny; + for (int j = 0; j < J; j++) { + if (ind == 1) { + lx /= 2; + } + else { + ly /= 2; + } + ind = 1 - ind; + } + ind = 1 - ind; + for (int j = 0; j < J; j++) { + if (ind == 1) { + lx *= 2; + } + else { + ly *= 2; + } + ind = 1 - ind; + } + if (!((lx == nx) && (ly == ny))) { + System.out.println("Image has been cropped"); + int dx = (nx - lx) / 2; + int dy = (ny - ly) / 2; + ComplexImage temp = getSubimage(dx, lx - 1 + dx, dy, ly - 1 + dy); + nx = temp.nx; + ny = temp.ny; + nxy = temp.nxy; + System.arraycopy(temp.real, 0, real, 0, nxy); + System.arraycopy(temp.imag, 0, imag, 0, nxy); + showReal("Cropped image"); + } + } + + /** + * If necessary, this ComplexImage is cropped by keeping its central part so + * that the dyadic wavelet analysis can be performed. + * + * @param J + * number of decomposition levels + */ + + public void cropDyadic(int J) { + int lx = nx; + int ly = ny; + for (int j = 0; j < J; j++) { + lx /= 2; + ly /= 2; + } + + for (int j = 0; j < J; j++) { + lx *= 2; + ly *= 2; + } + if (!((lx == nx) && (ly == ny))) { + System.out.println("Image has been cropped"); + int dx = (nx - lx) / 2; + int dy = (ny - ly) / 2; + ComplexImage temp = getSubimage(dx, lx - 1 + dx, dy, ly - 1 + dy); + nx = temp.nx; + ny = temp.ny; + nxy = temp.nxy; + System.arraycopy(temp.real, 0, real, 0, nxy); + System.arraycopy(temp.imag, 0, imag, 0, nxy); + showReal("Cropped image"); + } + } + + /** + * Returns the maximum number of wavelet iterations for this ComplexImage. + * + * @param type + * the transform lattice, "Quincunx" or "Dyadic" + */ + + public int noIterations(String type) { + int noit = 0; + int dx = nx; + int dy = ny; + if (type == "Dyadic") { + while ((dx % 2 == 0) && (dy % 2 == 0)) { + noit++; + dx /= 2; + dy /= 2; + } + } + if (type == "Quincunx") { + while ((dx % 2 == 0) && (dy % 2 == 0)) { + noit += 2; + dx /= 2; + dy /= 2; + } + if (dx % 2 == 0) { + noit++; + } + } + return noit; + } + + /** + * Crops this ComplexImage to a given size by keeping its central part. + * + * @param lx + * number of columns in the cropped ComplexImage + * @param ly + * number of rows in the cropped ComplexImage + */ + + public void croptoSize(int lx, int ly) { + if (!((lx == nx) && (ly == ny))) { + int dx = (nx - lx) / 2; + int dy = (ny - ly) / 2; + ComplexImage temp = getSubimage(dx, lx - 1 + dx, dy, ly - 1 + dy); + nx = temp.nx; + ny = temp.ny; + nxy = temp.nxy; + System.arraycopy(temp.real, 0, real, 0, nxy); + System.arraycopy(temp.imag, 0, imag, 0, nxy); + } + } + + /** + * Fills the real part of this squared ComplexImage with the zoneplate + * image. + */ + + public void zone() { + int size = nx; + int size2 = size / 2; + double c = Math.PI / sqrt2 / (double) nx; + int ind = 0; + for (int y1 = -size2; y1 < size2; y1++) { + for (int x1 = -size2; x1 < size2; x1++) { + real[ind++] = (1.0 + Math.cos(c * (double) (y1 * y1 + x1 * x1))) * 127.5; + } + } + } + + /** + * Fills the real part of this ComplexImage with random values of Gaussian + * distribution N(0,1). + */ + + public void makeNoise() { + Random random = new Random(); + for (int k = 0; k < nxy; k++) { + real[k] = random.nextGaussian(); + } + } + + /** + * Fills the real part of this ComplexImage with the Gaussian shaped spots. + */ + + public void makeGaussianSpots() { + Random random = new Random(); + int N = random.nextInt(12) + 9; // Number of spots, between 10 and 20 + // int N=1; + int[] X0 = new int[N]; + int[] Y0 = new int[N]; + double[] sigma = new double[N]; + for (int k = 0; k < N; k++) { + X0[k] = random.nextInt(nx - 60) + 30; + Y0[k] = random.nextInt(ny - 60) + 30; + sigma[k] = random.nextDouble() * 5.0 + 1.0; // sigma between 1 and 6 + } + for (int k = 0; k < N; k++) { + double s2 = 2.0 * sigma[k] * sigma[k]; + // double s1=1.0/(s2*Math.PI); + double s1 = 1.0 / (2.0 * Math.PI); + for (int y = -30; y < 30; y++) { + int dy = nx * y; + for (int x = -30; x < 30; x++) { + int p0 = Y0[k] * nx + X0[k]; + double s = s1 * Math.exp(-(double) (x * x + y * y) / s2); + real[p0 + dy + x] += s; + } + } + } + } + + /** + * Displays the real part of this ComplexImage on the screen. + * + * @param text + * name of the image to display + */ + + public void showReal(String text) { + float re[][] = new float[nx][ny]; + int index; + for (int k = 0; k < nx; k++) + for (int l = 0; l < ny; l++) { + index = l * nx + k; + re[k][l] = (float) real[index]; + } + FloatProcessor fp = new FloatProcessor(re); + ImagePlus imp = new ImagePlus(text, fp); + imp.show(); + } + + /** + * Displays the imaginary part of this ComplexImage on the screen. + * + * @param text + * name of the image to display + */ + + public void showImag(String text) { + float imaginary[][] = new float[nx][ny]; + int index; + for (int k = 0; k < nx; k++) + for (int l = 0; l < ny; l++) { + index = l * nx + k; + imaginary[k][l] = (float) imag[index]; + } + FloatProcessor fp = new FloatProcessor(imaginary); + ImagePlus imp = new ImagePlus(text, fp); + imp.show(); + } + + /** + * Displays the modulus of this ComplexImage on the screen. + * + * @param text + * name of the image to display + */ + + public void showModulus(String text) { + float mod[][] = new float[nx][ny]; + int index; + for (int k = 0; k < nx; k++) + if (imag == null) { + for (int l = 0; l < ny; l++) { + index = l * nx + k; + mod[k][l] = (float) Math.abs(real[index]); + } + } + else { + for (int l = 0; l < ny; l++) { + index = l * nx + k; + mod[k][l] = (float) Math.sqrt(real[index] * real[index] + imag[index] * imag[index]); + } + } + FloatProcessor fp = new FloatProcessor(mod); + ImagePlus imp = new ImagePlus(text, fp); + imp.show(); + } + + /** + * Displays the real and the imaginary part of this ComplexImage as a stack. + * + * @param text + * name of the stack to be displayed + */ + + public void displayComplexStack(String text) { + double color = -255.0; + ComplexImage[] out = new ComplexImage[2]; + ImageStack stack = new ImageStack(nx, ny); + float ima[][] = new float[nx][ny]; + for (int k = 0; k < nx; k++) { + for (int l = 0; l < ny; l++) { + int index = l * nx + k; + ima[k][l] = (float) real[index]; + } + } + FloatProcessor fp = new FloatProcessor(ima); + fp.setValue(color); + stack.addSlice("Real part", fp); + for (int k = 0; k < nx; k++) { + for (int l = 0; l < ny; l++) { + int index = l * nx + k; + ima[k][l] = (float) imag[index]; + } + } + fp = new FloatProcessor(ima); + fp.setValue(color); + stack.addSlice("Imaginary part", fp); + ImagePlus imp = new ImagePlus(text, stack); + imp.show(); + } + + /** + * Displays the real parts of ComplexImages in an array as a stack. + * + * @param array + * the aray of images to be displayed + * @param text + * name of the stack to be displayed + */ + + public static void displayStack(ComplexImage[] array, String text) { + int nx = array[0].nx; + int ny = array[0].ny; + ImageStack stack = new ImageStack(nx, ny); + float ima[][] = new float[nx][ny]; + for (int j = 0; j < array.length; j++) { + float im[][] = new float[nx][ny]; + int index; + for (int k = 0; k < nx; k++) { + for (int l = 0; l < ny; l++) { + index = l * nx + k; + ima[k][l] = (float) array[j].real[index]; + } + } + FloatProcessor fp = new FloatProcessor(ima); + stack.addSlice("j", fp); + } + ImagePlus imp = new ImagePlus(text, stack); + imp.show(); + } + + /** + * Displays the imaginary parts of ComplexImages in an array as a stack. + * + * @param array + * the aray of images to be displayed + * @param text + * name of the stack to be displayed + */ + + public static void displayStackImag(ComplexImage[] array, String text) { + int nx = array[0].nx; + int ny = array[0].ny; + ImageStack stack = new ImageStack(nx, ny); + float ima[][] = new float[nx][ny]; + for (int j = 0; j < array.length; j++) { + float im[][] = new float[nx][ny]; + int index; + for (int k = 0; k < nx; k++) + for (int l = 0; l < ny; l++) { + index = l * nx + k; + ima[k][l] = (float) array[j].imag[index]; + } + FloatProcessor fp = new FloatProcessor(ima); + stack.addSlice("j", fp); + } + ImagePlus imp = new ImagePlus(text, stack); + imp.show(); + } + + /** + * Displays this ComplexImage values on the screen. + * + * @param text + * name of the image to be displayed + */ + + public void displayValues(String text) { + DecimalFormat decimalFormat = new DecimalFormat(); + decimalFormat.applyPattern("0.0000"); + System.out.println(" "); + System.out.println(text); + System.out.println("sizenxnx=" + nx); + System.out.println("sizeny=" + ny); + for (int i = 0; i < ny; i++) { + for (int j = 0; j < nx; j++) { + System.out.print(" " + decimalFormat.format(real[i * nx + j]) + "+" + decimalFormat.format(imag[i * nx + j]) + "i"); + } + System.out.println(" "); + } + } + + /** + * Sets both the real and the imaginary part of this ComplexImage object to + * zero. + */ + + public void putZeros() { + if (imag == null) { + for (int i = 0; i < nxy; i++) { + real[i] = 0.0; + } + } + else { + for (int i = 0; i < nxy; i++) { + real[i] = imag[i] = 0.0; + } + } + } + + /** + * Sets the real part of this ComplexImage object to zero. + */ + + public void setRealtoZero() { + for (int k = 0; k < nxy; k++) { + real[k] = 0.0; + } + } + + /** + * Sets the imaginary part of this ComplexImage object to zero. + */ + + public void setImagtoZero() { + for (int k = 0; k < nxy; k++) { + imag[k] = 0.0; + } + } + + /** + * Sets all the values in this ComplexImage to a given constant. + * + * @param r + * - the real part of the ComplexImage to be set + * @param i + * - the imaginary part of the ComplexImage to be set + */ + + public void settoConstant(double r, double i) { + for (int k = 0; k < nxy; k++) { + real[k] = r; + imag[k] = i; + } + } + + /** + * Sets the real part of this ComplexImage to a given constant. + * + * @param r + * - the value to be set + */ + + public void settoConstant(double r) { + for (int k = 0; k < nxy; k++) { + real[k] = r; + } + } + + /** + * Creates a new ComplexImage object containing the chosen row of this + * ComplexImage. + * + * @param y + * the number of row to be copied + * @return ComplexImage containing the chosen row + */ + + public ComplexImage getRow(int y) { + ComplexImage row = new ComplexImage(nx, 1, imag == null); + System.arraycopy(real, y * nx, row.real, 0, nx); + if (!(imag == null)) { + System.arraycopy(imag, y * nx, row.imag, 0, nx); + } + return row; + } + + /** + * Copies the content of the chosen row of image to this ComplexImage. + * + * @param image + * the ComplexImage to copy from + * @param y + * the number of row to be copied + */ + + public void getRowContent(int y, ComplexImage image) { + nx = image.nx; + ny = 1; + int s = y * nx; + System.arraycopy(image.real, s, real, 0, image.nx); + if ((!(imag == null)) && (!(image.imag == null))) { + System.arraycopy(image.imag, s, imag, 0, image.nx); + } + } + + /** + * Copies an 1D array row to y-th row of this ComplexImage. + * + * @param y + * number of row to modify + * @param row + * ComplexImage with one row containing the row to be placed + */ + + public void putRow(int y, ComplexImage row) { + System.arraycopy(row.real, 0, real, y * nx, nx); + if ((!(imag == null)) && (!(row.imag == null))) { + System.arraycopy(row.imag, 0, imag, y * nx, nx); + } + } + + /** + * Creates a new ComplexImage object containing the chosen column of this + * ComplexImage. + * + * @param x + * number of column to be copied + * @return ComplexImage containing the chosen column of this ComplexImage + */ + + public ComplexImage getColumn(int x) { + ComplexImage column = new ComplexImage(ny, 1, imag == null); + if (imag == null) { + for (int y = 0, n = x; y < ny; y++) { + column.real[y] = real[n]; + n += nx; + } + } + else { + for (int y = 0, n = x; y < ny; y++) { + column.real[y] = real[n]; + column.imag[y] = imag[n]; + n += nx; + } + } + return column; + } + + /** + * Copies the content of the chosen column of image to this ComplexImage. + * + * @param image + * the ComplexImage to copy from + * @param x + * the number of column to be copied + */ + + public void getColumnContent(int x, ComplexImage image) { + nx = 1; + ny = image.ny; + if ((image.imag == null) || (imag == null)) { + for (int y = 0, n = x; y < image.ny; y++) { + real[y] = image.real[n]; + n += image.nx; + } + } + else { + for (int y = 0, n = x; y < image.ny; y++) { + real[y] = image.real[n]; + imag[y] = image.imag[n]; + n += image.nx; + } + } + } + + /** + * Copies an 1D array column to x-th column of this ComplexImage. + * + * @param x + * number of column to modify + * @param column + * ComplexImage with one column containing the column to be + * placed + */ + + public void putColumn(int x, ComplexImage column) { + if ((!(imag == null)) && (!(column.imag == null))) { + for (int y = 0, n = x; y < ny; y++) { + real[n] = column.real[y]; + imag[n] = column.imag[y]; + n += nx; + } + } + else { + for (int y = 0, n = x; y < ny; y++) { + real[n] = column.real[y]; + n += nx; + } + } + } + + /** + * Creates a new ComplexImage object containing the subimage of this + * ComplexImage. + * + * @param x1 + * starting x coordinate within this ComplexImage + * @param x2 + * end x coordinate within this ComplexImage + * @param y1 + * starting y coordinate within this ComplexImage + * @param y2 + * end y coordinate within this ComplexImage + * @return the subimage + */ + + public ComplexImage getSubimage(int x1, int x2, int y1, int y2) { + ComplexImage sub = new ComplexImage(x2 - x1 + 1, y2 - y1 + 1, imag == null); + int d = x1 + y1 * nx; + if (imag == null) { + for (int y = 0; y < sub.ny; y++) { + System.arraycopy(real, d + y * nx, sub.real, y * sub.nx, sub.nx); + } + + } + else { + for (int y = 0; y < sub.ny; y++) { + System.arraycopy(real, d + y * nx, sub.real, y * sub.nx, sub.nx); + System.arraycopy(imag, d + y * nx, sub.imag, y * sub.nx, sub.nx); + } + } + return (sub); + } + + /** + * Copies the content of a subimage of image to this ComplexImage. Gives the + * same result as getSubimage, but does not create a new object. Total + * number of pixels assigned to this ComplexImage when created has to be + * sufficient for the subimage. + * + * @param x1 + * starting x coordinate within image + * @param x2 + * end x coordinate within image + * @param y1 + * starting y coordinate within image + * @param y2 + * end y coordinate within image + * @param image + * the image to copy + */ + + public void getSubimageContent(int x1, int x2, int y1, int y2, ComplexImage image) { + int d = x1 + y1 * image.nx; + nx = x2 - x1 + 1; + ny = y2 - y1 + 1; + nxy = nx * ny; + if ((imag == null) || (image.imag == null)) { + for (int y = 0; y < ny; y++) { + System.arraycopy(image.real, d + y * image.nx, real, y * nx, nx); + } + } + else { + for (int y = 0; y < ny; y++) { + System.arraycopy(image.real, d + y * image.nx, real, y * nx, nx); + System.arraycopy(image.imag, d + y * image.nx, imag, y * nx, nx); + } + } + } + + /** + * Copies the ComplexImage sub to the position in this ComplexImage given by + * its upper left corner. + * + * @param sub + * the image to copy + * @param x1 + * starting x coordinate + * @param y1 + * starting y coordinate + */ + + public void putSubimage(int x1, int y1, ComplexImage sub) { + int k; + int k1; + int d = x1 + y1 * nx; + if (sub.imag == null) { + for (int y = 0; y < sub.ny; y++) { + System.arraycopy(sub.real, y * sub.nx, real, d + y * nx, sub.nx); + } + } + else { + for (int y = 0; y < sub.ny; y++) { + System.arraycopy(sub.real, y * sub.nx, real, d + y * nx, sub.nx); + System.arraycopy(sub.imag, y * sub.nx, imag, d + y * nx, sub.nx); + } + } + } + + /** + * Creates a ComplexImage object as a copy of this ComplexImage. + * + * @return the copy of this ComplexImage + */ + + public ComplexImage copyImage() { + ComplexImage temp = null; + if (imag == null) { + temp = new ComplexImage(nx, ny, true); + System.arraycopy(real, 0, temp.real, 0, nxy); + } + else { + temp = new ComplexImage(nx, ny); + System.arraycopy(real, 0, temp.real, 0, nxy); + System.arraycopy(imag, 0, temp.imag, 0, nxy); + } + return temp; + } + + /** + * Copies the content of ComplexImage original to this ComplexImage. Gives + * the same result as copyImage, but does not create a new object. Total + * number of pixels assigned to this ComplexImage when created has to be + * sufficient for the original image. + * + * @param original + * the ComplexImage to copy + */ + + public void copyImageContent(ComplexImage original) { + nx = original.nx; + ny = original.ny; + nxy = original.nxy; + if ((original.imag == null) || (imag == null)) { + System.arraycopy(original.real, 0, real, 0, nxy); + } + else { + System.arraycopy(original.real, 0, real, 0, nxy); + System.arraycopy(original.imag, 0, imag, 0, nxy); + } + } + + /** + * Computes a square modulus of this ComplexImage. Puts a square modulus in + * the real part of this ComplexImage and zeros in the imaginary part if it + * exists. + */ + + public void squareModulus() { + if (imag == null) { + for (int i = 0; i < nxy; i++) { + real[i] = real[i] * real[i]; + } + } + else { + for (int i = 0; i < nxy; i++) { + real[i] = real[i] * real[i] + imag[i] * imag[i]; + imag[i] = 0.0; + } + } + } + + /** + * Computes a modulus of this ComplexImage. Puts a modulus in the real part + * of this ComplexImage and zeros in the imaginary part. + */ + + public void modulus() { + for (int k = 0; k < nxy; k++) { + real[k] = Math.sqrt(real[k] * real[k] + imag[k] * imag[k]); + imag[k] = 0.0; + } + } + + /** + * Computes the phase of this ComplexImage. Puts a phase in the real part of + * this ComplexImage and zeros in the imaginary part. + */ + + public void phase() { + for (int k = 0; k < nxy; k++) { + real[k] = Math.atan(imag[k] / real[k]); + if (real[k] < 0.0) { + real[k] += Math.PI; + } + imag[k] = 0.0; + } + } + + /** + * Adds a ComplexImage object of the same dimensions to this ComplexImage. + * + * @param im + * the ComplexImage to add + */ + + public void add(ComplexImage im) { + if (!(im.imag == null)) { // im is complex + if (imag == null) { + imag = new double[nxy]; + } + for (int k = 0; k < nxy; k++) { + real[k] += im.real[k]; + imag[k] += im.imag[k]; + } + } + else { // im is real + for (int k = 0; k < nxy; k++) { + real[k] += im.real[k]; + } + } + } + + /** + * Adds a real constant to every element in this ComplexImage. + * + * @param d + * the constant to add + */ + + public void add(double d) { + for (int k = 0; k < nxy; k++) { + real[k] += d; + } + } + + /** + * Substracts a ComplexImage of the same dimensions from this ComplexImage. + * + * @param im + * the ComplexImage to be substracted + */ + + public void subtract(ComplexImage im) { + if (!(im.imag == null)) { // im is complex + if (imag == null) { + imag = new double[nxy]; + } + for (int k = 0; k < nxy; k++) { + real[k] -= im.real[k]; + imag[k] -= im.imag[k]; + } + } + else { // im is real + for (int k = 0; k < nxy; k++) { + real[k] -= im.real[k]; + } + } + } + + /** + * Performs the complex conjugate on this ComplexImage. + */ + + public void conj() { + if (!(imag == null)) { + for (int k = 0; k < nxy; k++) { + imag[k] = -imag[k]; + } + } + } + + /** + * Multiplies each element in this ComplexImage with a given constant. + * + * @param constant + * the multiplication constant + */ + + public void multiply(double constant) { + if (imag == null) { + for (int k = 0; k < nxy; k++) { + real[k] *= constant; + } + } + else { + for (int k = 0; k < nxy; k++) { + real[k] *= constant; + imag[k] *= constant; + } + } + } + + /** + * Multiplies this complex image by a complex constant re+i*im. + * + * @param re + * real part of the complex constant + * @param im + * imaginary part of the complex constant + */ + + public void multiply(double re, double im) { + for (int k = 0; k < nxy; k++) { + double r = real[k]; + real[k] = re * real[k] - im * imag[k]; + imag[k] = re * imag[k] + im * r; + } + } + + /** + * Multiplies this ComplexImage with another ComplexImage of same + * dimensions, pointwise. + * + * @param im + * the ComplexImage to multiply with + */ + + public void multiply(ComplexImage im) { + if (!(im.imag == null)) { // im is complex + if (imag == null) { + imag = new double[nxy]; + } + for (int k = 0; k < nxy; k++) { + double re = real[k]; + real[k] = im.real[k] * re - im.imag[k] * imag[k]; + imag[k] = im.real[k] * imag[k] + im.imag[k] * re; + } + } + else { // im is real + if (imag == null) { + for (int k = 0; k < nxy; k++) { + real[k] *= im.real[k]; + } + } + else { + for (int k = 0; k < nxy; k++) { + real[k] *= im.real[k]; + imag[k] *= im.real[k]; + } + } + } + } + + /** + * Multiplies this ComplexImage by ComplexImage obtained by taking every + * l-th sample of circulary extended ComplexImage im. + * + * @param im + * the ComplexImage to multiply with + * @param l + * subsampling factor for im + */ + + public void multiplyCircular(ComplexImage im, int l) { + int l2 = l * l; + int k; + int t; + double ima; + double re; + int l2y; + if (!(im.imag == null)) { // im is complex + if (imag == null) { + imag = new double[nxy]; + } + for (int y = 0; y < ny; y++) { + l2y = ((l * y) % im.ny) * im.nx; + for (int x = 0; x < nx; x++) { // x,y-coordinates in the image + k = nx * y + x; + t = l2y + ((l * x) % im.nx); + re = real[k]; + ima = imag[k]; + real[k] = im.real[t] * re - im.imag[t] * ima; + imag[k] = im.real[t] * ima + im.imag[t] * re; + } + } + } + else { // im is real + if (imag == null) { + for (int y = 0; y < ny; y++) { + l2y = ((l * y) % im.ny) * im.nx; + for (int x = 0; x < nx; x++) { // x,y-coordinates in the + // image + k = nx * y + x; + t = l2y + ((l * x) % im.nx); + real[k] *= im.real[t]; + } + } + } + else { + for (int y = 0; y < ny; y++) { + l2y = ((l * y) % im.ny) * im.nx; + for (int x = 0; x < nx; x++) { // x,y-coordinates in the + // image + k = nx * y + x; + t = l2y + ((l * x) % im.nx); + re = real[k]; + ima = imag[k]; + real[k] *= im.real[t]; + imag[k] *= im.real[t]; + } + } + } + } + } + + /** + * Multiplies this ComplexImage pointwise with ComplexImage obtained by + * taking every l-th sample of another ComplexImage im along each dimension. + * Dimensions of im have to be bigger or equal to dimensions of the + * ComplexImage multiplied by l. + * + * @param im + * the ComplexImage to multiply with + * @param l + * subsampling factor for im + */ + + public void multiply(ComplexImage im, int l) { + if (l == 1) { + multiply(im); + } + else { + int l2 = l * l; + double ima; + double re; + int d = l * im.nx; + if (!(im.imag == null)) { // im is complex + if (imag == null) { + imag = new double[nxy]; + } + for (int y = 0, l2y = 0; y < ny; y++, l2y += d) { + for (int t = l2y, k = nx * y, end = nx * y + nx; k < end; t += l, k++) { // x,y-coordinates + // in + // the + // image + re = real[k]; + real[k] = im.real[t] * re - im.imag[t] * imag[k]; + imag[k] = im.real[t] * imag[k] + im.imag[t] * re; + } + } + } + else { // im is real + if (imag == null) { + for (int y = 0, l2y = 0; y < ny; y++, l2y += d) { + for (int t = l2y, k = nx * y, end = nx * y + nx; k < end; t += l, k++) { // x,y-coordinates + // in + // the + // image + real[k] *= im.real[t]; + } + } + } + else { + for (int y = 0, l2y = 0; y < ny; y++, l2y += d) { + for (int t = l2y, k = nx * y, end = nx * y + nx; k < end; t += l, k++) { // x,y-coordinates + // in + // the + // image + real[k] *= im.real[t]; + imag[k] *= im.real[t]; + } + } + } + } + } + } + + /** + * Divides this ComplexImage by another ComplexImage im of same dimensions, + * pointwise. Where both real and imaginary part of im are smaller then + * 10^-30 result is given by cr+i*cim. + * + * @param im + * the ComplexImage to divide by + * @param cr + * the real part of the result if division by zero + * @param cim + * the imaginary part of the result if division by zero + */ + + public void divide(ComplexImage im, double cr, double cim) { + double ima; + double rea; + double eps = 1E-30; + if (!(im.imag == null)) { // im is complex + if (imag == null) { + imag = new double[nxy]; + } + for (int k = 0; k < nxy; k++) { + if ((Math.abs(im.real[k]) < eps) && (Math.abs(im.imag[k]) < eps)) { + real[k] = cr; + imag[k] = cim; + } + else { + rea = real[k]; + ima = imag[k]; + real[k] = (rea * im.real[k] + ima * im.imag[k]) / (im.real[k] * im.real[k] + im.imag[k] * im.imag[k]); + imag[k] = (ima * im.real[k] - rea * im.imag[k]) / (im.real[k] * im.real[k] + im.imag[k] * im.imag[k]); + } + } + } + else { // im is real + if (imag == null) { // divide real by real + for (int k = 0; k < nxy; k++) { + if ((Math.abs(im.real[k]) < eps)) { + real[k] = cr; + } + else { + real[k] /= im.real[k]; + } + } + } + else { // divide complex by real + for (int k = 0; k < nxy; k++) { + if ((Math.abs(im.real[k]) < eps)) { + real[k] = cr; + imag[k] = cim; + } + else { + real[k] /= im.real[k]; + imag[k] /= im.real[k]; + } + } + } + } + } + + /** + * Divides this ComplexImage by another ComplexImage im of the same + * dimensions, pointwise. Does not check for division by zero. + * + * @param im + * the ComplexImage to divide by + */ + + public void divide(ComplexImage im) { + if (!(im.imag == null)) { // im is complex + if (imag == null) { + imag = new double[nxy]; + } + for (int k = 0; k < nxy; k++) { + double rea = real[k]; + double d = (im.real[k] * im.real[k] + im.imag[k] * im.imag[k]); + real[k] = (rea * im.real[k] + imag[k] * im.imag[k]) / d; + imag[k] = (imag[k] * im.real[k] - rea * im.imag[k]) / d; + } + } + else { // im is real + if (imag == null) { // divide real by real + for (int k = 0; k < nxy; k++) { + real[k] /= im.real[k]; + } + } + else { // divide complex by real + for (int k = 0; k < nxy; k++) { + real[k] /= im.real[k]; + imag[k] /= im.real[k]; + } + } + } + } + + /** + * Computes the square root of each element of the real part of this + * ComplexImage and puts it in the real part of this ComplexImage. + */ + + public void rootReal() { + for (int k = 0; k < nxy; k++) { + real[k] = Math.sqrt(real[k]); + } + } + + /** + * Returns the median of the real part of this ComplexImage. + * + * @return median of the real part of this ComplexImage + */ + + public double median() { + ComplexImage temp = copyImage(); + java.util.Arrays.sort(temp.real); + double m = temp.real[nxy / 2]; + return m; + } + + /** + * Returns the median absolute deviation of the real part of this + * ComplexImage. + * + * @return median absolute deviation of the real part of this ComplexImage + */ + + public double mad() { + double m = median(); + ComplexImage temp = copyImage(); + temp.add(-m); + for (int x = 0; x < nxy; x++) { + temp.real[x] = Math.abs(temp.real[x]); + } + double md = temp.median(); + return md; + } + + /** + * Rises each element of the real part of this ComplexImage to the power of + * exp. + * + * @param exp + * exponent + */ + + public void powerReal(double exp) { + for (int i = 0; i < nxy; i++) { + real[i] = Math.pow(real[i], exp); + } + } + + /** + * Returns the mean value of the real part of this ComplexImage. + * + * @return the mean of the real part of this ComplexImage + */ + + public double meanReal() { + double d = 0.0; + for (int k = 0; k < nxy; k++) { + d += real[k]; + } + d /= nxy; + return d; + } + + /** + * Returns the mean of the real part of this ComplexImage inside the mask + * which is defined by real(ma)=val. + * + * @param ma + * mask + * @param val + * defines the mask foreground + * @return the mean value of real part of this ComplexImage inside the mask + */ + + public double meanMask(int[] ma, int val) { + double cnt = 0; + double s = 0; + for (int k = 0; k < nxy; k++) { + if (ma[k] == val) { + cnt += 1.0; + s += real[k]; + } + } + s /= cnt; + return s; + } + + /** + * Returns the mean value of this ComplexImage modulus. + * + * @return mean of this ComplexImage modulus + */ + + public double meanModulus() { + double d = 0.0; + if (imag == null) { + for (int k = 0; k < nxy; k++) { + d += Math.abs(real[k]); + } + } + else { + for (int k = 0; k < nxy; k++) { + d += Math.pow(real[k] * real[k] + imag[k] * imag[k], 0.5); + } + } + d /= (nxy); + return d; + } + + /** + * Computes the sum of the real part of this ComplexImage. + * + * @return the sum of the real part of this ComplexImage + */ + + public double sumReal() { + double d = 0.0; + for (int k = 0; k < nxy; k++) { + d += real[k]; + } + return d; + } + + /** + * Returns the maximum absolute value of the real part of this ComplexImage. + * + * @return the maximum absolute value of the real part of this ComplexImage + */ + + public double maxAbsReal() { + double max = real[0]; + double min = real[0]; + for (int k = 0; k < nxy; k++) { + if (real[k] > max) { + max = real[k]; + } + else { + if (real[k] < min) { + min = real[k]; + } + } + } + max = Math.abs(max); + min = Math.abs(min); + if (min > max) { + max = min; + } + return max; + } + + /** + * Returns the maximum value of the real part of this ComplexImage. + * + * @return the maximum value of the real part of this ComplexImage + */ + + public double max() { + double m = real[0]; + for (int k = 0; k < nxy; k++) { + if (real[k] > m) { + m = real[k]; + } + } + return m; + } + + /** + * Returns the minimum value of the real part of this ComplexImage. + * + * @return the minimum value of the real part of this ComplexImage + */ + + public double min() { + double m = real[0]; + for (int k = 0; k < nxy; k++) { + if (real[k] < m) { + m = real[k]; + } + } + return m; + } + + /** + * Returns the standard deviation of the real part of this ComplexImage. + * + * @return the standard deviation of the real part of this ComplexImage + */ + + public double deviation() { + double m = meanReal(); + double s = 0; + for (int k = 0; k < nxy; k++) { + double d = real[k] - m; + s += (d * d); + } + s /= nxy; + s = Math.sqrt(s); + return s; + } + + /** + * Performs the 2D Fast Fourier Transform on this ComplexImage. + */ + + public void FFT2D() { + // perform FFT1D for each row + FFT1D FFTrow = new FFT1D(nx); + ComplexImage row = new ComplexImage(nx, 1); + for (int y = 0; y < ny; y++) { + row.getRowContent(y, this); + FFTrow.transform(row.real, row.imag, nx, 0); + putRow(y, row); + } + // perform FFT1D for each column + ComplexImage column = new ComplexImage(1, ny); + FFT1D FFTcolumn = new FFT1D(ny); + for (int x = 0; x < nx; x++) { + column.getColumnContent(x, this); + FFTcolumn.transform(column.real, column.imag, ny, 0); + putColumn(x, column); + } + } + + /** + * Performs the 2D inverse Fast Fourier Transform on this ComplexImage. + */ + + public void iFFT2D() { + // perform iFFT1D for each row + ComplexImage row = new ComplexImage(nx, 1); + FFT1D FFTrow = new FFT1D(nx); + for (int y = 0; y < ny; y++) { + row.getRowContent(y, this); + FFTrow.inverse(row.real, row.imag, nx, 0); + putRow(y, row); + } + // perform iFFT1D for each column + ComplexImage column = new ComplexImage(1, ny); + FFT1D FFTcolumn = new FFT1D(ny); + for (int x = 0; x < nx; x++) { + column.getColumnContent(x, this); + FFTcolumn.inverse(column.real, column.imag, ny, 0); + putColumn(x, column); + } + } + + /** + * Exchanges quadrants of this ComplexImage 1 <-> 3, 2 <-> 4. The quadrants + * are defined as follows: <br> + * 1 | 2 <br> + * ----- <br> + * 4 | 3 <br> + */ + + public void shift() { + double p; + int q1; + int q2; + int q3; + int q4; + int nxy2 = nxy / 2; + int nx2 = nx / 2; + if (imag == null) { + for (int y = 0; y < ny / 2; y++) { + int ynx = nx * y; + for (int x = 0; x < nx2; x++) { + q1 = ynx + x; + q2 = q1 + nx2; + q4 = q1 + nxy2; + q3 = q4 + nx2; + p = real[q1]; + real[q1] = real[q3]; + real[q3] = p; + p = real[q2]; + real[q2] = real[q4]; + real[q4] = p; + } + } + } + else { + for (int y = 0; y < ny / 2; y++) { + int ynx = nx * y; + for (int x = 0; x < nx2; x++) { + q1 = ynx + x; + q2 = q1 + nx2; + q4 = q1 + nxy2; + q3 = q4 + nx2; + p = real[q1]; + real[q1] = real[q3]; + real[q3] = p; + p = real[q2]; + real[q2] = real[q4]; + real[q4] = p; + p = imag[q1]; + imag[q1] = imag[q3]; + imag[q3] = p; + p = imag[q2]; + imag[q2] = imag[q4]; + imag[q4] = p; + } + } + } + } + + /** + * Exchanges quadrants of this ComplexImage 1 <-> 2, 3 <-> 4. The quadrants + * are defined as follows: <br> + * 1 | 2 <br> + * ----- <br> + * 4 | 3 <br> + */ + + public void shiftX() { + double p; + int q1; + int q2; + int nx2 = nx / 2; + if (imag == null) { + for (int y = 0; y < ny; y++) { + int ynx = nx * y; + for (int x = 0; x < nx2; x++) { + q1 = ynx + x; + q2 = q1 + nx2; + p = real[q1]; + real[q1] = real[q2]; + real[q2] = p; + } + } + } + else { + for (int y = 0; y < ny; y++) { + int ynx = nx * y; + for (int x = 0; x < nx2; x++) { + q1 = ynx + x; + q2 = q1 + nx2; + p = real[q1]; + real[q1] = real[q2]; + real[q2] = p; + p = imag[q1]; + imag[q1] = imag[q2]; + imag[q2] = p; + } + } + } + } + + /** + * Exchanges quadrants of this ComplexImage 1 <-> 4, 2 <-> 3. The quadrants + * are defined as follows: <br> + * 1 | 2 <br> + * ----- <br> + * 4 | 3 <br> + */ + + public void shiftY() { + final int halfim = nxy / 2; + double p; + if (imag == null) { + for (int k = 0; k < halfim; k++) { + p = real[k]; + real[k] = real[k + halfim]; + real[k + halfim] = p; + } + } + else { + for (int k = 0; k < halfim; k++) { + p = real[k]; + real[k] = real[k + halfim]; + real[k + halfim] = p; + p = imag[k]; + imag[k] = imag[k + halfim]; + imag[k + halfim] = p; + } + } + } + + /** + * Shifts this ComplexImage right by shiftx and down by shifty. + * + * @param shiftx + * shift to the right + * @param shifty + * shift down + */ + + public ComplexImage circShift(int shiftx, int shifty) { + while (shiftx < 0) { + shiftx = nx + shiftx; + } + while (shifty < 0) { + shifty = ny + shifty; + } + while (shiftx > (nx - 1)) { + shiftx = shiftx - nx; + } + while (shifty > (ny - 1)) { + shifty = shifty - ny; + } + ComplexImage res = new ComplexImage(nx, ny); + ComplexImage temp = getSubimage(0, nx - shiftx - 1, 0, ny - shifty - 1); + res.putSubimage(shiftx, shifty, temp); + temp = getSubimage(nx - shiftx, nx - 1, ny - shifty, ny - 1); + res.putSubimage(0, 0, temp); + temp = getSubimage(0, nx - shiftx - 1, ny - shifty, ny - 1); + res.putSubimage(shiftx, 0, temp); + temp = getSubimage(nx - shiftx, nx - 1, 0, ny - shifty - 1); + res.putSubimage(0, shifty, temp); + return res; + } + + /** + * Performs quincunx downsampling followed by upsampling in Fourier domain + * on this ComplexImage. Sums the first quadrant with the third and the + * second with the forth. <br> + * 1 | 2 <br> + * ----- <br> + * 4 | 3 <br> + */ + + public void quincunxDownUp() { + int nx2 = nx / 2; + int ny2 = ny / 2; + int nxy2 = nxy / 2; + for (int y = 0; y < nxy2; y += nx) { + for (int q1 = y, end = q1 + nx2; q1 < end; q1++) { + int q2 = q1 + nx2; + int q4 = q1 + nxy2; + int q3 = q4 + nx2; + double r = real[q1] + real[q3]; + double im = imag[q1] + imag[q3]; + real[q1] = real[q3] = r; + imag[q1] = imag[q3] = im; + r = real[q2] + real[q4]; + im = imag[q2] + imag[q4]; + real[q2] = real[q4] = r; + imag[q2] = imag[q4] = im; + } + } + } + + /** + * Down and upsampling in Y direction Sums the first quadrant with the forth + * and the third with the third. <br> + * 1 | 2 <br> + * ----- <br> + * 4 | 3 <br> + */ + + public void downUpY() { + int nxy2 = nxy / 2; + for (int q1 = 0; q1 < nxy2; q1++) { + int q2 = q1 + nxy2; + double r = real[q1] + real[q2]; + double im = imag[q1] + imag[q2]; + real[q1] = real[q2] = r; + imag[q1] = imag[q2] = im; + } + } + + /** + * Performs dyadic downsampling followed by upsampling in Fourier domain on + * this ComplexImage. Sums the four quadrants pointwise and places the sum + * in each quadrant. + */ + + public void dyadicDownUp() { + int nx2 = nx / 2; + int ny2 = ny / 2; + int nxy2 = nxy / 2; + for (int y = 0; y < nxy2; y += nx) { + for (int q1 = y, end = q1 + nx2; q1 < end; q1++) { + int q2 = q1 + nx2; + int q3 = q1 + nxy2; + int q4 = q3 + nx2; + double r = real[q1] + real[q2] + real[q3] + real[q4]; + real[q1] = real[q2] = real[q3] = real[q4] = r; + double im = imag[q1] + imag[q2] + imag[q3] + imag[q4]; + imag[q1] = imag[q2] = imag[q3] = imag[q4] = im; + } + } + } + + /** + * Sums the four quadrants of this ComplexImage pointwise and reduces its + * size by 2 along each dimension. + */ + + public void dyadicDownUpCrop() { + int nx2 = nx / 2; + int ny2 = ny / 2; + int nxy2 = nxy / 2; + int ind = 0; + for (int y = 0; y < nxy2; y += nx) { + for (int q1 = y, end = q1 + nx2; q1 < end; q1++) { + int q2 = q1 + nx2; + int q3 = q1 + nxy2; + int q4 = q3 + nx2; + double r = real[q1] + real[q2] + real[q3] + real[q4]; + real[ind] = r; + double im = imag[q1] + imag[q2] + imag[q3] + imag[q4]; + imag[ind++] = im; + } + } + nx = nx2; + ny = ny2; + nxy /= 4; + } + + /** + * Sums the lower half of this ComplexImage to the upper half, pointwise. + * Keeps only the upper half. + */ + + public void dyadicDownY() { + ComplexImage temp = getSubimage(0, nx - 1, 0, ny / 2 - 1); + ComplexImage temp1 = getSubimage(0, nx - 1, ny / 2, ny - 1); + temp1.add(temp); + putSubimage(0, 0, temp1); + ny = ny / 2; + nxy /= 2; + } + + /** + * Subsamples this ComplexImage by 2x2, reducing its size by 2 along each + * dimension. + */ + + public void decimateCrop() { + int nx2 = nx / 2; + int ny2 = ny / 2; + if (imag == null) { + for (int y = 0; y < ny2; y++) { + for (int x = 0, k = 2 * nx * y, k1 = nx2 * y; x < nx2; x++, k += 2, k1++) { + real[k1] = real[k]; + } + } + } + else { + for (int y = 0; y < ny2; y++) { + for (int x = 0, k = 2 * nx * y, k1 = nx2 * y; x < nx2; x++, k += 2, k1++) { + real[k1] = real[k]; + imag[k1] = imag[k]; + } + } + } + nx /= 2; + ny /= 2; + nxy /= 4; + } + + /** + * Subsamples this ComplexImage by 2x2, circularily, while keeping its size + * unchanged. + */ + + public void decimate() { + ComplexImage temp = copyImage(); + if (imag == null) { + for (int l = 0, y = 0; l < nxy; l += nx, y += 2) { + if (y >= ny) { + y -= ny; + } + for (int k = l, x = 0; k < l + nx; k++, x += 2) { + if (x >= nx) { + x -= nx; + } + int k1 = y * nx + x; + real[k] = temp.real[k1]; + } + } + } + else { + for (int l = 0, y = 0; l < nxy; l += nx, y += 2) { + if (y >= ny) { + y -= ny; + } + for (int k = l, x = 0; k < l + nx; k++, x += 2) { + if (x >= nx) { + x -= nx; + } + int k1 = y * nx + x; + real[k] = temp.real[k1]; + imag[k] = temp.imag[k1]; + } + } + } + } + + /** + * Substitutes the 2k-th and the 2k+1-th column of this ComplexImage by + * their sum, for each integer k. The number of columns of the ComplexImage + * is reduced by two. + */ + + public void fold() { + int nxy2 = nxy / 2; + for (int k = 0; k < nxy2; k++) { + real[k] = real[2 * k] + real[2 * k + 1]; + imag[k] = imag[2 * k] + imag[2 * k + 1]; + } + nx /= 2; + nxy /= 2; + } + + /** + * Substitutes each column of this ComplexImage by two columns. Each of them + * contains the even (odd) samples from the initial column and the odd + * (even) positions contain zeros. The number of columns of the ComplexImage + * is increased by two. + */ + + public void unfold() { + double[] real1 = new double[2 * nxy]; + double[] imag1 = new double[2 * nxy]; + for (int k = 0; k < 2 * nxy; k++) + real1[k] = imag1[k] = 0; + for (int k = 0; k < nx * ny; k++) { + if (k % (2 * nx) < nx) { // odd row + real1[2 * k] = real[k]; + imag1[2 * k] = imag[k]; + } + else { + real1[2 * k + 1] = real[k]; + imag1[2 * k + 1] = imag[k]; + } + } + real = real1; + imag = imag1; + nx = nx * 2; + nxy *= 2; + } + + /** + * Extends this ComplexImage circularily to double its size along each + * direction. <br> + */ + + public void dyadicUpsample() { + int index; + int q1; + int q2; + int q3; + int q4; + int nx2 = 2 * nx; + int nxy2 = 2 * nxy; + double r; + double i; + if (real.length < 4 * nxy) { + double[] re = new double[4 * nxy]; + System.arraycopy(real, 0, re, 0, nxy); + real = re; + } + if (imag.length < 4 * nxy) { + double[] im = new double[4 * nxy]; + System.arraycopy(imag, 0, im, 0, nxy); + imag = im; + } + for (int y = ny - 1; y >= 0; y--) { + int ynx = y * nx; + int ynx2 = 2 * ynx; + for (int x = nx - 1; x >= 0; x--) { + index = ynx + x; + r = real[index]; + i = imag[index]; + q1 = ynx2 + x; + real[q1] = r; // First quadrant + imag[q1] = i; + q2 = q1 + nx; + real[q2] = r; // Second quadrant + imag[q2] = i; + q3 = q1 + nxy2; + real[q3] = r; // Third quadrant + imag[q3] = i; + q4 = q3 + nx; + real[q4] = r; // Forth quadrant + imag[q4] = i; + } + } + nx *= 2; + ny *= 2; + nxy *= 4; + } + + /** + * Multiplies this ComplexImage by e^ix, with x=2*pi*kx/nx, where kx is the + * column index. + */ + + public void modulatePlusX() { + if (imag == null) { + imag = new double[nxy]; + } + int index; + double x; + double c; + double s; + double re; + double im; + for (int k = 1; k < nx; k++) { + x = PI2 * (double) k / (double) nx; + c = Math.cos(x); + s = Math.sin(x); + for (int l = 0; l < ny; l++) { + index = l * nx + k; + re = real[index]; + im = imag[index]; + real[index] = c * re - s * im; + imag[index] = s * re + c * im; + } + } + } + + /** + * Multiplies this ComplexImage by e^-ix, with x=2*pi*kx/nx, where kx is the + * column index. + */ + + public void modulateMinusX() { + if (imag == null) { + imag = new double[nxy]; + } + int index; + double x; + double c; + double s; + double re; + double im; + for (int k = 1; k < nx; k++) { + x = (double) k * PI2 / (double) nx; + c = Math.cos(x); + s = Math.sin(x); + for (int l = 0; l < ny; l++) { + index = l * nx + k; + re = real[index]; + im = imag[index]; + real[index] = c * re + s * im; + imag[index] = c * im - s * re; + } + } + } + + /** + * Multiplies this ComplexImage by e^iy, with y=2*pi*ky/ny, where ky is the + * row index. + */ + + public void modulatePlusY() { + if (imag == null) { + imag = new double[nxy]; + } + int index; + double y; + double c; + double s; + double re; + double im; + int knx; + for (int k = 1; k < ny; k++) { + y = PI2 * (double) k / (double) ny; + c = Math.cos(y); + s = Math.sin(y); + knx = k * nx; + for (int l = 0; l < nx; l++) { + index = knx + l; + re = real[index]; + im = imag[index]; + real[index] = c * re - s * im; + imag[index] = s * re + c * im; + } + } + } + + /** + * Multiplies this ComplexImage by e^-iy, with y=2*pi*ky/ny, where ky is the + * row index. + */ + + public void modulateMinusY() { + if (imag == null) { + imag = new double[nxy]; + } + int index; + double y; + double c; + double s; + double re; + double im; + int knx; + for (int k = 1; k < ny; k++) { + y = PI2 * (double) k / (double) ny; + c = Math.cos(y); + s = Math.sin(y); + knx = k * nx; + for (int l = 0; l < nx; l++) { + index = knx + l; + re = real[index]; + im = imag[index]; + real[index] = c * re + s * im; + imag[index] = c * im - s * re; + } + } + } + + /** + * Multiplies this ComplexImage by e^i(x+y), with x=2*pi*kx/nx, where kx is + * the column index, and y=2*pi*ky/ny, where ky is the row index. + */ + + public void modulatePlusQuincunx() { + if (imag == null) { + imag = new double[nxy]; + } + int index; + double x; + double c; + double s; + double re; + double im; + for (int k = 0; k < nx; k++) { + for (int l = 0; l < ny; l++) { + x = PI2 * ((double) k / (double) nx + (double) l / (double) ny); + c = Math.cos(x); + s = Math.sin(x); + index = l * nx + k; + re = real[index]; + im = imag[index]; + real[index] = c * re - s * im; + imag[index] = s * re + c * im; + } + } + } + + /** + * Multiplies this ComplexImage by e^-i(x+y), with x=2*pi*kx/nx, where kx is + * the column index, and y=2*pi*ky/ny, where ky is the row index. + */ + + public void modulateMinusQuincunx() { + if (imag == null) { + imag = new double[nxy]; + } + int index; + double x; + double c; + double s; + double re; + double im; + for (int k = 0; k < nx; k++) { + for (int l = 0; l < ny; l++) { + x = PI2 * ((double) k / (double) nx + (double) l / (double) ny); + c = Math.cos(x); + s = Math.sin(x); + index = l * nx + k; + re = real[index]; + im = imag[index]; + real[index] = c * re + s * im; + imag[index] = c * im - s * re; + } + } + } + + /** + * Returns this ComplexImage rotated by 45 degrees clockwise. Only the + * ComplexImage samples on the quincunx grid are kept, the ComplexImage is + * rotated so that the remaining samples are all in the same region, and the + * rest of the output ComplexImage is filled by the background. Size of + * rotated image is bigger or equal to image size before rotation + * + * @param back + * the background color to be used. + * @return the rotated ComplexImage + */ + + public ComplexImage rotate(double back) { + double s1 = 0; + double s0 = 0; + int k = 0; + int i = 1; + for (int y = 0; y < nxy; y += nx) { + for (int ind = y + k, end = y + nx; ind < end; ind += 2) { + s0 += Math.abs(real[ind]); + s1 += Math.abs(real[ind + i]); + } + k = 1 - k; + i = -i; + } + k = (s0 < s1) ? 1 : 0; + int s = (nx + ny) / 2; + ComplexImage rot = new ComplexImage(s, s); + rot.settoConstant(back, back); + for (int y = 0; y < ny; y++) { + for (int x = k, ind = y * nx + k; x < nx; x += 2, ind += 2) { + int x1 = (x - y + ny - 1) / 2; + int y1 = (x + y) / 2; + rot.real[rot.nx * y1 + x1] = real[ind]; + rot.imag[rot.nx * y1 + x1] = imag[ind]; + } + k = 1 - k; + } + return rot; + } + + /** + * Rotates this ComplexImage back, anticlockwise by 45 degrees. After + * applying the rotate operation followed by the unrotate, only the samples + * on the quincunx grid are kept. + * + * @param kx + * the number of rows in the unrotated ComplexImage + * @param ky + * the number of columns in the unrotated ComplexImage + */ + + public void unrotate(int kx, int ky) { + int kxy = kx * ky; + int nx1 = nx; + int ny1 = ny; + double[] re = new double[kxy]; + double[] im = new double[kxy]; + nx = kx; + ny = ky; + nxy = nx * ny; + int k = 0; + for (int y = 0; y < ky; y++) { + for (int x = k, ind = kx * y + k; x < kx; x += 2, ind += 2) { + int x1 = (x - y + ky - 1) / 2; + int y1 = (x + y) / 2; + re[ind] = real[nx1 * y1 + x1]; + im[ind] = imag[nx1 * y1 + x1]; + } + k = 1 - k; + } + System.arraycopy(re, 0, real, 0, kxy); + System.arraycopy(im, 0, imag, 0, kxy); + } + + /** + * Places the ComplexImage original extended by its first row and first + * column in this ComplexImage. + * + * @param original + * the image to extend + */ + + public void extend(ComplexImage original) { + putSubimage(0, 0, original); + for (int x = 0; x < original.nx; x++) { + real[(original.nx + 1) * original.ny + x] = real[x]; + } + for (int y = 0; y < original.ny + 1; y++) { + real[(original.nx + 1) * y + original.nx] = real[(original.nx + 1) * y]; + } + } + + /** + * Normalizes the modulus of this ComplexImage so that all values are in the + * range (0,250). Does not modify the background given by the value 255.0 in + * the real part. + */ + + public void stretch() { + double max = 250.0; + double back = 255.0; + ComplexImage mod = copyImage(); + if (imag == null) { + double maximage = mod.maxAbsReal(); + double sp = max / maximage; + for (int i = 0; i < nxy; i++) { + if (real[i] != back) { + real[i] *= sp; + } + } + } + else { + mod.modulus(); + double maximage = mod.max(); + double sp = max / maximage; + for (int i = 0; i < nxy; i++) { + if (real[i] != back) { + real[i] *= sp; + imag[i] *= sp; + } + } + } + } + + /** + * Stretches each subband of the nonredundant quincunx transform in this + * ComplexImage so that all values are between -250 and 250. + * + * @param J + * number of decomposition levels + */ + + void displayQuincunxNonredundant(int J) { + int dx = nx; + int dy = ny; + for (int j = 1; j <= J; j++) { + ComplexImage sub; + if (j % 2 == 1) { // Odd iteration + sub = getSubimage(dx / 2, dx - 1, 0, dy - 1); + sub.stretch(); + putSubimage(dx / 2, 0, sub); + dx /= 2; + } + else { + sub = getSubimage(0, dx - 1, dy / 2, dy - 1); + sub.stretch(); + putSubimage(0, dy / 2, sub); + dy /= 2; + } + } + } + + /** + * Puts a frame of the given color around the real part of this + * ComplexImage. + * + * @param color + * the frame color + */ + + public void frame(double color) { + for (int k = 0, nxy1 = nxy - 1; k < nx; k++) { + real[k] = real[nxy1 - k] = color; + } + for (int k = 1; k < ny; k++) { + real[k * nx] = real[k * nx - 1] = color; + } + } + + /** + * Puts the frames of given colors around both the real and the complex part + * of this ComplexImage. + * + * @param colorr + * the frame color for the real part + * @param colori + * the frame color for the complex part + */ + + public void frame(double colorr, double colori) { + for (int k = 0, nxy1 = nxy - 1; k < nx; k++) { + real[k] = real[nxy1 - k] = colorr; + imag[k] = imag[nxy1 - k] = colori; + } + for (int k = 1; k < ny; k++) { + real[k * nx] = real[k * nx - 1] = colorr; + imag[k * nx] = imag[k * nx - 1] = colori; + } + } + + /** + * Extends this ComplexImage by adding zero rows and columns around the + * edges. + */ + + public void extendWithZeros() { + ComplexImage temp = new ComplexImage(nx + 2, ny + 2); + ComplexImage temp1 = copyImage(); + temp.putSubimage(1, 1, temp1); + real = temp.real; + imag = temp.imag; + nx += 2; + ny += 2; + nxy = nx * ny; + } + + /** + * Removes the first and the last row and coulumn of this ComplexImage. + */ + + public void reduce() { + ComplexImage temp = getSubimage(1, nx - 2, 1, ny - 2); + real = temp.real; + imag = temp.imag; + nx -= 2; + ny -= 2; + nxy = nx * ny; + } + + /** + * Sets this ComplexImage value to 1 where it is different from 0. + */ + + public void createMap() { + for (int i = 0; i < nxy; i++) { + if (!(real[i] == 0.0)) { + real[i] = 1.0; + } + } + } + + /** + * Implements soft threshold on this ComplexImage. Where the modulus of the + * ComplexImage is bigger then t it is reduced by t while the phase of the + * ComplexImage remains unchanged. Elsewhere, the value is set to zero. + * + * @param t + * threshold + */ + + public void softThreshold(double t) { + double t2 = t * t; + for (int k = 0; k < nxy; k++) { + if ((real[k] * real[k] + imag[k] * imag[k]) < t2) { + real[k] = imag[k] = 0; + } + else { + double r = real[k]; + double im = imag[k]; + double m = Math.sqrt(r * r + im * im); + real[k] -= r / m; + imag[k] -= im / m; + } + } + } + + /** + * Implements hard threshold on this ComplexImage. Where the modulus of the + * ComplexImage is smaler then t the value it is set to zero. + * + * @param t + * the threshold + */ + + public void hardThreshold(double t) { + double t2 = t * t; + for (int k = 0; k < nxy; k++) { + if ((real[k] * real[k] + imag[k] * imag[k]) < t2) { + real[k] = imag[k] = 0.0; + } + } + } + + /** + * Sets the real part of this ComplexImage to max where it is higher then + * max and to min where it is smaller then min. + * + * @param min + * the lower limit + * @param max + * the upper limit + */ + + public void limitImage(double min, double max) { + for (int i = 0; i < nxy; i++) { + if (real[i] > max) { + real[i] = max; + } + else { + if (real[i] < min) { + real[i] = min; + } + } + } + } + + /* + * Searches for neighbouring pixels to include in the zerocrossings map + */ + + private void search8(int x, int y) { + if ((x < nx) && (x >= 0) && (y < ny) && (y >= 0)) { + int i = y * nx + x; + if (real[i] == 1.0) { + real[i] = 3.0; + search8(x + 1, y); + search8(x - 1, y); + search8(x, y + 1); + search8(x, y - 1); + search8(x + 1, y + 1); + search8(x - 1, y - 1); + search8(x - 1, y + 1); + search8(x + 1, y - 1); + } + } + } + + /* + * Creates the zerocrossing map from the primary map + */ + + private void zeroCrossHysteresis8() { + for (int y = 0; y < ny; y++) { + int i = y * nx; + for (int x = 0; x < nx; x++, i++) { + if (real[i] == 2.0) { + search8(x + 1, y); + search8(x - 1, y); + search8(x, y + 1); + search8(x, y - 1); + search8(x + 1, y + 1); + search8(x - 1, y - 1); + search8(x - 1, y + 1); + search8(x + 1, y - 1); + } + } + } + for (int x = 0; x < nxy; x++) { + if (real[x] < 1.5) { + real[x] = 0.0; + } + else { + real[x] = 1.0; + } + } + } + + /** + * Performs Canny edge detection on this ComplexImage and keeps only the + * pixels that belong to the edge map. The edge of the image is included in + * the map. + * + * @param Tl + * the lower threshold + * @param Th + * the higher threshold + * @return the percentage of pixels that belong to the edge map, excluding + * the edge + */ + + public double canny(double Tl, double Th) { + ComplexImage mod = copyImage(); + mod.modulus(); + // determin directions + int[] dir = new int[nxy]; // Directions of gradient 0,1,2,3 + for (int x = 0; x < nxy; x++) { + if (((imag[x] <= 0.0) && (real[x] > -imag[x])) || ((imag[x] >= 0.0) && (real[x] < -imag[x]))) { + dir[x] = 0; + } + if (((real[x] > 0.0) && (-imag[x] >= real[x])) || ((real[x] < 0.0) && (real[x] >= -imag[x]))) { + dir[x] = 1; + } + if (((real[x] <= 0.0) && (real[x] > imag[x])) || ((real[x] >= 0.0) && (real[x] < imag[x]))) { + dir[x] = 2; + } + if (((imag[x] < 0.0) && (real[x] <= imag[x])) || ((imag[x] > 0.0) && (real[x] >= imag[x]))) { + dir[x] = 3; + } + } + // Nonmaxima supression + ComplexImage mod1 = mod.copyImage(); + int[][] neighbours = { { nx, nx - 1, -nx, -nx + 1 }, { -1, nx - 1, 1, -nx + 1 }, { -1, -nx - 1, 1, nx + 1 }, { -nx, -nx - 1, nx, nx + 1 } }; + // exclude edges + for (int x = 0, x1 = nxy - 1; x < nx; x++, x1--) { + mod1.real[x] = mod1.real[x1] = 0.0; + } + for (int x = 0, x1 = nxy - 1; x < nxy; x += nx, x1 -= nx) { + mod1.real[x] = mod1.real[x1] = 0.0; + } + for (int x = nx + 1; x < nxy - nx - 1; x++) { + double d = Math.abs(imag[x] / real[x]); + if (d > 1.0) { + d = 1.0 / d; + } + double ss1 = mod.real[x + neighbours[dir[x]][0]] * (1.0 - d) + mod.real[x + neighbours[dir[x]][1]] * d; + double ss2 = mod.real[x + neighbours[dir[x]][2]] * (1.0 - d) + mod.real[x + neighbours[dir[x]][3]] * d; + if (!((mod.real[x] > ss1) && (mod.real[x] > ss2))) { + mod1.real[x] = 0.0; + } + } + // Hysteresis + // Form a map + ComplexImage map = new ComplexImage(nx, ny); + for (int x = 0; x < nxy; x++) { + if (mod1.real[x] > Th) { + map.real[x] = 2.0; + } + else { + if (mod1.real[x] > Tl) { + map.real[x] = 1.0; + } + } + } + map.zeroCrossHysteresis8(); + double pr = map.sumReal() / map.nxy; + map.frame(1.0); + multiply(map); + return pr; + } + + /** + * Differentiates the real part of this ComplexImage along x. + */ + + public void derivativeX() { + ComplexImage temp = new ComplexImage(nx, ny); + System.arraycopy(real, 0, temp.real, 0, nxy); + temp.FFT2D(); + for (int k = 0; k < nx; k++) { + double x = PI2 * (double) k / (double) nx; + if (x > Math.PI) { + x -= PI2; + } + for (int l = 0; l < ny; l++) { + int index = l * nx + k; + temp.real[index] *= x; + temp.imag[index] *= x; + } + } + temp.conj(); + System.arraycopy(temp.real, 0, imag, 0, nxy); + System.arraycopy(temp.imag, 0, real, 0, nxy); + iFFT2D(); + } + + /** + * Differentiates the real part of this ComplexImage along y. + */ + + public void derivativeY() { + ComplexImage temp = new ComplexImage(nx, ny); + System.arraycopy(real, 0, temp.real, 0, nxy); + temp.FFT2D(); + for (int k = 0; k < ny; k++) { + double y = PI2 * (double) k / (double) ny; + if (y > Math.PI) { + y -= PI2; + } + for (int l = 0; l < nx; l++) { + int index = k * nx + l; + temp.real[index] *= y; + temp.imag[index] *= y; + } + } + temp.conj(); + System.arraycopy(temp.real, 0, imag, 0, nxy); + System.arraycopy(temp.imag, 0, real, 0, nxy); + iFFT2D(); + } + + /** + * Smooth the real part of this ComplexImage. The value of each pixel is + * computed as the mean value of its 3x3 neighbourhood. + */ + + public void smooth() { + // Define neighbour + int[] d = { 1, -1, nx, -nx, nx + 1, nx - 1, -nx + 1, -nx - 1, 0 }; + double[] w1 = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; // additional + // weights + // that + // depend + // on + // position + ComplexImage temp = copyImage(); + for (int i = nx + 1; i < nxy - nx - 1; i++) { + double m = 0.0; + double s = 0.0; + for (int k = 0; k < 9; k++) { + double w = w1[k]; + s += w; // normalization + m += (temp.real[i + d[k]] * w); + } + m /= s; + real[i] = m; + } + } + + /** + * Smooth the real part of this ComplexImage using the given weights. The + * value of each pixel is computed as the mean value of its weighted 3x3 + * neighbourhood. + * + * @param weights + * the weights, a Compleximage of the same size as this + * ComplexImage + */ + + public void smooth(ComplexImage weights) { + // Define neighbour + int[] d = { 1, -1, nx, -nx, nx + 1, nx - 1, -nx + 1, -nx - 1, 0 }; + double[] w1 = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; // additional + // weights + // that + // depend + // on + // position + ComplexImage temp = copyImage(); + for (int i = nx + 1; i < nxy - nx - 1; i++) { + double m = 0.0; + double s = 0.0; + for (int k = 0; k < 9; k++) { + double w = weights.real[i + d[k]] * w1[k]; + s += w; // normalization + double t = temp.real[i + d[k]]; + m += (t * w); + } + m /= s; + real[i] = m; + } + } + + /** + * Computes the first parameter of Riesz transform of this ComplexImage. + */ + + public void riesz1() { + FFT2D(); + ComplexImage mult1 = new ComplexImage(nx, ny); + for (int y = 0; y < ny; y++) { + double omy = 2.0 * Math.PI * (double) y / (double) ny - Math.PI; + for (int x = 0; x < nx; x++) { + double omx = 2.0 * Math.PI * (double) x / (double) nx - Math.PI; + mult1.real[nx * y + x] = 1.0 / Math.sqrt(omx * omx + omy * omy); + mult1.real[nx * y + x] *= omx; + } + } + mult1.shift(); + mult1.real[0] = 1.0; + multiply(mult1); + iFFT2D(); + double[] temparray = new double[nxy]; + System.arraycopy(real, 0, temparray, 0, nxy); + System.arraycopy(imag, 0, real, 0, nxy); + System.arraycopy(temparray, 0, imag, 0, nxy); + conj(); + } + + /** + * Computes the second parameter of Riesz transform of this ComplexImage. + */ + + public void riesz2() { + FFT2D(); + ComplexImage mult1 = new ComplexImage(nx, ny); + for (int y = 0; y < ny; y++) { + double omy = 2.0 * Math.PI * (double) y / (double) ny - Math.PI; + for (int x = 0; x < nx; x++) { + double omx = 2.0 * Math.PI * (double) x / (double) nx - Math.PI; + mult1.real[nx * y + x] = 1.0 / Math.sqrt(omx * omx + omy * omy); + mult1.real[nx * y + x] *= omy; + } + } + mult1.shift(); + mult1.real[0] = 1.0; + multiply(mult1); + double[] temparray = new double[nxy]; + System.arraycopy(real, 0, temparray, 0, nxy); + System.arraycopy(imag, 0, real, 0, nxy); + System.arraycopy(temparray, 0, imag, 0, nxy); + conj(); + iFFT2D(); + } +} diff --git a/src/bilib/src/polyharmonicwavelets/DyadicFilters.java b/src/bilib/src/polyharmonicwavelets/DyadicFilters.java new file mode 100644 index 0000000..36b7f36 --- /dev/null +++ b/src/bilib/src/polyharmonicwavelets/DyadicFilters.java @@ -0,0 +1,497 @@ +package polyharmonicwavelets; + +// +// DyadicFilters.java +// PolyharmonicWavelets +// +// Created by Biomedical Imaging Group on 2/13/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +import java.util.*; +import ij.*; +import java.text.DecimalFormat; + +/** + * This class computes the filters for the dyadic wavelet transform. + * + * @author Katarina Balac, EPFL. + */ + +public class DyadicFilters { + + /** + * The analysis filters. FA[0] the lowpass analysis filter FA[1], FA[2], + * FA[3] the highpass analysis filters + */ + public ComplexImage[] FA; + + /** + * The synthesis filters. FS[0] the lowpass synthesis filter FS[1], FS[2], + * FS[3] the highpass synthesis filters + */ + public ComplexImage[] FS; + + /** + * Pyramid synthesis filters + */ + public ComplexImage[] FP; + + /** + * The prefilter. + */ + public ComplexImage P; + + /** + * The autocorrelation. + */ + + public ComplexImage ac; + + private Parameters param; // Parameters for the + // transform + private int nx; // Size of filters + private int ny; + private final double PI = Math.PI; + private final double PI2 = 2.0 * PI; + private final double sqrt2 = Math.sqrt(2.0); + + /** + * Constructor, creates a filters object and reserves the memory space for + * all the analysis and synthesis filters that will be needed depending on + * parameters. + * + * @param par + * the wavelet transform parameters + * @param sizex + * the number of columns in the image to transform + * @param sizey + * the number of rows in the image to transform + */ + + public DyadicFilters(Parameters par, int sizex, int sizey) { + param = par; + nx = sizex; + ny = sizey; + FA = new ComplexImage[4]; + FA[0] = new ComplexImage(nx, ny, param.N == 0); + FA[1] = new ComplexImage(nx, ny, (!(param.redundancy == param.BASIS) && (param.N == 0))); + if (param.redundancy == param.BASIS) { + FA[2] = new ComplexImage(nx, ny); + FA[3] = new ComplexImage(nx, ny); + } + FS = new ComplexImage[4]; + if (!(param.analysesonly)) { + FS = new ComplexImage[4]; + FS[0] = new ComplexImage(nx, ny, param.N == 0); + for (int i = 1; i < 4; i++) { + FS[i] = new ComplexImage(nx, ny); + } + if (param.redundancy == param.PYRAMID) { + FP = new ComplexImage[3]; + for (int i = 0; i < 3; i++) { + FP[i] = new ComplexImage(nx, ny); + } + } + } + } + + /** + * Sets the wavelet transform parameters to desired value. + * + * @param param + * the parameters to set + */ + + public void setParameters(Parameters param) { + this.param = param; + } + + /* + * Calculates numenator of scaling function, localization support is [ minx + * : (maxx-minx)/sizex : maxx-(maxx-minx)/sizex, miny : (maxy-miny)/sizey : + * maxy-(maxy-miny)/sizey ] output is of size [sizex, sizey] and defined + * on[minx...maxx-eps,miny...maxy-eps] + */ + + private ComplexImage multiplicator(int sizex, int sizey, double minx, double miny, double maxx, double maxy, double gama, int N) { + ComplexImage result = new ComplexImage(sizex, sizey, N == 0); + double gama2 = gama / 2.0; + final double d83 = 8.0 / 3.0; + final double d23 = 2.0 / 3.0; + double epsx = (maxx - minx) / (4.0 * (double) sizex); + double epsy = (maxy - miny) / (4.0 * (double) sizey); + double rx = (maxx - minx) / (double) sizex; + double ry = (maxy - miny) / (double) sizey; + double[] sxarr = new double[sizex]; + double[] x1arr = new double[sizex]; + double x = minx; + for (int kx = 0; kx < sizex; kx++, x += rx) { + sxarr[kx] = Math.sin(x / 2) * Math.sin(x / 2); + double x1 = x; + while (x1 >= Math.PI - epsx) + x1 = x1 - PI2; + while (x1 < -Math.PI - epsx) + x1 = x1 + PI2; + x1arr[kx] = x1; + } + double y = miny; + for (int ky = 0; ky < sizey; ky++, y += ry) { + int kxy = ky * sizex; + double sy = Math.sin(y / 2.0); + sy = sy * sy; + double y1 = y; + while (y1 >= Math.PI - epsy) + y1 = y1 - PI2; + while (y1 < -Math.PI - epsy) + y1 = y1 + PI2; + double y11 = y1; + for (int kx = 0, index = kxy; kx < sizex; kx++, index++) { + y1 = y11; + double x1 = x1arr[kx]; + final double sx = sxarr[kx]; + double a = 1.0; + if (param.type == param.ISOTROPIC) { // Isotropic + a = 4.0 * (sx + sy) - d83 * (sx * sy); + } + if (param.type == param.CHANGESIGMA) { + final double sigma2 = param.s2; + final double b = -16.0 / sigma2; + final double c = 24.0 / (sigma2 * sigma2) - 16.0 / (3.0 * sigma2); + final double d = 8.0 / (sigma2 * sigma2) + 32.0 / 45.0 - 16.0 / (3.0 * sigma2); + final double e = 4.0 / 3.0 - 8.0 / sigma2; + a = 4.0 * (sx + sy) + b * (sx * sy) + c * (sx * sx * sy + sy * sy * sx) + d * (sx * sx * sx + sy * sy * sy) + e * (sx * sx + sy * sy); + } + double re = Math.pow(a, gama2); + double im = 0.0; + if (N > 0) { + boolean xpi = ((x1 < -Math.PI + epsx) && (x1 > -Math.PI - epsx)); + boolean ypi = ((y1 < -Math.PI + epsy) && (y1 > -Math.PI - epsy)); + boolean x0 = ((x1 < epsx) && (x1 > -epsx)); + boolean y0 = ((y1 < epsy) && (y1 > -epsy)); + if (!(x0 && y0)) { + double x1p = x1; + double y1p = y1; + if (xpi && !y0 && !ypi) { + x1p = 0.0; + } + if (ypi && !x0 && !xpi) { + y1p = 0.0; + } + x1 = x1p; + y1 = y1p; + } + for (int i = 0; i < N; i++) { + double re1 = re * x1 - im * y1; + double im1 = re * y1 + im * x1; + re = re1; + im = im1; + } + double t = Math.pow(x1 * x1 + y1 * y1, (double) N / 2.0); + if (t == 0.0) { + result.real[index] = 0.0; + result.imag[index] = 0.0; + } + else { + result.real[index] = re / t; + result.imag[index] = im / t; + } + } + else { + result.real[index] = re; + } + } + } + return result; + } + + /* + * Calculates denominator of scaling function support is [ 0 : maxx/sizex : + * maxx-maxx/sizex, 0 : maxy/sizey : maxy-maxy/sizey ] output is of size + * [sizex, sizey] and defined on[0...maxx-eps,0...maxy-eps] + */ + + private ComplexImage denominator(int sizex, int sizey, double minx, double miny, double maxx, double maxy, int N) { + ComplexImage result = new ComplexImage(sizex, sizey); + double gamaN2; + gamaN2 = (param.order - N) / 2.0; + for (int ky = 0; ky < sizey; ky++) { + int kxy = ky * sizex; + double y = miny + (double) ky * (maxy - miny) / (double) sizey; + for (int kx = 0, index = kxy; kx < sizex; kx++, index++) { + double x = minx + (double) kx * (maxx - minx) / (double) sizex; + double re = Math.pow(x * x + y * y, gamaN2); + double im = 0.0; + if (N > 0) { + for (int i = 0; i < N; i++) { + double re1 = re * x - im * y; + double im1 = re * y + im * x; + re = re1; + im = im1; + } + result.real[index] = re; + result.imag[index] = im; + } + else { + result.real[index] = re; + } + } + } + return result; + } + + /* + * Computes the prefilter P + */ + + private void calculatePrefilter() { + P = multiplicator(nx, ny, -PI, -PI, PI, PI, param.order, 0); + ComplexImage d = denominator(nx, ny, -PI, -PI, PI, PI, 0); + P.divide(d, 1.0, 0.0); + P.shift(); + if (param.flavor == param.DUALOPERATOR) { + P.divide(ac); + } + } + + /* + * Calculate filters for pyramid synthesis + */ + + private void pyramidSynthesisFilters() { + FA[1].multiply(1 / sqrt2); + ComplexImage[] Ge = FP; + Ge[0].copyImageContent(FA[1]); + Ge[1].copyImageContent(FA[1]); + Ge[2].copyImageContent(FA[1]); + Ge[0].multiply(FS[1]); + Ge[1].multiply(FS[2]); + Ge[2].multiply(FS[3]); + Ge[0].multiply(0.5); + Ge[1].multiply(0.5); + Ge[2].multiply(0.5); + ComplexImage[] Geconj = Ge; + Geconj[0].conj(); + Geconj[1].conj(); + Geconj[2].conj(); + int nx2 = FA[1].nx / 2; + int ny2 = FA[1].ny / 2; + int nxy2 = FA[1].nxy / 2; + int[] d = { 0, nx2, nxy2, nx2 + nxy2 }; + double[] mr = new double[9]; + double[] mi = new double[9]; + for (int ky = 0, km = 0; ky < nxy2; ky += FA[1].nx) { + for (int kx = ky, end = ky + nx2; kx < end; kx++, km++) { + for (int i = 0; i < 9; i++) { + mr[i] = mi[i] = 0.0; + } + double inr0 = 0.0; + double inr1 = 0.0; + double inr2 = 0.0; + double inr4 = 0.0; + double inr5 = 0.0; + double inr8 = 0.0; + double inri = 0.0; + double ini1 = 0.0; + double ini2 = 0.0; + double ini4 = 0.0; + double ini5 = 0.0; + double ini8 = 0.0; + for (int l = 0; l < 4; l++) { + int k = kx + d[l]; + inr0 += Geconj[0].real[k] * Geconj[0].real[k] + Geconj[0].imag[k] * Geconj[0].imag[k]; + inr4 += Geconj[1].real[k] * Geconj[1].real[k] + Geconj[1].imag[k] * Geconj[1].imag[k]; + inr8 += Geconj[2].real[k] * Geconj[2].real[k] + Geconj[2].imag[k] * Geconj[2].imag[k]; + inr1 += Geconj[0].real[k] * Geconj[1].real[k] + Geconj[0].imag[k] * Geconj[1].imag[k]; + ini1 += (-Geconj[0].real[k] * Geconj[1].imag[k] + Geconj[0].imag[k] * Geconj[1].real[k]); + inr2 += Geconj[0].real[k] * Geconj[2].real[k] + Geconj[0].imag[k] * Geconj[2].imag[k]; + ini2 += (-Geconj[0].real[k] * Geconj[2].imag[k] + Geconj[0].imag[k] * Geconj[2].real[k]); + inr5 += Geconj[1].real[k] * Geconj[2].real[k] + Geconj[1].imag[k] * Geconj[2].imag[k]; + ini5 += (-Geconj[1].real[k] * Geconj[2].imag[k] + Geconj[1].imag[k] * Geconj[2].real[k]); + } + // invert m + mr[0] = (inr4 * inr8) - (inr5 * inr5 + ini5 * ini5); + mr[1] = (inr2 * inr5 + ini2 * ini5) - (inr1 * inr8); + mi[1] = (-inr2 * ini5 + ini2 * inr5) - (ini1 * inr8); + mr[2] = (inr1 * inr5 - ini1 * ini5) - (inr2 * inr4); + mi[2] = (inr1 * ini5 + ini1 * inr5) - (ini2 * inr4); + double dr = mr[0] * inr0 + mr[1] * inr1 + mi[1] * ini1 + mr[2] * inr2 + mi[2] * ini2; + mr[3] = ((inr2 * inr5 + ini2 * ini5) - (inr1 * inr8)) / dr; + mi[3] = ((inr2 * ini5 - ini2 * inr5) + (ini1 * inr8)) / dr; + mr[4] = ((inr0 * inr8) - (inr2 * inr2 + ini2 * ini2)) / dr; + mr[5] = ((inr1 * inr2 + ini1 * ini2) - (inr0 * inr5)) / dr; + mi[5] = ((inr1 * ini2 - ini1 * inr2) - (inr0 * ini5)) / dr; + mr[6] = ((inr1 * inr5 - ini1 * ini5) - (inr2 * inr4)) / dr; + mi[6] = ((-inr1 * ini5 - ini1 * inr5) + (ini2 * inr4)) / dr; + mr[7] = ((inr2 * inr1 + ini2 * ini1) - (inr0 * inr5)) / dr; + mi[7] = ((inr2 * ini1 - ini2 * inr1) + (inr0 * ini5)) / dr; + mr[8] = ((inr0 * inr4) - (inr1 * inr1 + ini1 * ini1)) / dr; + mr[0] /= dr; + mr[1] /= dr; + mi[1] /= dr; + mr[2] /= dr; + mi[2] /= dr; + // end invert m + for (int l = 0; l < 4; l++) { + int k = kx + d[l]; + double[] ger = new double[3]; + double[] gei = new double[3]; + for (int i = 0; i < 3; i++) { + ger[i] = Geconj[i].real[k]; + gei[i] = Geconj[i].imag[k]; + } + for (int i = 0; i < 3; i++) { + double gr = 0.0; + double gi = 0.0; + for (int j = 0; j < 3; j++) { + gr += ger[j] * mr[3 * i + j] - gei[j] * mi[3 * i + j]; + gi += ger[j] * mi[3 * i + j] + gei[j] * mr[3 * i + j]; + } + Geconj[i].real[k] = gr; + Geconj[i].imag[k] = gi; + } + } + } + } + FP = Geconj; + FA[1].multiply(sqrt2); + } + + /** + * Calculates all filters needed to perform dyadic transform with given + * parameters. + */ + + public void calculateFilters() { + double k = 1.0 / Math.pow(2.0, param.order); + ComplexImage HH = null; + ComplexImage L1 = null; + if (param.accompute == param.ITERATIVE) { + // Compute filter on a 2x finer grid for autocorrelation + ComplexImage L = multiplicator(2 * nx, 2 * ny, 0.0, 0.0, 2.0 * PI2, 2.0 * PI2, param.order, param.N); // Numerator(2*omega) + // is + // complex + L1 = multiplicator(2 * nx, 2 * ny, 0.0, 0.0, PI2, PI2, param.order, param.N); // Numerator(omega) + // is + // complex + ComplexImage HHdouble = L; + HHdouble.multiply(k); + HHdouble.divide(L1, 1.0, 0.0); + // compute filter on a regular grid + HH = HHdouble.copyImage(); + HH.decimateCrop(); + L1.decimateCrop(); + // compute autocorrelation if needed + if (!(param.analysesonly)) { + HHdouble.squareModulus(); + ac = Autocorrelation.autocorrIterative(HHdouble); + } + } + else { + ComplexImage L = multiplicator(nx, ny, 0.0, 0.0, 2.0 * PI2, 2.0 * PI2, param.order, param.N); // Numerator(2*omega) + // is + // complex + L1 = multiplicator(nx, ny, 0.0, 0.0, PI2, PI2, param.order, param.N); // Numerator(omega) + // is + // complex + HH = L; + HH.multiply(k); + HH.divide(L1, 1.0, 0.0); + if (!((param.analysesonly) && (param.flavor == Parameters.MARR))) { + ComplexImage simpleloc = multiplicator(nx, ny, 0.0, 0.0, PI2, PI2, 2 * param.order, 0); + ac = Autocorrelation.autocorrGamma(simpleloc, param.order); + } + } + calculatePrefilter(); + if ((param.flavor == param.OPERATOR) || (param.flavor == param.DUALOPERATOR) || (param.flavor == param.MARR)) { + // Analysis filters + FA[0].copyImageContent(HH); + FA[0].multiply(2.0); + ComplexImage G = FA[1]; + G.copyImageContent(L1); + if (!(param.flavor == param.MARR)) { + G.divide(ac); + } + G.multiply(2.0); + if (param.rieszfreq == 1) { + ComplexImage V2 = multiplicator(nx, ny, 0.0, 0.0, PI2, PI2, 2.0, 0); + G.multiply(V2); + } + G.conj(); + ComplexImage R = null; + if (param.redundancy == param.BASIS) { // Basis is not for Marr + FA[2].copyImageContent(G); + FA[3].copyImageContent(G); + } + FA[1] = G; + // Compute synthesis filters + if (!(param.analysesonly)) { + // Synthesis lowpass + ComplexImage L1conj = L1.copyImage(); + L1conj.conj(); + ComplexImage H = FS[0]; + H.copyImageContent(HH); + H.conj(); + double k1 = k * 4.0; + H.multiply(k1); + ComplexImage acd = ac.copyImage(); + acd.decimate(); + ComplexImage Gs = ac.copyImage(); + Gs.divide(acd); + Gs.multiply(0.25); + H.multiply(Gs); + FS[0] = H; + FS[0].multiply(2.0 / k); + // Synthesis highpass + Gs.divide(L1conj, 0.0, 0.0); + ComplexImage D = HH.copyImage(); + D.squareModulus(); + D.multiply(1.0 / k); + D.multiply(ac); + D.multiply(k1); + ComplexImage D1 = D.copyImage(); + ComplexImage D2 = D.copyImage(); + ComplexImage D12 = D.copyImage(); + D1.shiftX(); + D2.shiftY(); + D12.shift(); + D1.multiply(Gs); + D2.multiply(Gs); + D12.multiply(Gs); + FS[1] = D1.copyImage(); + FS[1].add(D12); + FS[2] = D2.copyImage(); + FS[2].add(D12); + FS[3] = D1; + FS[3].add(D2); + } + if (param.flavor == param.DUALOPERATOR) { + ComplexImage[] Ftmp = FA; + FA = FS; + FS = Ftmp; + FA[0].conj(); + FA[1].conj(); + FA[2].conj(); + FA[3].conj(); + FS[0].conj(); + FS[1].conj(); + FS[2].conj(); + FS[3].conj(); + } + } + if (param.redundancy == param.BASIS) { // Basis is not for Marr + FA[1].modulateMinusX(); + FA[2].modulateMinusY(); + FA[3].modulateMinusQuincunx(); + } + if (!(param.analysesonly)) { + FS[1].modulatePlusX(); + FS[2].modulatePlusY(); + FS[3].modulatePlusQuincunx(); + } + + if ((param.redundancy == param.PYRAMID) && (!(param.analysesonly))) { + pyramidSynthesisFilters(); + } + } +} \ No newline at end of file diff --git a/src/bilib/src/polyharmonicwavelets/DyadicTransform.java b/src/bilib/src/polyharmonicwavelets/DyadicTransform.java new file mode 100644 index 0000000..70f06bf --- /dev/null +++ b/src/bilib/src/polyharmonicwavelets/DyadicTransform.java @@ -0,0 +1,556 @@ +package polyharmonicwavelets; + +// +// DyadicTransform.java +// PolyharmonicWavelets +// +// Created by Biomedical Imaging Group on 2/13/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +import java.util.*; +import ij.*; +import java.text.DecimalFormat; + +/** + * This class performs basis, pyramid and redundant dyadic transform. + * + * @author Katarina Balac, EPFL. + */ + +public class DyadicTransform { + + // Analysis and synthesis filters + // FA[0]=H1 + // FA[1]=G1 + // FA[2]=G2 + // FA[3]=G3 + // similar for analysis filters + // H - lowpass filters + // G - highpass filters + + private ComplexImage[] FA; + private ComplexImage[] FS; + private ComplexImage[] FP; + private ComplexImage P; + private int J; // number of iterations + private Parameters param; + private final int nx; + private final int ny; + private final double PI2 = 2.0 * Math.PI; + private final double sqrt2 = Math.sqrt(2.0); + private DyadicFilters filters; + + /** + * Creates a DyadicTransform object. + * + * @param filt + * the filters used for the dyadic transform + * @param par + * the transform parameters + */ + + public DyadicTransform(DyadicFilters filt, Parameters par) { + J = par.J; + param = par; + FA = filt.FA; + FS = filt.FS; + FP = filt.FP; + P = filt.P; + nx = FA[0].nx; + ny = FA[0].ny; + filters = filt; + } + + /** + * Performs the in-place nonredundant dyadic analysis on the input + * ComplexImage. + * + * @param image + * the image to transform + */ + + public void dyadicAnalysis(ComplexImage image) { + ComplexImage H = FA[0].copyImage(); + ComplexImage G1 = FA[1].copyImage(); + ComplexImage G2 = FA[2].copyImage(); + ComplexImage G3 = FA[3].copyImage(); + H.multiply(0.25); + G1.multiply(0.25); + G2.multiply(0.25); + G3.multiply(0.25); + ComplexImage R = image.copyImage(); // remaining lowpass subband + R.FFT2D(); + if (param.prefilter) { + R.multiply(P); + } + int l = 1; + for (int j = 1; j <= J; j++) { + ComplexImage Y0 = R.copyImage(); + ComplexImage Y1 = R.copyImage(); + ComplexImage Y2 = R.copyImage(); + ComplexImage Y3 = R.copyImage(); + Y0.multiply(H, l); + Y1.multiply(G1, l); + Y2.multiply(G2, l); + Y3.multiply(G3, l); + Y0.dyadicDownUpCrop(); + Y1.dyadicDownUpCrop(); + Y2.dyadicDownUpCrop(); + Y3.dyadicDownUpCrop(); + // Put highpass subbands in place + Y1.iFFT2D(); + Y2.iFFT2D(); + Y3.iFFT2D(); + R = Y0.copyImage(); + image.putSubimage(R.nx, 0, Y1); + image.putSubimage(0, R.ny, Y2); + image.putSubimage(R.nx, R.ny, Y3); + l *= 2; + } + R.iFFT2D(); + image.putSubimage(0, 0, R); + } + + /** + * Performs the in-place nonredundant dyadic synthesis on ComplexImage. + * + * @param image + * the image to transform + */ + + public void dyadicSynthesis(ComplexImage image) { + ComplexImage H = FS[0]; + ComplexImage G1 = FS[1]; + ComplexImage G2 = FS[2]; + ComplexImage G3 = FS[3]; + int l = 1; + for (int k = 1; k < J; k++, l *= 2) + ; + int cx = image.nx; // size of remaining lowpass subband + int cy = image.ny; + for (int i = 0; i < J; i++, cx /= 2, cy /= 2) + ; + ComplexImage Y = new ComplexImage(image.nx, image.ny); + ComplexImage Z = new ComplexImage(image.nx, image.ny); + Y.getSubimageContent(0, cx - 1, 0, cy - 1, image); // Lowpass + Y.FFT2D(); + for (int j = J; j > 0; j--) { + Y.dyadicUpsample(); + Y.multiply(H, l); + Z.getSubimageContent(cx, 2 * cx - 1, 0, cy - 1, image); + Z.FFT2D(); + Z.dyadicUpsample(); + Z.multiply(G1, l); + Y.add(Z); + Z.getSubimageContent(0, cx - 1, cy, 2 * cy - 1, image); + Z.FFT2D(); + Z.dyadicUpsample(); + Z.multiply(G2, l); + Y.add(Z); + Z.getSubimageContent(cx, 2 * cx - 1, cy, 2 * cy - 1, image); + Z.FFT2D(); + Z.dyadicUpsample(); + Z.multiply(G3, l); + Y.add(Z); + l /= 2; + cx *= 2; + cy *= 2; + } + if (param.prefilter) { + Y.divide(P); + } + Y.iFFT2D(); + image.copyImageContent(Y); + } + + /** + * Returns the result of fully redundant pyramid analysis of ComplexImage + * and all the intermediary lowpass subbands. + * + * @param image + * the image to transform + * @return in [0][] the array of dyadic transform subbands, and in [1][] the + * intermidiary lowpass subbands + */ + + public ComplexImage[][] dyadicAnalysesRedundantLowpass(ComplexImage image) { + ComplexImage[] lowpassSubbands = new ComplexImage[J + 1]; + ComplexImage[] array = new ComplexImage[J + 1]; + ComplexImage H = FA[0].copyImage(); + ComplexImage G = FA[1].copyImage(); + G.multiply(0.5 * sqrt2); + ComplexImage X = image.copyImage(); + X.FFT2D(); + if (param.prefilter) { + X.multiply(P); + } + for (int j = 1, l = 1; j <= J; j++) { + ComplexImage Yh = X.copyImage(); + Yh.multiplyCircular(G, l); + // Put Yh in stack + Yh.iFFT2D(); + array[j - 1] = Yh; + ComplexImage Yl = X; + Yl.multiplyCircular(H, l); + lowpassSubbands[j - 1] = Yl.copyImage(); + lowpassSubbands[j - 1].iFFT2D(); + X = Yl; + l *= 2; + } + // Put remaining lowpass in stack + X.iFFT2D(); + array[J] = X; + lowpassSubbands[J] = X.copyImage(); + G.multiply(sqrt2); + ComplexImage[][] out = new ComplexImage[2][]; + out[0] = array; + out[1] = lowpassSubbands; + return out; + } + + /** + * Returns the result of fully redundant dyadic analysis of ComplexImage. + * + * @param image + * the ComplexImage to transform + * @return the array of dyadic transform subbands + */ + + public ComplexImage[] dyadicAnalysesRedundant(ComplexImage image) { + ComplexImage[] array = new ComplexImage[J + 1]; + ComplexImage H = FA[0].copyImage(); + ComplexImage G = FA[1].copyImage(); + G.multiply(0.5 * sqrt2); + ComplexImage X = image.copyImage(); + X.FFT2D(); + if (param.prefilter) { + X.multiply(P); + } + for (int j = 1, l = 1; j <= J; j++) { + ComplexImage Yh = X.copyImage(); + Yh.multiplyCircular(G, l); + // Put Yh in stack + Yh.iFFT2D(); + array[j - 1] = Yh; + ComplexImage Yl = X; + Yl.multiplyCircular(H, l); + X = Yl; + l *= 2; + } + // Put remaining lowpass in stack + X.iFFT2D(); + array[J] = X; + G.multiply(sqrt2); + return array; + } + + /** + * Returns the result of fully redundant dyadic synthesis. + * + * @param array + * subbands of dyadic pyramid transform + * @return the result of synthesis + */ + + public ComplexImage dyadicSynthesisRedundant(ComplexImage[] array) { + ComplexImage H = FS[0].copyImage(); + ComplexImage G = FS[1].copyImage(); + G.add(FS[2]); + G.add(FS[3]); + G.multiply(0.25 * sqrt2); + H.multiply(0.25); + if (param.flavor == param.MARR) { + G.divide(filters.ac); + } + int l = 1; + for (int k = 1; k < J; k++, l *= 2) + ; + ComplexImage Y = array[J].copyImage(); // Lowpass + Y.FFT2D(); + for (int j = J; j > 0; j--) { + ComplexImage Z = array[j - 1].copyImage(); + Z.FFT2D(); + Y.multiplyCircular(H, l); + Z.multiplyCircular(G, l); + l /= 2; + Y.add(Z); + } + if (param.prefilter) { + Y.divide(P); + } + Y.iFFT2D(); + return Y; + } + + /** + * Returns the result of dyadic pyramid analysis of ComplexImage. + * + * @param image + * the image to transform + * @return the array of dyadic transform subbands + */ + + public ComplexImage[] dyadicAnalysesPyramid(ComplexImage image) { + ComplexImage[] array = new ComplexImage[J + 1]; + ComplexImage H = FA[0]; + ComplexImage G = FA[1]; + H.multiply(0.25); + G.multiply(0.5 * sqrt2); + ComplexImage X = image.copyImage(); + X.FFT2D(); + if (param.prefilter) { + X.multiply(P); + } + for (int j = 1, l = 1; j <= J; j++) { + ComplexImage Yh = X.copyImage(); + Yh.multiply(G, l); + // Put Yh in stack + Yh.iFFT2D(); + array[j - 1] = Yh; + ComplexImage Yl = X; + Yl.multiply(H, l); + l *= 2; + Yl.dyadicDownUpCrop(); + X = Yl; + } + // Put remaining lowpass in stack + X.iFFT2D(); + array[J] = X; + H.multiply(4.0); + G.multiply(sqrt2); + return array; + } + + /** + * Returns the result of dyadic pyramid analysis of ComplexImage and all the + * intermediary lowpass subbands. + * + * @param image + * the image to transform + * @return in [0][] the array of dyadic transform subbands, and in [1][] the + * intermidiary lowpass subbands + */ + + public ComplexImage[][] dyadicAnalysesPyramidLowpass(ComplexImage image) { + ComplexImage[] lowpassSubbands = new ComplexImage[J + 1]; + ComplexImage[] array = new ComplexImage[J + 1]; + ComplexImage H = FA[0]; + ComplexImage G = FA[1]; + H.multiply(0.25); + G.multiply(0.5 * sqrt2); + ComplexImage X = image.copyImage(); + // ComplexImage Yh=new ComplexImage(image.nx,image.ny); + X.FFT2D(); + if (param.prefilter) { + X.multiply(P); + } + for (int j = 1, l = 1; j <= J; j++) { + ComplexImage Yh = new ComplexImage(image.nx, image.ny); + Yh.copyImageContent(X); + Yh.multiply(G, l); + // Put Yh in stack + Yh.iFFT2D(); + array[j - 1] = Yh; + ComplexImage Yl = X; + Yl.multiply(H, l); + lowpassSubbands[j - 1] = Yl.copyImage(); + lowpassSubbands[j - 1].iFFT2D(); + l *= 2; + Yl.dyadicDownUpCrop(); + X = Yl; + } + // Put remaining lowpass in stack + X.iFFT2D(); + array[J] = X; + lowpassSubbands[J] = X.copyImage(); + // H.multiply(4.0); + // G.multiply(sqrt2); + ComplexImage[][] out = new ComplexImage[2][]; + out[0] = array; + out[1] = lowpassSubbands; + return out; + } + + /** + * Returns the result of pyramid dyadic synthesis of array. + * + * @param array + * subbands of dyadic pyramid transform + * @return the result of synthesis + */ + + public ComplexImage dyadicSynthesisPyramid(ComplexImage[] array) { + FS[1].multiply(0.5); + FS[2].multiply(0.5); + FS[3].multiply(0.5); + ComplexImage Gconj = FA[1].copyImage(); + Gconj.multiply(1 / sqrt2); + ComplexImage[] G0 = FP; + // Reconstruct with calculated filters G0, FS, Gconj + int l = 1; + for (int i = 0; i < J; i++, l *= 2) + ; + ComplexImage Y1 = new ComplexImage(array[0].nx, array[0].ny); + ComplexImage Y2 = new ComplexImage(array[0].nx, array[0].ny); + ComplexImage GY = new ComplexImage(array[0].nx, array[0].ny); + Y1.copyImageContent(array[J]); + Y1.FFT2D(); + for (int j = J - 1; j >= 0; j--) { + Y2.copyImageContent(array[j]); + Y2.FFT2D(); + l /= 2; + Y1.dyadicUpsample(); + Y1.multiply(FS[0], l); + GY.copyImageContent(Y1); + GY.multiply(Gconj, l); + ComplexImage Y21 = Y2; + Y21.subtract(GY); + GY.copyImageContent(Y21); + GY.multiply(G0[1], l); + GY.dyadicDownUp(); + GY.multiply(FS[2], l); + Y1.add(GY); + GY.copyImageContent(Y21); + GY.multiply(G0[2], l); + GY.dyadicDownUp(); + GY.multiply(FS[3], l); + Y1.add(GY); + Y21.multiply(G0[0], l); + Y21.dyadicDownUp(); + Y21.multiply(FS[1], l); + Y1.add(Y21); + } + if (param.prefilter) { + Y1.divide(P); + } + Y1.iFFT2D(); + FS[1].multiply(2.0); + FS[2].multiply(2.0); + FS[3].multiply(2.0); + return Y1; + } + + /** + * Prepares the real dyadic pyramid transform coeffitients for being + * displayed. Rescales the subbands for visualisation and puts all subbands + * in one ComplexImage. + * + * @param array + * subbands of dyadic pyramid transform + * @param back + * background color + * @param rescale + * if rescale=false there is no rescaling + * @param lp + * if lp=false the lowpass subband is not displayed + * @return the image to display + */ + + public ComplexImage displayDyadicPyramidReal(ComplexImage[] array, double back, boolean rescale, boolean lp) { + int J1 = J; + if (!lp) { + J1 -= 1; + } + int nx = array[0].nx; + int ny = array[0].ny; + ComplexImage disp = new ComplexImage(nx, 2 * ny); + disp.settoConstant(back, back); + int x = 0; + int y = 0; + int dx = nx / 4; + int dy = ny; + for (int j = 0; j <= J1; j++) { + ComplexImage temp = array[j].copyImage(); + if (rescale) { + temp.stretch(); + } + temp.frame(back); + disp.putSubimage(x, y, temp); + x += dx; + y += dy; + dx /= 2; + dy /= 2; + } + return disp; + } + + /** + * Prepares the dyadic pyramid transform coeffitients for being displayed. + * + * @param array + * subbands of dyadic pyramid transform + * @param back + * background color + * @param rescale + * if rescale=false there is no rescaling + * @param lp + * if lp=false the lowpass subband is not displayed + * @return the image to display + */ + + public ComplexImage displayDyadicPyramid(ComplexImage[] array, double back, boolean rescale, boolean lp) { + int J1 = J; + if (!lp) { + J1 -= 1; + } + int nx = array[0].nx; + int ny = array[0].ny; + ComplexImage disp = new ComplexImage(2 * nx, 2 * ny); + disp.settoConstant(back, back); + int x = 0; + int y = 0; + int dx = nx / 2; + int dy = ny; + for (int j = 0; j <= J1; j++) { + ComplexImage temp = array[j].copyImage(); + if (rescale) { + temp.stretch(); + } + temp.frame(back, back); + + disp.putSubimage(x, y, temp); + System.arraycopy(temp.imag, 0, temp.real, 0, temp.nxy); + disp.putSubimage(x + temp.nx, y, temp); + disp.setImagtoZero(); + x += dx; + y += dy; + dx /= 2; + dy /= 2; + } + return disp; + } + + /** + * Prepares a nonredundant dyadic transform for being displayed, stretches + * each subband. + * + * @param image + * the basis transform coefficients + * @return the image with rescaled subbands to display + */ + + public ComplexImage displayBasis(ComplexImage image) { + int dx = image.nx; + int dy = image.ny; + ComplexImage out = new ComplexImage(dx, dy); + ComplexImage sub; + for (int j = 1; j <= J; j++) { + sub = image.getSubimage(dx / 2, dx - 1, 0, dy / 2 - 1); + sub.stretch(); + out.putSubimage(dx / 2, 0, sub); + sub.getSubimageContent(dx / 2, dx / 2 + sub.nx - 1, dy / 2, dy / 2 + sub.ny - 1, image); + sub.stretch(); + out.putSubimage(dx / 2, dy / 2, sub); + sub.getSubimageContent(0, sub.nx - 1, dy / 2, dy / 2 + sub.ny - 1, image); + sub.stretch(); + out.putSubimage(0, dy / 2, sub); + dx /= 2; + dy /= 2; + } + sub = image.getSubimage(0, dx - 1, 0, dy - 1); + sub.stretch(); + out.putSubimage(0, 0, sub); + return out; + } +} \ No newline at end of file diff --git a/src/bilib/src/polyharmonicwavelets/FFT1D.java b/src/bilib/src/polyharmonicwavelets/FFT1D.java new file mode 100644 index 0000000..d2458f7 --- /dev/null +++ b/src/bilib/src/polyharmonicwavelets/FFT1D.java @@ -0,0 +1 @@ +package polyharmonicwavelets; import ij.*; /** * Performs a 1D FFT. * <hr> * <p> * <b>Plugin of ImageJ:</b><br> * Fractional Spline Wavelet<br> * * <p> * <b>Authors:</b><br> * Gil Gaillard, Michael Liebling, Daniel Sage, Dimitri Van De Ville <a * href="mailto:daniel.sage@epfl.ch?subject=Fractional Spline Wavelet Plugin" * >daniel.sage@epfl.ch</a><br> * Swiss Federal Institute of Technology Lausanne, Biomedical Imaging Group, * CH-1015 Lausanne, Switzerland, <a * href="http://bigwww.epfl.ch">http://bigwww.epfl.ch</a><br> * * <p> * <b>Version:</b><br> * April 2003<br> * * <p> * <b>Copyright</b><br> * Copyright � 2003, Swiss Federal Institute of Technology, Lausanne, * Switzerland, (EPFL)<br> * * <hr> * * <p> * <b>Purpose of the class:</b><br> * Perform a 1D FFT. */ public class FFT1D { private boolean radix2 = true; private double Rearg[]; private double Imarg[]; private double[] yReOut; private double[] yImOut; public FFT1D(int size) { int m = 1; int size1 = size; double fact; double arg; while (size1 > 2) { size1 /= 2; m++; } if ((int) Math.round(Math.pow(2, m)) == size) { radix2 = true; n = 1 << m; fact = 2.0 * Math.PI / (double) n; Imarg = new double[n]; Rearg = new double[n]; // compute W coefficients for (int i = 0; i < n; i++) { arg = fact * (double) i; Rearg[i] = Math.cos(arg); Imarg[i] = -Math.sin(arg); } } else { radix2 = false; maxPrimeFactor = /* 65537; */(int) ((double) (size + 1)); maxPrimeFactorDiv2 = (maxPrimeFactor + 1) / 2;// (int)((double)(maxPrimeFactor+1)/2); twiddleRe = new double[maxPrimeFactor]; twiddleIm = new double[maxPrimeFactor]; trigRe = new double[maxPrimeFactor]; trigIm = new double[maxPrimeFactor]; zRe = new double[maxPrimeFactor]; zIm = new double[maxPrimeFactor]; vRe = new double[maxPrimeFactorDiv2]; vIm = new double[maxPrimeFactorDiv2]; wRe = new double[maxPrimeFactorDiv2]; wIm = new double[maxPrimeFactorDiv2]; yReOut = new double[size]; yImOut = new double[size]; // Math.pi = 4*Math.atan(1); n = size; sofarRadix = new int[maxFactorCount]; actualRadix = new int[maxFactorCount]; remainRadix = new int[maxFactorCount]; transTableSetup(sofarRadix, actualRadix, remainRadix); } } /** * Select the algorithm to perform the FFT1D, Cooley-Tukey or Mix. * * @param Re * real part of the input signal * @param Im * imaginary part of the input signal * @param size * length of the FFT * @param shift * set the start of the FFT */ final public void transform(double Re[], double Im[], int size, int shift) // Call // this // function // for // FFT! { n = size; if (radix2) { doFFT1D_CooleyTukey(Re, Im, size, shift); } else { if (shift == 0) doFFT_Mix(Re, Im, size); else doFFT_Mix(Re, Im, size, shift); } } /** * Select the algorithm to perform the Inverse FFT1D, Cooley-Tukey or Mix. * * @param Re * real part of the input signal * @param Im * imaginary part of the input signal * @param size * length of the IFFT * @param shift * set the start of the IFFT */ final public void inverse(double Re[], double Im[], int size, int shift) // Call // this // for // inverse // FFT! { n = size; if (radix2) { doIFFT1D_CooleyTukey(Re, Im, size, shift); } else { if (shift == 0) doIFFT_Mix(Re, Im, size); else doIFFT_Mix(Re, Im, size, shift); } } /** * Perform the FFT1D. * * There are two algorithms, the first for power of two length is a * Cooley-Tukey algorithm, the second for all size has been downloaded from * the Web (Mixfft.java). These are used to transform rows or columns in the * wavelet transform. * * @param Re * real part of the input signal * @param Im * imaginary part of the input signal * @param size * length of the FFT * @param shift * set the start of the FFT */ private void doFFT1D_CooleyTukey(double Re[], double Im[], int size, int shift) { int m = 1; int size1 = size; while (size1 > 2) { size1 /= 2; m++; } double Retmp, Imtmp; int i, j, k, stepsize, shifter; int i_j, i_j_s; // bit inversion for (i = j = shift; i < shift + n - 1; i++) { if (i < j) { Retmp = Re[i]; Imtmp = Im[i]; Re[i] = Re[j]; Im[i] = Im[j]; Re[j] = Retmp; Im[j] = Imtmp; } k = n >> 1; while (k + shift <= j) { j -= k; k /= 2; } j += k; } // Perform the FFT for (stepsize = 1, shifter = m - 1; stepsize < n; stepsize <<= 1, --shifter) { for (j = shift; j < shift + n; j += stepsize << 1) { for (i = 0; i < stepsize; i++) { i_j = i + j; i_j_s = i_j + stepsize; if (i > 0) { Retmp = Rearg[i << shifter] * Re[i_j_s] - Imarg[i << shifter] * Im[i_j_s]; Im[i_j_s] = Rearg[i << shifter] * Im[i_j_s] + Imarg[i << shifter] * Re[i_j_s]; Re[i_j_s] = Retmp; } Retmp = Re[i_j] - Re[i_j_s]; Imtmp = Im[i_j] - Im[i_j_s]; Re[i_j] += Re[i_j_s]; Im[i_j] += Im[i_j_s]; Re[i_j_s] = Retmp; Im[i_j_s] = Imtmp; } } } } /** * Perform the IFFT1D. * * Same algorithms as these used for the FFT. * * @param Re * real part of the input signal * @param Im * imaginary part of the input signal * @param size * length of the IFFT * @param shift * set the start of the IFFT */ private void doIFFT1D_CooleyTukey(double Re[], double Im[], final int size, int shift) { for (int i = shift; i < shift + size; i++) { Im[i] = -Im[i]; } transform(Re, Im, size, shift); for (int i = shift; i < shift + size; i++) { Re[i] = Re[i] / size; /* Output is in Re */ Im[i] = -Im[i] / size; } } /* * fft(int n, double xRe[], double xIm[], double yRe[], double yIm[]) * ------------------------------------------------------------------------ * NOTE : This is copyrighted material, Not public domain. See below. * ------------------------------------------------------------------------ * Input/output: int n transformation length. double xRe[] real part of * input sequence. double xIm[] imaginary part of input sequence. double * yRe[] real part of output sequence. double yIm[] imaginary part of output * sequence. * ------------------------------------------------------------------------ * Function: The procedure performs a fast discrete Fourier transform (FFT) * of a complex sequence, x, of an arbitrary length, n. The output, y, is * also a complex sequence of length n. * * y[k] = sum(x[m]*exp(-i*2*Math.pi*k*m/n), m=0..(n-1)), k=0,...,(n-1) * * The largest prime factor of n must be less than or equal to the constant * maxPrimeFactor defined below. * ------------------------------------------------------------------------ * Author: Jens Joergen Nielsen For non-commercial use only. Bakkehusene 54 * A $100 fee must be paid if used DK-2970 Hoersholm commercially. Please * contact. DENMARK * * E-mail : jjn@get2net.dk All rights reserved. October 2000. Homepage : * http://home.get2net.dk/jjn * ------------------------------------------------------------------------ * Implementation notes: The general idea is to factor the length of the * DFT, n, into factors that are efficiently handled by the routines. * * A number of short DFT's are implemented with a minimum of arithmetical * operations and using (almost) straight line code resulting in very fast * execution when the factors of n belong to this set. Especially radix-10 * is optimized. * * Prime factors, that are not in the set of short DFT's are handled with * direct evaluation of the DFP expression. * * Please report any problems to the author. Suggestions and improvements * are welcomed. * ------------------------------------------------------------------------ * Benchmarks: The Microsoft Visual C++ comMath.piler was used with the * following comMath.pile options: /nologo /Gs /G2 /W4 /AH /Ox /D "NDEBUG" * /D "_DOS" /FR and the FFTBENCH test executed on a 50MHz 486DX : * * Length Time [s] Accuracy [dB] * * 128 0.0054 -314.8 256 0.0116 -309.8 512 0.0251 -290.8 1024 0.0567 -313.6 * 2048 0.1203 -306.4 4096 0.2600 -291.8 8192 0.5800 -305.1 100 0.0040 * -278.5 200 0.0099 -280.3 500 0.0256 -278.5 1000 0.0540 -278.5 2000 0.1294 * -280.6 5000 0.3300 -278.4 10000 0.7133 -278.5 * ------------------------------------------------------------------------ * The following procedures are used : factorize : factor the transformation * length. transTableSetup : setup table with sofar-, actual-, and * remainRadix. permute : permutation allows in-place calculations. * twiddleTransf : twiddle multiplications and DFT's for one stage. initTrig * : initialise sine/cosine table. fft_4 : length 4 DFT, a la Nussbaumer. * fft_5 : length 5 DFT, a la Nussbaumer. fft_10 : length 10 DFT using prime * factor FFT. fft_odd : length n DFT, n odd. * *********************************************************************** */ private int maxPrimeFactor; private int maxPrimeFactorDiv2; private int maxFactorCount = 20; private int n, nFactor; private final double c3_1 = -1.5000000000000E+00; // c3_1 = // cos(2*Math.pi/3)-1; private final double c3_2 = 8.6602540378444E-01; // c3_2 = // sin(2*Math.pi/3); private final double u5 = 1.2566370614359E+00; // u5 = // 2*Math.pi/5; private final double c5_1 = -1.2500000000000E+00; // c5_1 = // (cos(u5)+cos(2*u5))/2-1; private final double c5_2 = 5.5901699437495E-01; // c5_2 = // (cos(u5)-cos(2*u5))/2; private final double c5_3 = -9.5105651629515E-01; // c5_3 = // -sin(u5); private final double c5_4 = -1.5388417685876E+00; // c5_4 = // -(sin(u5)+sin(2*u5)); private final double c5_5 = 3.6327126400268E-01; // c5_5 = // (sin(u5)-sin(2*u5)); private final double c8 = 7.0710678118655E-01; // c8 = // 1/sqrt(2); // private double Math.pi; private int groupOffset, dataOffset, blockOffset, adr; private int groupNo, dataNo, blockNo, twNo; private double omega, tw_re, tw_im; private double[] twiddleRe; private double[] twiddleIm; private double[] trigRe; private double[] trigIm; private double[] zRe; private double[] zIm; private double[] vRe; private double[] vIm; private double[] wRe; private double[] wIm; private int[] sofarRadix; private int[] actualRadix; private int[] remainRadix; /** * */ private void factorize(int fact[], int num) { int i, j, k; int nRadix; int[] radices = new int[7]; int[] factors = new int[maxFactorCount]; nRadix = 6; radices[1] = 2; radices[2] = 3; radices[3] = 4; radices[4] = 5; radices[5] = 8; radices[6] = 10; if (num == 1) { j = 1; factors[1] = 1; } else j = 0; i = nRadix; while ((num > 1) && (i > 0)) { if ((num % radices[i]) == 0) { num = num / radices[i]; j = j + 1; factors[j] = radices[i]; } else i = i - 1; } if (factors[j] == 2) /* substitute factors 2*8 with 4*4 */ { i = j - 1; while ((i > 0) && (factors[i] != 8)) i--; if (i > 0) { factors[j] = 4; factors[i] = 4; } } if (num > 1) { for (k = 2; k < Math.sqrt(num) + 1; k++) while ((num % k) == 0) { num = num / k; j = j + 1; factors[j] = k; } if (num > 1) { j = j + 1; factors[j] = num; } } for (i = 1; i <= j; i++) { fact[i] = factors[j - i + 1]; } nFactor = j; } /** * After N is factored the parameters that control the stages are generated. * * @param sofar * the product of the radices so far. * @param actual * : the radix handled in this stage. * @param remain * : the product of the remaining radices. */ final private void transTableSetup(int sofar[], int actual[], int remain[]) { int i; factorize(actual, n); if (actual[1] > maxPrimeFactor) { System.out.println("\nPrime factor of FFT length too large : %6d" + actual[1]); System.out.println("\nPlease modify the value of maxPrimeFactor in mixfft.c"); } remain[0] = n; sofar[1] = 1; remain[1] = n / actual[1]; for (i = 2; i <= nFactor; i++) { sofar[i] = sofar[i - 1] * actual[i - 1]; remain[i] = remain[i - 1] / actual[i]; } } /** * The sequence y is the permuted input sequence x so that the following * transformations can be performed in-place, and the final result is the * normal degree. */ final private void permute(int fact[], int remain[], double xRe[], double xIm[], double yRe[], double yIm[]) { int i, j = 0, k; int[] count = new int[maxFactorCount]; for (i = 1; i <= nFactor; i++) count[i] = 0; k = 0; for (i = 0; i <= n - 2; i++) { yRe[i] = xRe[k]; yIm[i] = xIm[k]; j = 1; k = k + remain[j]; count[1] = count[1] + 1; while (count[j] >= fact[j]) { count[j] = 0; k = k - remain[j - 1] + remain[j + 1]; j = j + 1; count[j] = count[j] + 1; } } yRe[n - 1] = xRe[n - 1]; yIm[n - 1] = xIm[n - 1]; } /* * Twiddle factor multiplications and transformations are performed on a * group of data. The number of multiplications with 1 are reduced by * skipMath.ping the twiddle multiplication of the first stage and of the * first group of the following stages. */ final private void initTrig(final int radix) { int i; double w, xre, xim; w = 2 * Math.PI / radix; trigRe[0] = 1; trigIm[0] = 0; xre = Math.cos(w); xim = -Math.sin(w); trigRe[1] = xre; trigIm[1] = xim; for (i = 2; i < radix; i++) { trigRe[i] = xre * trigRe[i - 1] - xim * trigIm[i - 1]; trigIm[i] = xim * trigRe[i - 1] + xre * trigIm[i - 1]; } } /** * */ private void fft_4(double aRe[], double aIm[]) { double t1_re, t1_im, t2_re, t2_im; double m2_re, m2_im, m3_re, m3_im; t1_re = aRe[0] + aRe[2]; t1_im = aIm[0] + aIm[2]; t2_re = aRe[1] + aRe[3]; t2_im = aIm[1] + aIm[3]; m2_re = aRe[0] - aRe[2]; m2_im = aIm[0] - aIm[2]; m3_re = aIm[1] - aIm[3]; m3_im = aRe[3] - aRe[1]; aRe[0] = t1_re + t2_re; aIm[0] = t1_im + t2_im; aRe[2] = t1_re - t2_re; aIm[2] = t1_im - t2_im; aRe[1] = m2_re + m3_re; aIm[1] = m2_im + m3_im; aRe[3] = m2_re - m3_re; aIm[3] = m2_im - m3_im; } /** * */ private void fft_5(double aRe[], double aIm[]) { double t1_re, t1_im, t2_re, t2_im, t3_re, t3_im; double t4_re, t4_im, t5_re, t5_im; double m2_re, m2_im, m3_re, m3_im, m4_re, m4_im; double m1_re, m1_im, m5_re, m5_im; double s1_re, s1_im, s2_re, s2_im, s3_re, s3_im; double s4_re, s4_im, s5_re, s5_im; t1_re = aRe[1] + aRe[4]; t1_im = aIm[1] + aIm[4]; t2_re = aRe[2] + aRe[3]; t2_im = aIm[2] + aIm[3]; t3_re = aRe[1] - aRe[4]; t3_im = aIm[1] - aIm[4]; t4_re = aRe[3] - aRe[2]; t4_im = aIm[3] - aIm[2]; t5_re = t1_re + t2_re; t5_im = t1_im + t2_im; aRe[0] = aRe[0] + t5_re; aIm[0] = aIm[0] + t5_im; m1_re = c5_1 * t5_re; m1_im = c5_1 * t5_im; m2_re = c5_2 * (t1_re - t2_re); m2_im = c5_2 * (t1_im - t2_im); m3_re = -c5_3 * (t3_im + t4_im); m3_im = c5_3 * (t3_re + t4_re); m4_re = -c5_4 * t4_im; m4_im = c5_4 * t4_re; m5_re = -c5_5 * t3_im; m5_im = c5_5 * t3_re; s3_re = m3_re - m4_re; s3_im = m3_im - m4_im; s5_re = m3_re + m5_re; s5_im = m3_im + m5_im; s1_re = aRe[0] + m1_re; s1_im = aIm[0] + m1_im; s2_re = s1_re + m2_re; s2_im = s1_im + m2_im; s4_re = s1_re - m2_re; s4_im = s1_im - m2_im; aRe[1] = s2_re + s3_re; aIm[1] = s2_im + s3_im; aRe[2] = s4_re + s5_re; aIm[2] = s4_im + s5_im; aRe[3] = s4_re - s5_re; aIm[3] = s4_im - s5_im; aRe[4] = s2_re - s3_re; aIm[4] = s2_im - s3_im; } /** * */ private void fft_8() { double[] aRe = new double[4]; double[] aIm = new double[4]; double[] bRe = new double[4]; double[] bIm = new double[4]; double gem; aRe[0] = zRe[0]; bRe[0] = zRe[1]; aRe[1] = zRe[2]; bRe[1] = zRe[3]; aRe[2] = zRe[4]; bRe[2] = zRe[5]; aRe[3] = zRe[6]; bRe[3] = zRe[7]; aIm[0] = zIm[0]; bIm[0] = zIm[1]; aIm[1] = zIm[2]; bIm[1] = zIm[3]; aIm[2] = zIm[4]; bIm[2] = zIm[5]; aIm[3] = zIm[6]; bIm[3] = zIm[7]; fft_4(aRe, aIm); fft_4(bRe, bIm); gem = c8 * (bRe[1] + bIm[1]); bIm[1] = c8 * (bIm[1] - bRe[1]); bRe[1] = gem; gem = bIm[2]; bIm[2] = -bRe[2]; bRe[2] = gem; gem = c8 * (bIm[3] - bRe[3]); bIm[3] = -c8 * (bRe[3] + bIm[3]); bRe[3] = gem; zRe[0] = aRe[0] + bRe[0]; zRe[4] = aRe[0] - bRe[0]; zRe[1] = aRe[1] + bRe[1]; zRe[5] = aRe[1] - bRe[1]; zRe[2] = aRe[2] + bRe[2]; zRe[6] = aRe[2] - bRe[2]; zRe[3] = aRe[3] + bRe[3]; zRe[7] = aRe[3] - bRe[3]; zIm[0] = aIm[0] + bIm[0]; zIm[4] = aIm[0] - bIm[0]; zIm[1] = aIm[1] + bIm[1]; zIm[5] = aIm[1] - bIm[1]; zIm[2] = aIm[2] + bIm[2]; zIm[6] = aIm[2] - bIm[2]; zIm[3] = aIm[3] + bIm[3]; zIm[7] = aIm[3] - bIm[3]; } /** * */ private void fft_10() { double[] aRe = new double[5]; double[] aIm = new double[5]; double[] bRe = new double[5]; double[] bIm = new double[5]; aRe[0] = zRe[0]; bRe[0] = zRe[5]; aRe[1] = zRe[2]; bRe[1] = zRe[7]; aRe[2] = zRe[4]; bRe[2] = zRe[9]; aRe[3] = zRe[6]; bRe[3] = zRe[1]; aRe[4] = zRe[8]; bRe[4] = zRe[3]; aIm[0] = zIm[0]; bIm[0] = zIm[5]; aIm[1] = zIm[2]; bIm[1] = zIm[7]; aIm[2] = zIm[4]; bIm[2] = zIm[9]; aIm[3] = zIm[6]; bIm[3] = zIm[1]; aIm[4] = zIm[8]; bIm[4] = zIm[3]; fft_5(aRe, aIm); fft_5(bRe, bIm); zRe[0] = aRe[0] + bRe[0]; zRe[5] = aRe[0] - bRe[0]; zRe[6] = aRe[1] + bRe[1]; zRe[1] = aRe[1] - bRe[1]; zRe[2] = aRe[2] + bRe[2]; zRe[7] = aRe[2] - bRe[2]; zRe[8] = aRe[3] + bRe[3]; zRe[3] = aRe[3] - bRe[3]; zRe[4] = aRe[4] + bRe[4]; zRe[9] = aRe[4] - bRe[4]; zIm[0] = aIm[0] + bIm[0]; zIm[5] = aIm[0] - bIm[0]; zIm[6] = aIm[1] + bIm[1]; zIm[1] = aIm[1] - bIm[1]; zIm[2] = aIm[2] + bIm[2]; zIm[7] = aIm[2] - bIm[2]; zIm[8] = aIm[3] + bIm[3]; zIm[3] = aIm[3] - bIm[3]; zIm[4] = aIm[4] + bIm[4]; zIm[9] = aIm[4] - bIm[4]; } /** * */ private void fft_odd(int radix) { double rere, reim, imre, imim; int i, j, k, p, max; p = radix; max = (p + 1) / 2; for (j = 1; j < max; j++) { vRe[j] = zRe[j] + zRe[p - j]; vIm[j] = zIm[j] - zIm[p - j]; wRe[j] = zRe[j] - zRe[p - j]; wIm[j] = zIm[j] + zIm[p - j]; } for (j = 1; j < max; j++) { zRe[j] = zRe[0]; zIm[j] = zIm[0]; zRe[p - j] = zRe[0]; zIm[p - j] = zIm[0]; k = j; for (i = 1; i < max; i++) { rere = trigRe[k] * vRe[i]; imim = trigIm[k] * vIm[i]; reim = trigRe[k] * wIm[i]; imre = trigIm[k] * wRe[i]; zRe[p - j] += rere + imim; zIm[p - j] += reim - imre; zRe[j] += rere - imim; zIm[j] += reim + imre; k = k + j; if (k >= p) k = k - p; } } for (j = 1; j < max; j++) { zRe[0] = zRe[0] + vRe[j]; zIm[0] = zIm[0] + wIm[j]; } } /** * */ final private void twiddleTransf(int sofarRadix, int radix, int remainRadix, double yRe[], double yIm[]) { double cosw, sinw, gem; double t1_re, t1_im, t2_re, t2_im, t3_re, t3_im; double t4_re, t4_im, t5_re, t5_im; double m2_re, m2_im, m3_re, m3_im, m4_re, m4_im; double m1_re, m1_im, m5_re, m5_im; double s1_re, s1_im, s2_re, s2_im, s3_re, s3_im; double s4_re, s4_im, s5_re, s5_im; initTrig(radix); omega = 2 * Math.PI / (double) (sofarRadix * radix); cosw = Math.cos(omega); sinw = -Math.sin(omega); tw_re = 1.0; tw_im = 0; dataOffset = 0; groupOffset = dataOffset; adr = groupOffset; for (dataNo = 0; dataNo < sofarRadix; dataNo++) { if (sofarRadix > 1) { twiddleRe[0] = 1.0; twiddleIm[0] = 0.0; twiddleRe[1] = tw_re; twiddleIm[1] = tw_im; for (twNo = 2; twNo < radix; twNo++) { twiddleRe[twNo] = tw_re * twiddleRe[twNo - 1] - tw_im * twiddleIm[twNo - 1]; twiddleIm[twNo] = tw_im * twiddleRe[twNo - 1] + tw_re * twiddleIm[twNo - 1]; } gem = cosw * tw_re - sinw * tw_im; tw_im = sinw * tw_re + cosw * tw_im; tw_re = gem; } for (groupNo = 0; groupNo < remainRadix; groupNo++) { if ((sofarRadix > 1) && (dataNo > 0)) { zRe[0] = yRe[adr]; zIm[0] = yIm[adr]; blockNo = 1; do { adr = adr + sofarRadix; zRe[blockNo] = twiddleRe[blockNo] * yRe[adr] - twiddleIm[blockNo] * yIm[adr]; zIm[blockNo] = twiddleRe[blockNo] * yIm[adr] + twiddleIm[blockNo] * yRe[adr]; blockNo++; } while (blockNo < radix); } else for (blockNo = 0; blockNo < radix; blockNo++) { zRe[blockNo] = yRe[adr]; zIm[blockNo] = yIm[adr]; adr = adr + sofarRadix; } switch (radix) { case 2: gem = zRe[0] + zRe[1]; zRe[1] = zRe[0] - zRe[1]; zRe[0] = gem; gem = zIm[0] + zIm[1]; zIm[1] = zIm[0] - zIm[1]; zIm[0] = gem; break; case 3: t1_re = zRe[1] + zRe[2]; t1_im = zIm[1] + zIm[2]; zRe[0] = zRe[0] + t1_re; zIm[0] = zIm[0] + t1_im; m1_re = c3_1 * t1_re; m1_im = c3_1 * t1_im; m2_re = c3_2 * (zIm[1] - zIm[2]); m2_im = c3_2 * (zRe[2] - zRe[1]); s1_re = zRe[0] + m1_re; s1_im = zIm[0] + m1_im; zRe[1] = s1_re + m2_re; zIm[1] = s1_im + m2_im; zRe[2] = s1_re - m2_re; zIm[2] = s1_im - m2_im; break; case 4: t1_re = zRe[0] + zRe[2]; t1_im = zIm[0] + zIm[2]; t2_re = zRe[1] + zRe[3]; t2_im = zIm[1] + zIm[3]; m2_re = zRe[0] - zRe[2]; m2_im = zIm[0] - zIm[2]; m3_re = zIm[1] - zIm[3]; m3_im = zRe[3] - zRe[1]; zRe[0] = t1_re + t2_re; zIm[0] = t1_im + t2_im; zRe[2] = t1_re - t2_re; zIm[2] = t1_im - t2_im; zRe[1] = m2_re + m3_re; zIm[1] = m2_im + m3_im; zRe[3] = m2_re - m3_re; zIm[3] = m2_im - m3_im; break; case 5: t1_re = zRe[1] + zRe[4]; t1_im = zIm[1] + zIm[4]; t2_re = zRe[2] + zRe[3]; t2_im = zIm[2] + zIm[3]; t3_re = zRe[1] - zRe[4]; t3_im = zIm[1] - zIm[4]; t4_re = zRe[3] - zRe[2]; t4_im = zIm[3] - zIm[2]; t5_re = t1_re + t2_re; t5_im = t1_im + t2_im; zRe[0] = zRe[0] + t5_re; zIm[0] = zIm[0] + t5_im; m1_re = c5_1 * t5_re; m1_im = c5_1 * t5_im; m2_re = c5_2 * (t1_re - t2_re); m2_im = c5_2 * (t1_im - t2_im); m3_re = -c5_3 * (t3_im + t4_im); m3_im = c5_3 * (t3_re + t4_re); m4_re = -c5_4 * t4_im; m4_im = c5_4 * t4_re; m5_re = -c5_5 * t3_im; m5_im = c5_5 * t3_re; s3_re = m3_re - m4_re; s3_im = m3_im - m4_im; s5_re = m3_re + m5_re; s5_im = m3_im + m5_im; s1_re = zRe[0] + m1_re; s1_im = zIm[0] + m1_im; s2_re = s1_re + m2_re; s2_im = s1_im + m2_im; s4_re = s1_re - m2_re; s4_im = s1_im - m2_im; zRe[1] = s2_re + s3_re; zIm[1] = s2_im + s3_im; zRe[2] = s4_re + s5_re; zIm[2] = s4_im + s5_im; zRe[3] = s4_re - s5_re; zIm[3] = s4_im - s5_im; zRe[4] = s2_re - s3_re; zIm[4] = s2_im - s3_im; break; case 8: fft_8(); break; case 10: fft_10(); break; default: fft_odd(radix); break; } adr = groupOffset; for (blockNo = 0; blockNo < radix; blockNo++) { yRe[adr] = zRe[blockNo]; yIm[adr] = zIm[blockNo]; adr = adr + sofarRadix; } groupOffset = groupOffset + sofarRadix * radix; adr = groupOffset; } dataOffset = dataOffset + 1; groupOffset = dataOffset; adr = groupOffset; } } /* * Perform the FFT for all sizes of signal. */ private void doFFT_Mix(double xRe[], double xIm[], final int size) { // int[] sofarRadix = new int[maxFactorCount]; // int[] actualRadix = new int[maxFactorCount]; // int[] remainRadix = new int[maxFactorCount]; int count; // Mod // Math.pi = 4*Math.atan(1); n = size; transTableSetup(sofarRadix, actualRadix, remainRadix); permute(actualRadix, remainRadix, xRe, xIm, yReOut, yImOut); for (count = 1; count <= nFactor; count++) twiddleTransf(sofarRadix[count], actualRadix[count], remainRadix[count], yReOut, yImOut); // Copy results for (int i = 0; i < n; i++) { xRe[i] = yReOut[i]; xIm[i] = yImOut[i]; } } /* * Perform the FFT for all sizes of signal, the start and the length of the * FFT can be choosen to transform a part of a signal. */ private void doFFT_Mix(double xRe[], double xIm[], final int size, final int shift) { double[] tmp_xRe = new double[size]; double[] tmp_xIm = new double[size]; for (int i = 0; i < size; i++) { tmp_xRe[i] = xRe[i + shift]; tmp_xIm[i] = xIm[i + shift]; } doFFT_Mix(tmp_xRe, tmp_xIm, size); for (int i = 0; i < size; i++) { xRe[i + shift] = tmp_xRe[i]; xIm[i + shift] = tmp_xIm[i]; } } /* * Perform the IFFT for all sizes of signal. */ private void doIFFT_Mix(double xRe[], double xIm[], final int size) { for (int i = 0; i < size; i++) { xIm[i] = -xIm[i]; } doFFT_Mix(xRe, xIm, size); for (int i = 0; i < size; i++) { xRe[i] = xRe[i] / size; xIm[i] = -xIm[i] / size; } } /* * Perform the IFFT for all sizes of signal, the start and the length of the * IFFT can be choosen to transform a part of a signal. */ private void doIFFT_Mix(double xRe[], double xIm[], final int size, final int shift) { double[] tmp_xRe = new double[size]; double[] tmp_xIm = new double[size]; for (int i = 0; i < size; i++) { tmp_xRe[i] = xRe[i + shift]; tmp_xIm[i] = xIm[i + shift]; } doIFFT_Mix(tmp_xRe, tmp_xIm, size); for (int i = 0; i < size; i++) { xRe[i + shift] = tmp_xRe[i]; xIm[i + shift] = tmp_xIm[i]; } } } // end of class \ No newline at end of file diff --git a/src/bilib/src/polyharmonicwavelets/GammaFunction.java b/src/bilib/src/polyharmonicwavelets/GammaFunction.java new file mode 100644 index 0000000..aa2ec7e --- /dev/null +++ b/src/bilib/src/polyharmonicwavelets/GammaFunction.java @@ -0,0 +1,208 @@ + +//package dr.math; + +package polyharmonicwavelets; + +/** + * (This file is part of BEAST) Computes the Gamma function. + * <p> + * Copyright (C) 2002-2006 Alexei Drummond and Andrew Rambaut + * <p> + * This file is part of BEAST. See the NOTICE file distributed with this work + * for additional information regarding copyright ownership and licensing. + * <p> + * BEAST is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) any + * later version. + * <p> + * BEAST is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * <p> + * You should have received a copy of the GNU Lesser General Public License + * along with BEAST; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA<br> + * + * @author Korbinian Strimmer + * + */ +public class GammaFunction { + // + // Public stuff + // + + // Gamma function + + /** + * log Gamma function: ln(gamma(alpha)) for alpha>0, accurate to 10 decimal + * places + * + * @param alpha + * argument + * @return the log of the gamma function of the given alpha + */ + public static double lnGamma(double alpha) { + // Pike MC & Hill ID (1966) Algorithm 291: Logarithm of the gamma + // function. + // Communications of the Association for Computing Machinery, 9:684 + + double x = alpha, f = 0.0, z; + + if (x < 7) { + f = 1; + z = x - 1; + while (++z < 7) { + f *= z; + } + x = z; + f = -Math.log(f); + } + z = 1 / (x * x); + + return f + (x - 0.5) * Math.log(x) - x + 0.918938533204673 + (((-0.000595238095238 * z + 0.000793650793651) * z - 0.002777777777778) * z + 0.083333333333333) / x; + } + + /** + * Incomplete Gamma function Q(a,x) (a cleanroom implementation of Numerical + * Recipes gammq(a,x); in Mathematica this function is called + * GammaRegularized) + * + * @param a + * parameter + * @param x + * argument + * @return function value + */ + public static double incompleteGammaQ(double a, double x) { + return 1.0 - incompleteGamma(x, a, lnGamma(a)); + } + + /** + * Incomplete Gamma function P(a,x) = 1-Q(a,x) (a cleanroom implementation + * of Numerical Recipes gammp(a,x); in Mathematica this function is + * 1-GammaRegularized) + * + * @param a + * parameter + * @param x + * argument + * @return function value + */ + public static double incompleteGammaP(double a, double x) { + return incompleteGamma(x, a, lnGamma(a)); + } + + /** + * Incomplete Gamma function P(a,x) = 1-Q(a,x) (a cleanroom implementation + * of Numerical Recipes gammp(a,x); in Mathematica this function is + * 1-GammaRegularized) + * + * @param a + * parameter + * @param x + * argument + * @param lnGammaA + * precomputed lnGamma(a) + * @return function value + */ + public static double incompleteGammaP(double a, double x, double lnGammaA) { + return incompleteGamma(x, a, lnGammaA); + } + + /** + * Returns the incomplete gamma ratio I(x,alpha) where x is the upper limit + * of the integration and alpha is the shape parameter. + * + * @param x + * upper limit of integration + * @param alpha + * shape parameter + * @param ln_gamma_alpha + * the log gamma function for alpha + * @return the incomplete gamma ratio + */ + private static double incompleteGamma(double x, double alpha, double ln_gamma_alpha) { + // (1) series expansion if (alpha>x || x<=1) + // (2) continued fraction otherwise + // RATNEST FORTRAN by + // Bhattacharjee GP (1970) The incomplete gamma integral. Applied + // Statistics, + // 19: 285-287 (AS32) + + double accurate = 1e-8, overflow = 1e30; + double factor, gin, rn, a, b, an, dif, term; + double pn0, pn1, pn2, pn3, pn4, pn5; + + if (x == 0.0) { + return 0.0; + } + // System.out.println("x="+x+" alpha="+alpha); + if (x < 0.0 || alpha <= 0.0) { + throw new IllegalArgumentException("Arguments out of bounds"); + } + + factor = Math.exp(alpha * Math.log(x) - x - ln_gamma_alpha); + + if (x > 1 && x >= alpha) { + // continued fraction + a = 1 - alpha; + b = a + x + 1; + term = 0; + pn0 = 1; + pn1 = x; + pn2 = x + 1; + pn3 = x * b; + gin = pn2 / pn3; + + do { + a++; + b += 2; + term++; + an = a * term; + pn4 = b * pn2 - an * pn0; + pn5 = b * pn3 - an * pn1; + + if (pn5 != 0) { + rn = pn4 / pn5; + dif = Math.abs(gin - rn); + if (dif <= accurate) { + if (dif <= accurate * rn) { + break; + } + } + + gin = rn; + } + pn0 = pn2; + pn1 = pn3; + pn2 = pn4; + pn3 = pn5; + if (Math.abs(pn4) >= overflow) { + pn0 /= overflow; + pn1 /= overflow; + pn2 /= overflow; + pn3 /= overflow; + } + } + while (true); + gin = 1 - factor * gin; + } + else { + // series expansion + gin = 1; + term = 1; + rn = alpha; + do { + rn++; + term *= x / rn; + gin += term; + } + while (term > accurate); + gin *= factor / alpha; + } + return gin; + } + +} diff --git a/src/bilib/src/polyharmonicwavelets/Parameters.java b/src/bilib/src/polyharmonicwavelets/Parameters.java new file mode 100644 index 0000000..6ee099f --- /dev/null +++ b/src/bilib/src/polyharmonicwavelets/Parameters.java @@ -0,0 +1,139 @@ +package polyharmonicwavelets; + +// +// Parameters.java +// PolyharmonicWavelets +// +// Created by Biomedical Imaging Group on 2/13/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +/** + * This class stores all the parameters neded to perform the wavelet transform. + */ + +public class Parameters { + /** + * If true, only compute the analyses filters. + */ + public boolean analysesonly = false; + /** + * If rieszfreq=1 analysis wavelet filter will be multiplied by V2. Used for + * Riesz transform. No need to change this, it is not a user input. + */ + public int rieszfreq = 0; + /** + * Constant that defines the polyharmonic B-spline flavor. + */ + public final static int BSPLINE = 0; + /** + * Constant that defines the orthogonal flavor, quincunx only. + */ + public final static int ORTHOGONAL = 1; + /** + * Constant that defines the dual of the B-spline. + */ + public final static int DUAL = 2; + /** + * Constant that defines the operator wavelet. + */ + public final static int OPERATOR = 3; + /** + * Constant that defines the Marr wavelet. + */ + public final static int MARR = 7; + /** + * Constant that defines the dual of the operator. + */ + public final static int DUALOPERATOR = 8; + /** + * Defines the wavelet flavor: BSPLINE, ORTHOGONAL, DUAL, OPERATOR, MARR or + * DUALOPERATOR. + */ + public int flavor = ORTHOGONAL; + + /** + * Constant that defines the basis transform. + */ + public final static int BASIS = 0; + /** + * Constant that defines the fully redundant transform. + */ + public final static int REDUNDANT = 1; + /** + * Constant that defines the pyramid transform. + */ + public final static int PYRAMID = 2; + /** + * The redundancy, should be set to BASIS, PYRAMID, or REDUNDANT. + */ + public int redundancy = PYRAMID; + /** + * Constant that defines standard isotropic polyharmonic Bspline. + */ + public final static int ISOTROPIC = 1; + /** + * Constant that defines isotropic polyharmonic Bspline that allowes to + * change standard deviation. + */ + public final static int CHANGESIGMA = 4; + /** + * Te isotropy type. + */ + public int type = ISOTROPIC; + /** + * Defines the standard deviation of gaussian if type=changesigma + */ + public double s2 = 6.0; // only used if + // type=changesigma + /** + * Constant that defines the quincunx lattice. + */ + public final static int QUINCUNX = 0; + /** + * Constant that defines the dyadic lattice. + */ + public final static int DYADIC = 1; + /** + * The lattice type, set to QUINCUNX or DYADIC. + */ + public int lattice = DYADIC; + + /** + * This parameter defines whether the prefilter should be used (if true) or + * not (if false). + */ + public boolean prefilter = true; + + /** + * Constant that defines the iterative method for computing the + * autocorrelation. + */ + public final static int ITERATIVE = 0; + /** + * Constant that defines the gamma function method for computing the + * autocorrelation. + */ + public final static int GAMMA = 1; + /** + * This parameter defines the autocorrelation computation method, GAMMA or + * ITERATIVE. + */ + public int accompute = GAMMA; + + /** + * The B-spline order, gamma. + */ + public double order = 2.0; + + /** + * The iterate of a rotation covariant operator. Usually 0 for polyharmonic + * wavelets or 1 for Marr wavelets, but other values are also possible. + */ + public int N = 1; + + /** + * The number of wavelet decomposition levels. + */ + public int J = 1; +} diff --git a/src/bilib/src/polyharmonicwavelets/QuincunxFilters.java b/src/bilib/src/polyharmonicwavelets/QuincunxFilters.java new file mode 100644 index 0000000..3992040 --- /dev/null +++ b/src/bilib/src/polyharmonicwavelets/QuincunxFilters.java @@ -0,0 +1,505 @@ +package polyharmonicwavelets; + +// +// QuincunxFilters.java +// PolyharmonicWavelets +// +// Created by Biomedical Imaging Group on 2/13/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +import java.util.*; +import ij.*; +import java.text.DecimalFormat; + +/** + * This class computes the filters for the quincunx wavelet transform. + * + * @author Katarina Balac, EPFL. + */ + +public class QuincunxFilters { + + /** + * The analysis filters. FA[0] lowpass analysis filter for odd iteration + * FA[1] highpass analysis filter for odd iteration FA[2] lowpass analysis + * filter for even iteration FA[3] highpass analysis filter for even + * iteration + */ + public ComplexImage[] FA; + + /** + * The synthesis filters. FS[0] lowpass synthesis filter for odd iteration + * FS[1] highpass synthesis filter for odd iteration FS[2] lowpass synthesis + * filter for even iteration FS[3] highpass synthesis filter for even + * iteration + */ + public ComplexImage[] FS; + + /** + * The prefilter. + */ + public ComplexImage P; // Prefilter + private Parameters param; // Parameters for the + // transform + private int nx; // Size of filters + private int ny; + private final double PI2 = 2.0 * Math.PI; + private final double PI = Math.PI; + + /** + * Constructor, creates a QuincunxFilters objectand computes all the + * filters. + * + * @param par + * the wavelet transform parameters + * @param sizex + * the number of columns in the image to transform + * @param sizey + * the number of rows in the image to transform + */ + + public QuincunxFilters(Parameters par, int sizex, int sizey) { + param = par; + nx = sizex; + ny = sizey; + FA = new ComplexImage[4]; + FS = new ComplexImage[4]; + calculateFilters(); + } + + /* + * Performs linear interpolation on the real part of ComplexImage ac as + * ac[w]=ac[Dw], where D is the quincunx subsampling matrix and w is a two + * element column vector with w1 and w2 uniformly distributed from 0 to + * 2*PI, and ac[w1+2*k*PI,w2+2*n*PI]=ac[w1,w2] for all k and n integer. + */ + + private ComplexImage interpolateQuincunxReal(ComplexImage ac) { + ComplexImage out = new ComplexImage(nx, ny, ac.imag == null); + int nx1 = nx - 1; + int ny1 = ny - 1; + for (int cy = 0; cy < ny; cy++) { + for (int cx = 0; cx < nx; cx++) { + // calculate (x+y)mod(2pi),(y-x)mod(2pi) + double x = ((double) cx / (double) nx) * PI2; + double y = ((double) cy / (double) ny) * PI2; + double sum = x + y; + double dif = y - x; + // Find closest integers to x,y + double x1 = sum * (double) nx / PI2; + double y1 = dif * (double) ny / PI2; + double fx = Math.floor(x1); + double fy = Math.floor(y1); + x1 = x1 - fx; + y1 = y1 - fy; + int kx = (int) fx; + int ky = (int) fy; + while (ky > ny1) + ky -= ny; + while (ky < 0) + ky += ny; + while (kx > nx1) + kx -= nx; + while (kx < 0) + kx += nx; + int ky1 = ky + 1; + while (ky1 > ny1) + ky1 -= ny; + int kx1 = kx + 1; + while (kx1 > nx1) + kx1 -= nx; + double a = ac.real[nx * ky1 + kx1]; + double b = ac.real[nx * ky1 + kx]; + double c = ac.real[nx * ky + kx]; + double d = ac.real[nx * ky + kx1]; + double res = y1 * (x1 * a + (1.0 - x1) * b) + (1.0 - y1) * (x1 * d + (1.0 - x1) * c); + out.real[cy * nx + cx] = res; + } + } + return out; + } + + /* + * Returns the numenator of scaling function, localization support is [ minx + * : (maxx-minx)/sizex : maxx-(maxx-minx)/sizex, miny : (maxy-miny)/sizey : + * maxy-(maxy-miny)/sizey ] output is of size [sizex, sizey] and defined + * on[minx...maxx-eps,miny...maxy-eps] type=0, dyadic type=1, quincunx, + * computes V(D^tw) + */ + + private ComplexImage localization(int sizex, int sizey, double minx, double miny, double maxx, double maxy, double gama, int N, int type) { + ComplexImage result = new ComplexImage(sizex, sizey, N == 0); + double gama2 = gama / 2.0; + double epsx = PI2 / (5.0 * (double) sizex); + double epsy = PI2 / (5.0 * (double) sizey); + final double d83 = 8.0 / 3.0; + final double d23 = 2.0 / 3.0; + for (int ky = 0; ky < sizey; ky++) { + int kxy = ky * sizex; + double rx = (maxx - minx) / (double) sizex; + double ry = (maxy - miny) / (double) sizey; + for (int kx = 0, index = kxy; kx < sizex; kx++, index++) { + double y = miny + (double) ky * ry; + double x = minx + (double) kx * rx; + if (type == 1) { // quincunx + double xt = x; + double yt = y; + x = xt + yt; + y = yt - xt; + } + double y1 = y; + while (y1 >= Math.PI - epsy) + y1 = y1 - PI2; + while (y1 < -Math.PI - epsy) + y1 = y1 + PI2; + double x1 = x; + while (x1 >= Math.PI - epsx) + x1 = x1 - PI2; + while (x1 < -Math.PI - epsx) + x1 = x1 + PI2; + double a = 1.0; + // Compute modulus of localization depending on type + double sx = Math.sin(x / 2); + sx = sx * sx; + double sy = Math.sin(y / 2); + sy = sy * sy; + if (param.type == param.ISOTROPIC) { // Isotropic + a = 4.0 * (sx + sy) - d83 * (sx * sy); + } + if (param.type == param.CHANGESIGMA) { + final double sigma2 = param.s2; + final double b = -16.0 / sigma2; + final double c = 24.0 / (sigma2 * sigma2) - 16.0 / (3.0 * sigma2); + final double d = 8.0 / (sigma2 * sigma2) + 32.0 / 45.0 - 16.0 / (3.0 * sigma2); + final double e = 4.0 / 3.0 - 8.0 / sigma2; + a = 4.0 * (sx + sy) + b * (sx * sy) + c * (sx * sx * sy + sy * sy * sx) + d * (sx * sx * sx + sy * sy * sy) + e * (sx * sx + sy * sy); + } + double re = Math.pow(a, gama2); + double im = 0.0; + if (N > 0) { + for (int i = 0; i < N; i++) { + double re1 = re * x1 - im * y1; + double im1 = re * y1 + im * x1; + re = re1; + im = im1; + } + double t = Math.pow(x1 * x1 + y1 * y1, (double) N / 2.0); + if (t == 0.0) { + result.real[index] = 0.0; + result.imag[index] = 0.0; + } + else { + result.real[index] = re / t; + result.imag[index] = im / t; + } + } + else { + result.real[index] = re; + } + } + } + return result; + } + + /* + * Calculates denominator of scaling function support is [ 0 : maxx/sizex : + * maxx-maxx/sizex, 0 : maxy/sizey : maxy-maxy/sizey ] output is of size + * [sizex, sizey] and defined on[0...maxx-eps,0...maxy-eps] + */ + + private ComplexImage denominator(int sizex, int sizey, double minx, double miny, double maxx, double maxy, int N) { + ComplexImage result = new ComplexImage(sizex, sizey); + double gamaN2; + gamaN2 = (param.order - N) / 2.0; + for (int ky = 0; ky < sizey; ky++) { + int kxy = ky * sizex; + double y = miny + (double) ky * (maxy - miny) / (double) sizey; + for (int kx = 0, index = kxy; kx < sizex; kx++, index++) { + double x = minx + (double) kx * (maxx - minx) / (double) sizex; + double re = Math.pow(x * x + y * y, gamaN2); + double im = 0.0; + if (N > 0) { + for (int i = 0; i < N; i++) { + double re1 = re * x - im * y; + double im1 = re * y + im * x; + re = re1; + im = im1; + } + result.real[index] = re; + result.imag[index] = im; + } + else { + result.real[index] = re; + } + } + } + return result; + } + + /* + * Computes prefilter + */ + + private void quincunxPrefilter(ComplexImage ac) { + P = new ComplexImage(nx, ny, true); + P.settoConstant(1.0); + P = localization(nx, ny, -PI, -PI, PI, PI, param.order, 0, 0); + ComplexImage d = denominator(nx, ny, -PI, -PI, PI, PI, 0); + P.divide(d, 1.0, 0.0); + P.shift(); + if (param.flavor == param.ORTHOGONAL) { + ComplexImage acsqrt = ac.copyImage(); + acsqrt.rootReal(); + P.divide(acsqrt); + } + if ((param.flavor == param.BSPLINE) || (param.flavor == param.DUALOPERATOR)) { + P.divide(ac); + } + } + + /* + * Computesall the filters for odd iteration B-refinement filter for the + * quincunx lattice ortho-orthonormalizing factor ac - autocorrelation acD - + * sampled autocorrelation it-number of iteration if it=0 computes filters + * for odd iteration, if it=1 for even iteration H - analysis highpass H1 - + * synthesis highpass L1 - analysis lowpass L - synthesis lowpass if even, + * even iteration, else, odd iteration + */ + + private void computeLowpassHighpass(ComplexImage B, ComplexImage ac, ComplexImage acD, ComplexImage loc, boolean even) { + ComplexImage L = null; + ComplexImage L1 = null; + ComplexImage H = null; + ComplexImage H1 = null; + ComplexImage ortho = acD.copyImage(); + ortho.divide(ac); + final double sqrt2 = Math.sqrt(2.0); + B.multiply(sqrt2); + if (param.flavor == param.ORTHOGONAL) { + ComplexImage orthot = ortho; + orthot.rootReal(); + L1 = B; + L1.divide(orthot); + H = L1.copyImage(); + if (even) { + H.shift(); + } + else { + H.shiftY(); + } + if (!param.analysesonly) { + L = L1.copyImage(); + H1 = H.copyImage(); + H1.conj(); + } + L1.conj(); + } + if (param.flavor == param.DUAL) { + L1 = B.copyImage(); + if (even) { + ac.shift(); + B.shift(); + } + else { + ac.shiftY(); + B.shiftY(); + } + H = B.copyImage(); + H.conj(); + H.multiply(ac); + if (!param.analysesonly) { + L = L1.copyImage(); + L.divide(ortho); + L.conj(); + H1 = B; + H1.divide(acD); + } + } + if (param.flavor == param.BSPLINE) { + L1 = B.copyImage(); + L1.divide(ortho); + L1.conj(); + if (!param.analysesonly) { + L = B.copyImage(); + H = B.copyImage(); + H.divide(acD); + } + if (even) { + B.shift(); + } + else { + B.shiftY(); + } + H1 = B; + H1.conj(); + ac.shift(); + H1.multiply(ac); + } + if (param.flavor == param.OPERATOR) { + L1 = B.copyImage(); + ComplexImage ac0 = ac.copyImage(); + ComplexImage loc0 = loc; + loc0.multiply(sqrt2); + if (even) { + ac.shift(); + B.shift(); + } + else { + ac.shiftY(); + B.shiftY(); + } + if (!param.analysesonly) { + L = L1.copyImage(); + L.divide(ortho); + L.conj(); + H1 = B; + H1.squareModulus(); + H1.multiply(ac); + H1.multiply(ac0); + H1.divide(loc0, 0.0, 0.0); + H1.divide(acD, 0.0, 0.0); + H1.conj(); + } + H = loc0; + H.conj(); + H.divide(ac0); + } + if (param.flavor == param.DUALOPERATOR) { + L1 = B.copyImage(); + if (!param.analysesonly) { + L = L1.copyImage(); + L.conj(); + H1 = loc.copyImage(); + H1.multiply(sqrt2); + H1.divide(ac); + } + L1.divide(ortho); + ComplexImage ac0 = ac.copyImage(); + ComplexImage loc0 = loc; + if (even) { + ac.shift(); + ortho.shift(); + B.shift(); + } + else { + ac.shiftY(); + ortho.shiftY(); + B.shiftY(); + } + H = B; + H.squareModulus(); + H.multiply(1.0 / Math.sqrt(2.0)); + H.multiply(ac); + H.multiply(ac0); + H.divide(loc0, 0.0, 0.0); + H.divide(acD, 0.0, 0.0); + } + if (param.flavor == param.MARR) { + loc.multiply(sqrt2); + L1 = B.copyImage(); + ComplexImage ac0 = ac.copyImage(); + ComplexImage loc0 = loc; + if (even) { + ac.shift(); + B.shift(); + } + else { + ac.shiftY(); + B.shiftY(); + } + if (!param.analysesonly) { + H1 = B; + H1.squareModulus(); + H1.multiply(ac); + H1.multiply(ac0); + H1.divide(loc0, 0.0, 0.0); + H1.divide(acD); + H1.conj(); + L = L1.copyImage(); + L.divide(ortho); + L.conj(); + } + H = loc0; + H.conj(); + } + if (even) { + if (param.redundancy == param.BASIS) { + H.modulateMinusY(); + if (!param.analysesonly) { + H1.modulatePlusY(); + } + } + if ((param.redundancy == param.PYRAMID) && (!param.analysesonly)) { + H1.modulatePlusY(); + } + FA[1] = H; + FS[1] = H1; + FA[0] = L1; + FS[0] = L; + } + else { + if (param.redundancy == param.BASIS) { + H.modulateMinusQuincunx(); + if (!param.analysesonly) { + H1.modulatePlusQuincunx(); + } + } + if ((param.redundancy == param.PYRAMID) && (!param.analysesonly)) { + H1.modulatePlusQuincunx(); + } + FA[3] = H; + FS[3] = H1; + FA[2] = L1; + FS[2] = L; + } + } + + /** + * Computes all filters needed for the quincunx transform. + */ + + public void calculateFilters() { + ComplexImage ac = null; + ComplexImage L = null; + ComplexImage LD = localization(nx, ny, 0.0, 0.0, PI2, PI2, param.order, param.N, 1); + double c = Math.pow(0.5, param.order / 2.0); + if (param.accompute == param.ITERATIVE) { + int nx2 = 2 * nx; + int ny2 = 2 * ny; + ComplexImage Ldouble = localization(2 * nx, 2 * ny, 0.0, 0.0, PI2, PI2, param.order, param.N, 1); + L = Ldouble.copyImage(); + L.decimateCrop(); + ComplexImage H = localization(2 * nx, 2 * ny, 0.0, 0.0, 2.0 * PI2, 2.0 * PI2, param.order, param.N, 0); + H.multiply(c * c); + H.divide(Ldouble, 1.0, 0.0); + ac = H; + ac.squareModulus(); + ac = Autocorrelation.autocorrIterative(ac); + } + else { + L = localization(nx, ny, 0.0, 0.0, PI2, PI2, param.order, param.N, 0); + ComplexImage simpleloc = localization(nx, ny, 0.0, 0.0, PI2, PI2, 2 * param.order, 0, 0); + ac = Autocorrelation.autocorrGamma(simpleloc, param.order); + } + ComplexImage B = LD.copyImage(); + B.multiply(c); + B.divide(L, Math.cos(0.25 * PI * (double) param.N), -Math.sin(0.25 * PI * (double) param.N)); + // Interpolation to calculate acD + ComplexImage acD = interpolateQuincunxReal(ac); + ComplexImage loc = L; + quincunxPrefilter(ac); + ComplexImage ac0 = ac.copyImage(); + ComplexImage loc0 = loc.copyImage(); + computeLowpassHighpass(B, ac, acD, loc, true); + B = loc0; + B.decimate(); + B.multiply(c); + B.divide(LD, Math.cos(0.25 * PI * (double) param.N), -Math.sin(0.25 * PI * (double) param.N)); + loc = LD; + ac = acD; + acD = ac0; + acD.decimate(); + computeLowpassHighpass(B, ac, acD, loc, false); + } +} diff --git a/src/bilib/src/polyharmonicwavelets/QuincunxTransform.java b/src/bilib/src/polyharmonicwavelets/QuincunxTransform.java new file mode 100644 index 0000000..f97d3ea --- /dev/null +++ b/src/bilib/src/polyharmonicwavelets/QuincunxTransform.java @@ -0,0 +1,561 @@ +package polyharmonicwavelets; + +// +// QuincunxTransform.java +// PolyharmonicWavelets +// +// Created by Biomedical Imaging Group on 2/13/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +import java.util.*; +import ij.*; +import java.text.DecimalFormat; +import ij.text.*; +import ij.process.*; +import ij.plugin.filter.PlugInFilter; + +/** + * This class performs basis, pyramid and redundant quincunx transform. + * + * @author Katarina Balac, EPFL. + */ + +public class QuincunxTransform { + + // Analysis and synthesis filters + // FA[0]=H1 + // FA[1]=G1 + // FA[2]=H1D + // FA[3]=G1D + // similar for analysis filters + // H - lowpass filters + // G - highpass filters + // H1,G1 - filters for odd iteration + // H1D, G1D - filters for even iteration + // if transform is pyramid, filters for pyramid transform are FP: + // FP[0]=Gls + // FP[1]=GlsD + + private Parameters param; + private ComplexImage[] FA; + private ComplexImage[] FS; + private ComplexImage[] FP; + private ComplexImage P; + private int J; // number of iterations + private final double PI2 = 2.0 * Math.PI; + private final double sqrt2 = Math.sqrt(2.0); + + /** + * Creates a QuincunxTransform object. + * + * @param filt + * the filters used for the transform + * @param par + * the transform parameters + */ + + public QuincunxTransform(QuincunxFilters filt, Parameters par) { + J = par.J; + param = par; + FA = new ComplexImage[4]; + FS = new ComplexImage[4]; + for (int i = 0; i < 4; i++) { + FA[i] = filt.FA[i]; + FS[i] = filt.FS[i]; + } + P = filt.P.copyImage(); + } + + /** + * Performs the in-place nonredundant quincunx analysis on the input + * ComplexImage. + * + * @param image + * the image to transform + */ + + public void quincunxAnalysis(ComplexImage image) { + ComplexImage H1 = FA[0]; // Filters for odd iteration + ComplexImage G1 = FA[1]; + H1.multiply(0.5); + G1.multiply(0.5); + ComplexImage H1Dl = FA[2].getSubimage(0, FA[2].nx / 2 - 1, 0, FA[2].ny - 1); // Filters + // for + // even + // iteration + ComplexImage G1Dl = FA[3].getSubimage(0, FA[3].nx / 2 - 1, 0, FA[3].ny - 1); + H1Dl.multiply(0.5); + G1Dl.multiply(0.5); + ComplexImage Y1 = null; // lowpass subband + ComplexImage Y2 = null; // highpass subband + ComplexImage R = image.copyImage(); // remaining lowpass subband + R.FFT2D(); + if (param.prefilter) { + R.multiply(P); + } + int l = 1; + for (int j = 1; j <= J; j++) { + int mj = j % 2; + if (mj == 1) { // odd iteration + // filtering: Y1=R*H1 Y2=R*G1 + Y1 = R.copyImage(); + Y1.multiply(H1, l); + Y2 = R.copyImage(); + Y2.multiply(G1, l); + Y1.quincunxDownUp(); + if (!(j == J)) { // not last itteration, leave only left half of + // Y1 + Y1 = Y1.getSubimage(0, Y1.nx / 2 - 1, 0, Y1.ny - 1); + } + Y2.quincunxDownUp(); + // Transform highpass subband Y2 and put it in image + Y2.iFFT2D(); + Y2.fold(); + image.putSubimage(Y2.nx, 0, Y2); + } + else { // even iteration + Y1 = R.copyImage(); + Y1.multiply(H1Dl, l); + Y2 = R.copyImage(); + Y2.multiply(G1Dl, l); + // Downsampling + Y1.dyadicDownY(); + Y2.dyadicDownY(); + // Transform highpass subband Y2 and put it in image + Y2.iFFT2D(); + image.putSubimage(0, Y2.ny, Y2); + l *= 2; + } + R = Y1; + } + // insert lowpass subband + Y1.iFFT2D(); + if (J % 2 == 1) { + Y1.fold(); + } + H1.multiply(2.0); + G1.multiply(2.0); + image.putSubimage(0, 0, Y1); + } + + /** + * Performs the in-place nonredundant quincunx synthesis of image. + * + * @param image + * the image to transform + */ + + public void quincunxSynthesis(ComplexImage image) { + ComplexImage H1 = FS[0]; + ComplexImage G1 = FS[1]; + ComplexImage H1D = FS[2]; + ComplexImage G1D = FS[3]; + int p = (J - J % 2) / 2; + int l = 1; + for (int k = 0; k < p; k++, l *= 2) + ; + // Size of lowpass subband + int cy = image.ny / l; + int cx = image.nx / l; + if (J % 2 == 1) { + cx = cx / 2; + } + // Get lowpasssubband + ComplexImage Y1 = image.getSubimage(0, cx - 1, 0, cy - 1); + if (J % 2 == 1) { + Y1.unfold(); + } + Y1.FFT2D(); + for (int j = J; j > 0; j--) { + int mj = j % 2; + if (mj == 1) { // odd iteration + // Get highpass + ComplexImage Y2 = image.getSubimage(cx, 2 * cx - 1, 0, cy - 1); + cx *= 2; + Y2.unfold(); + Y2.FFT2D(); + // filtering + Y1.multiplyCircular(H1, l); + Y2.multiplyCircular(G1, l); + Y1.add(Y2); + } + else { // even iteration + // Get highpass + ComplexImage Y2 = image.getSubimage(0, cx - 1, cy, 2 * cy - 1); + cy *= 2; + Y2.FFT2D(); + l /= 2; + Y1.dyadicUpsample(); + Y2.dyadicUpsample(); + // filtering + Y1.multiplyCircular(H1D, l); + Y2.multiplyCircular(G1D, l); + Y1.add(Y2); + } + } + image.real = Y1.real; + image.imag = Y1.imag; + if (param.prefilter) { + image.divide(P, 1.0, 0.0); + } + image.iFFT2D(); + } + + /** + * Returns the fully redundant quincunx analysis of image. + * + * @param image + * the image to transform + * @return the array of quincunx transform subbands + */ + + public ComplexImage[] quincunxAnalysisRedundant(ComplexImage image) { + ComplexImage H = FA[0]; // Lowpass odd iteration filter + ComplexImage G = FA[1]; // Highpass odd iteration filter + ComplexImage HD = FA[2]; // Lowpass even iteration filter + ComplexImage GD = FA[3]; // Highpass even iteration filter + G.multiply(sqrt2); + GD.multiply(sqrt2); + H.multiply(sqrt2); + HD.multiply(sqrt2); + double sqrt2inv = 1.0 / sqrt2; + ComplexImage[] array = new ComplexImage[J + 1]; + int l = 1; + // Next index in array + ComplexImage R = image.copyImage(); + R.FFT2D(); + if (param.prefilter) { + R.multiply(P); + } + for (int j = 1; j < J + 1; j++) { + int k = j - 1; + array[k] = R.copyImage(); + if (j % 2 == 1) { // odd iteration + array[k].multiplyCircular(G, l); // Multiply with highpass odd + // iteration filter + R.multiplyCircular(H, l); // Multiply with lowpass odd iteration + // filter + } + else { + array[k].multiplyCircular(GD, l); // Multiply with highpass even + // iteration filter + R.multiplyCircular(HD, l); // Multiply with lowpass even + // iteration filter + l *= 2; + } + array[k].multiply(sqrt2inv); + R.multiply(sqrt2inv); + array[k].iFFT2D(); + } + array[J] = R.copyImage(); + array[J].iFFT2D(); + G.multiply(1.0 / sqrt2); + GD.multiply(1.0 / sqrt2); + H.multiply(1.0 / sqrt2); + HD.multiply(1.0 / sqrt2); + return array; + } + + /** + * Returns the fully redundant quincunx synthesis of array. + * + * @param array + * subbands of fully redundant quincunx transform + * @return the result of synthesis + */ + + public ComplexImage quincunxSynthesisRedundant(ComplexImage[] array) { + ComplexImage H = FS[0]; // Lowpass odd iteration filter + ComplexImage G = FS[1]; // Highpass odd iteration filter + ComplexImage HD = FS[2]; // Lowpass even iteration filter + ComplexImage GD = FS[3]; // Highpass even iteration filter + G.multiply(1.0 / sqrt2); + GD.multiply(1.0 / sqrt2); + H.multiply(1.0 / sqrt2); + HD.multiply(1.0 / sqrt2); + double sqrt2inv = 1.0 / Math.sqrt(2.0); + ComplexImage LP = array[J]; + ComplexImage HP; + LP.FFT2D(); + int l = 1; + for (int j = 1; j < (J + 1) / 2; j++, l *= 2) + ; + for (int j = J; j > 0; j--) { + HP = array[j - 1]; + HP.FFT2D(); + if (j % 2 == 1) { // odd iteration + HP.multiplyCircular(G, l); // Multiply with highpass odd + // iteration filter + LP.multiplyCircular(H, l); // Multiply with lowpass odd + // iteration filter + l /= 2; + } + else { + HP.multiplyCircular(GD, l); // Multiply with highpass even + // iteration filter + LP.multiplyCircular(HD, l); // Multiply with lowpass even + // iteration filter + } + LP.add(HP); + LP.multiply(sqrt2inv); + } + if (param.prefilter) { + LP.divide(P, 1.0, 0.0); + } + LP.iFFT2D(); + G.multiply(sqrt2); + GD.multiply(sqrt2); + H.multiply(sqrt2); + HD.multiply(sqrt2); + return LP; + } + + /** + * Returns the result of pyramid quincunx analysis of ComplexImage. + * + * @param image + * the image to transform + * @return the array of quincunx transform subbands + */ + + public ComplexImage[] quincunxAnalysisPyramid(ComplexImage image) { + ComplexImage[] array = new ComplexImage[J + 1]; + ComplexImage H1 = FA[0].copyImage(); // Filters for odd iteration + H1.multiply(0.5); + ComplexImage H1Dl = FA[2].getSubimage(0, FA[2].nx / 2 - 1, 0, FA[2].ny - 1); // Filters + // for + // even + // iteration + H1Dl.multiply(0.5); + ComplexImage G1 = FA[1].copyImage(); + ComplexImage G1D = FA[3].copyImage(); + ComplexImage Y2 = null; // highpass subband + ComplexImage Y1 = image.copyImage(); // remaining lowpass subband + Y1.FFT2D(); + if (param.prefilter) { + Y1.multiply(P); + } + int l = 1; + for (int j = 1; j <= J; j++) { + int mj = j % 2; + Y2 = Y1.copyImage(); + if (mj == 1) { // odd iteration + Y2.multiply(G1, l); + Y1.multiply(H1, l); + Y1.quincunxDownUp(); + // Put Y2 in stack + Y2.iFFT2D(); + array[j - 1] = Y2; + } + else { // even iteration + // Y1=left half of Y1*left half of H1D + Y2.multiply(G1D, l); + Y2.iFFT2D(); + Y2 = Y2.rotate(0); + array[j - 1] = Y2; + Y1 = Y1.getSubimage(0, Y1.nx / 2 - 1, 0, Y1.ny - 1); + Y1.multiply(H1Dl, l); + Y1.dyadicDownY(); + l *= 2; + } + } + // insert lowpass subband + Y1.iFFT2D(); + if (J % 2 == 1) { + Y1 = Y1.rotate(0.0); + } + array[J] = Y1; + return array; + } + + /** + * Returns the result of pyramid quincunx synthesis of array. + * + * @param array + * subbands of quincunx pyramid transform + * @return the result of synthesis + */ + + public ComplexImage quincunxSynthesisPyramid(ComplexImage[] array) { + // Compute Gls + ComplexImage Ge = FS[1].copyImage(); + Ge.multiply(FA[1]); + ComplexImage Gemodsqr = Ge.copyImage(); + Gemodsqr.squareModulus(); + Gemodsqr.quincunxDownUp(); + ComplexImage Gls = Ge; + Gls.conj(); + Gls.divide(Gemodsqr, 1.0, 0.0); + // Compute GlsD + ComplexImage GeD = FS[3].copyImage(); + GeD.multiply(FA[3]); + Gemodsqr = GeD.copyImage(); + Gemodsqr.squareModulus(); + Gemodsqr.downUpY(); + ComplexImage GlsD = GeD.copyImage(); + GlsD.conj(); + GlsD.divide(Gemodsqr, 1.0, 0.0); + // do synthesis + // Filters for odd iteration + ComplexImage H = FS[0]; + ComplexImage H1 = FA[1]; + ComplexImage G = FS[1]; + // Filters for even iteration + ComplexImage HD = FS[2]; + ComplexImage H1D = FA[3]; + ComplexImage GD = FS[3]; + ComplexImage HP = new ComplexImage(array[1].nx, array[1].ny); + ComplexImage LP = new ComplexImage(array[1].nx, array[0].ny); + ComplexImage LP1 = new ComplexImage(array[1].nx, array[1].ny); + // Get lowpass + LP.copyImageContent(array[J]); + if (J % 2 == 1) { + LP.unrotate(array[J - 1].nx, array[J - 1].ny); + } + LP.FFT2D(); + int l = 1; + for (int i = 0, J12 = (J - 1) / 2; i < J12; i++, l *= 2) + ; + int mj = J % 2; + for (int j = J; j > 0; j--) { + HP.copyImageContent(array[j - 1]); + if (mj == 0) { // even iteration + HP.unrotate(array[j - 2].nx, array[j - 2].ny); + HP.FFT2D(); + LP.dyadicUpsample(); + LP.multiply(HD, l); + LP1.copyImageContent(LP); + LP1.multiply(H1D, l); + HP.subtract(LP1); + HP.multiply(GlsD, l); + HP.downUpY(); + HP.multiplyCircular(GD, l); + LP.add(HP); + } + else { // odd iteration + HP.FFT2D(); + LP.multiplyCircular(H, l); + LP1.copyImageContent(LP); + LP1.multiply(H1, l); + HP.subtract(LP1); + HP.multiply(Gls, l); + HP.quincunxDownUp(); + HP.multiply(G, l); + LP.add(HP); + l /= 2; + } + mj = 1 - mj; + } + if (param.prefilter) { + LP.divide(P, 1.0, 0.0); + } + LP.iFFT2D(); + return LP; + } + + /** + * Prepares the quincunx pyramid transform coeffitients for being displayed. + * Rescales the subbands for visualisation and puts all subbands in one + * ComplexImage. + * + * @param array + * subbands of quincunx pyramid transform + * @param back + * background color + * @param rescale + * if rescale=false there is no rescaling + * @param lp + * if lp=false the lowpass subband is not displayed + * @return the image to display + */ + + public ComplexImage displayPyramid(ComplexImage[] array, double back, boolean rescale, boolean lp) { + int nx = array[0].nx; + int ny = array[0].ny; + int s = (nx + ny) / 2; + int l; + if (nx > ny) { + l = (nx + ny) / 2; + } + else { + l = ny; + } + ComplexImage display = new ComplexImage(nx + s, 2 * l); + display.settoConstant(back, back); + int x = 0; + int y = 0; + for (int j = 0; j < J; j++) { + ComplexImage temp = array[j].copyImage(); + if (rescale) { + temp.stretch(); + } + if (j % 2 == 1) { // even iteration + temp.unrotate(array[j - 1].nx, array[j - 1].ny); + temp = temp.rotate(back); + } + + temp.frame(back); + display.putSubimage(x, y, temp); + if ((j == J - 1) && (J % 2 == 0)) { + y += l; + x = nx - array[J].nx / 2; + } + else { + if (x == nx) { + y += l; + l /= 2; + x = nx - array[j + 1].nx; + } + else { + x = nx; + } + } + } + if (lp) { + ComplexImage temp = array[J].copyImage(); + if (J % 2 == 1) { // even iteration + temp.unrotate(array[J - 1].nx, array[J - 1].ny); + temp = temp.rotate(back); + } + temp.stretch(); + temp.frame(back); + display.putSubimage(x, y, temp); + } + return display; + } + + /** + * Prepares a nonredundant quincunx transform for being displayed, stretches + * each subband. + * + * @param image + * the basis transform coefficients + * @return the image with rescaled subbands to display + */ + + public ComplexImage displayBasis(ComplexImage image) { + int dx = image.nx; + int dy = image.ny; + ComplexImage out = new ComplexImage(dx, dy); + ComplexImage sub; + for (int j = 1; j <= J; j++) { + if (j % 2 == 1) { // Odd iteration + sub = image.getSubimage(dx / 2, dx - 1, 0, dy - 1); + sub.stretch(); + out.putSubimage(dx / 2, 0, sub); + dx /= 2; + } + else { + sub = image.getSubimage(0, dx - 1, dy / 2, dy - 1); + sub.stretch(); + out.putSubimage(0, dy / 2, sub); + dy /= 2; + } + } + sub = image.getSubimage(0, dx - 1, 0, dy - 1); + sub.stretch(); + out.putSubimage(0, 0, sub); + return out; + } +} \ No newline at end of file diff --git a/src/bilib/src/polyharmonicwavelets/Riesz.java b/src/bilib/src/polyharmonicwavelets/Riesz.java new file mode 100644 index 0000000..bc26422 --- /dev/null +++ b/src/bilib/src/polyharmonicwavelets/Riesz.java @@ -0,0 +1,447 @@ +package polyharmonicwavelets; + +// +// Riesz.java +// +// +// Created by Biomedical Imaging Group on 2/14/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +import java.util.*; +import ij.*; + +/** + * This class computes the Riesz wavelet transform of the image as well as its + * monogenic transform parameters such as the local orientation, amplitude and + * the instantaneous frequency in each subband. + * + * @author Katarina Balac, EPFL. + */ + +public class Riesz { + + /** + * The image. + */ + public ComplexImage image; + + /** + * The polyharmonic wavelet transform of the image. + */ + public ComplexImage[] p; + + /** + * The riesz transform of p, the wavelet Riesz transform of the image. + */ + public ComplexImage[] q; + + private Parameters param; + + /** + * Local wavenumber for subband j in wavenumber[j] + */ + public ComplexImage[] wavenumber = null; + /** + * Local modulus for subband j in modulus[j] + */ + public ComplexImage[] modulus = null; + /** + * Local phase for subband j in phase[j] + */ + public ComplexImage[] phase = null; + /** + * Local orientation for subband j in orientation[j] + */ + public ComplexImage[] orientation = null; + + private DyadicTransform transform; + private DyadicFilters filters; + private int J; + private double order; + + /** + * This variable stores all the lowpass subbands. The j-th subband is in + * lowpassSubbands[j-1], j=1...J+1. + */ + + public ComplexImage[] lowpassSubbands; + + /** + * Creates a Riesz object, computes all the monogenic features desired. The + * modulus is always computes. + * + * @param imag + * the image + * @param parameters + * the transform parameters + * @param phase + * if true compute the monogenic phase + * @param orientation + * if true compute the monogenic orientation + * @param wavenumber + * if true compute the monogenic wavenumber + */ + + public Riesz(ComplexImage imag, Parameters parameters, boolean phase, boolean orientation, boolean wavenumber) { + image = imag; + param = parameters; + param.analysesonly = true; + param.flavor = param.MARR; + param.prefilter = true; + param.lattice = param.DYADIC; // only gamma and J can be set from the + // outside + param.rieszfreq = 0; + order = param.order; + J = param.J; + param.N = 1; + filters = new DyadicFilters(param, image.nx, image.ny); + filters.calculateFilters(); + transform = new DyadicTransform(filters, param); + switch (parameters.redundancy) { + case Parameters.PYRAMID: { + q = transform.dyadicAnalysesPyramid(image); + } + break; + case Parameters.REDUNDANT: { + q = transform.dyadicAnalysesRedundant(image); + } + break; + } + double[] temp1 = filters.FA[0].imag; + filters.FA[0].imag = null; + double[] temp2 = filters.FA[1].imag; + filters.FA[1].imag = null; + param.N = 0; + filters.setParameters(param); + filters.calculateFilters(); + transform = new DyadicTransform(filters, param); + ComplexImage[][] plp = null; + switch (param.redundancy) { + case Parameters.PYRAMID: { + plp = transform.dyadicAnalysesPyramidLowpass(image); + } + break; + case Parameters.REDUNDANT: { + plp = transform.dyadicAnalysesRedundantLowpass(image); + } + break; + default: { + System.out.println("Redundancy has to be REDUNDANT or PYRAMID"); + } + } + p = plp[0]; + lowpassSubbands = plp[1]; + filters.FA[0].imag = temp1; + filters.FA[1].imag = temp2; + + computeModulus(); + + if (orientation) { + computeOrientation(); + } + if (phase) { + computePhase(); + } + if (wavenumber) { + computeWavenumber(); + } + } + + /* + * Computes the local modulus and places it in modulus. + */ + private void computeModulus() { + modulus = new ComplexImage[J]; + for (int j = 0; j < J; j++) { + modulus[j] = new ComplexImage(q[j].nx, q[j].ny, true); + int size = modulus[j].nxy; + for (int k = 0; k < size; k++) { + modulus[j].real[k] = Math.sqrt(p[j].real[k] * p[j].real[k] + q[j].real[k] * q[j].real[k] + q[j].imag[k] * q[j].imag[k]); + } + } + } + + /* + * Computes the local orientation and places it in orientation. + */ + private void computeOrientation() { + orientation = new ComplexImage[J]; + int lx = image.nx; + int ly = image.ny; + ComplexImage modq = new ComplexImage(image.nx, image.ny); + for (int j = 0; j < J; j++) { + orientation[j] = new ComplexImage(q[j].nx, q[j].ny, true); + modq.nx = q[j].nx; + modq.ny = q[j].ny; + int size = orientation[j].nxy; + for (int k = 0; k < size; k++) { + double a = modulus[j].real[k] * modulus[j].real[k]; + // Compute the weights modq to smooth + // modq.real[k]=(q[j].real[k]*q[j].real[k]+q[j].imag[k]*q[j].imag[k])/a; + orientation[j].real[k] = Math.atan2(q[j].real[k], q[j].imag[k]); + if (orientation[j].real[k] < 0) + orientation[j].real[k] += Math.PI / 2; + else + orientation[j].real[k] -= Math.PI / 2; + orientation[j].real[k] = -orientation[j].real[k]; + } + // Added by Daniel Sage 30.05.2008 + // Median-like on the angle when the modulus is too low + // orientation[j].smooth(modq); + /* + * modq.showReal("modq "+ j); for(int k=0; k<modq.nxy; k++) { int x= + * k / modq.nx; int y= k % modq.nx; if (x > 0) if (x <modq.nx-1) if + * (y > 0) if (y <modq.ny-1) { double max = modq.real[k]; int kmax = + * k; double min = modq.real[k]; int kmin = k; for (int u=x-1; + * u<x+1; u++) for (int v=y-1; v<y+1; v++) { if + * (modq.real[u+v*modq.nx] > max) { kmax = u+v*modq.nx; max = + * modq.real[kmax]; } if (modq.real[u+v*modq.nx] < min) { kmin = + * u+v*modq.nx; min = modq.real[kmin]; } } if (max - modq.real[k] > + * 0.8) { orientation[j].real[k] = orientation[j].real[kmax]; } if + * (modq.real[k]-min > 0.8) { orientation[j].real[k] = + * orientation[j].real[kmin]; } } } + */ + } + } + + /* + * Computes the local phase and places it in phase. + */ + private void computePhase() { + phase = new ComplexImage[J]; + for (int j = 0; j < J; j++) { + phase[j] = new ComplexImage(q[j].nx, q[j].ny, true); + int size = phase[j].nxy; + for (int k = 0; k < size; k++) { + double a = modulus[j].real[k]; + phase[j].real[k] = Math.acos(p[j].real[k] / a); + if (q[j].imag[k] < 0) { + phase[j].real[k] *= -1.0; + } + } + } + } + + /* + * Computes the local wave number and places it in wavenumber. + */ + private void computeWavenumber() { + ComplexImage[] q1xq2y = null; + ComplexImage[] pxpy = null; + param.rieszfreq = 1; + param.N = 0; + param.order = order - 1; + double[] temp1 = filters.FA[0].imag; + filters.FA[0].imag = null; + double[] temp2 = filters.FA[1].imag; + filters.FA[1].imag = null; + filters.setParameters(param); + filters.calculateFilters(); + transform = new DyadicTransform(filters, param); + if (param.redundancy == param.PYRAMID) { + q1xq2y = transform.dyadicAnalysesPyramid(image); + } + if (param.redundancy == param.REDUNDANT) { + q1xq2y = transform.dyadicAnalysesRedundant(image); + } + param.N = 1; + filters.FA[0].imag = temp1; + filters.FA[1].imag = temp2; + filters.setParameters(param); + filters.calculateFilters(); + transform = new DyadicTransform(filters, param); + if (param.redundancy == param.PYRAMID) { + pxpy = transform.dyadicAnalysesPyramid(image); + } + if (param.redundancy == param.REDUNDANT) { + pxpy = transform.dyadicAnalysesRedundant(image); + } + filters = null; + wavenumber = new ComplexImage[J]; + ComplexImage modq = new ComplexImage(image.nx, image.ny); + for (int j = 0; j < J; j++) { + wavenumber[j] = new ComplexImage(q[j].nx, q[j].ny, true); + modq.nx = q[j].nx; + modq.ny = q[j].ny; + int size = size = wavenumber[j].nxy; + for (int k = 0; k < size; k++) { + double a = modulus[j].real[k] * modulus[j].real[k]; + // Compute the weights modq to smooth + // modq.real[k]=(q[j].real[k]*q[j].real[k]+q[j].imag[k]*q[j].imag[k])/a; + // wave number + wavenumber[j].real[k] = (p[j].real[k] * q1xq2y[j].real[k] + q[j].real[k] * pxpy[j].real[k] + q[j].imag[k] * pxpy[j].imag[k]) / a; + } + // wavenumber[j].smooth(modq); + } + } + + /* + * Computes the local wave number and places it in wavenumber. + */ + public ComplexImage[] computeModifiedRiesz() { + ComplexImage[] q1xq2y = null; + ComplexImage[] pxpy = null; + param.rieszfreq = 1; + param.N = 0; + param.order = order - 1; + double[] temp1 = filters.FA[0].imag; + filters.FA[0].imag = null; + double[] temp2 = filters.FA[1].imag; + filters.FA[1].imag = null; + filters.setParameters(param); + filters.calculateFilters(); + transform = new DyadicTransform(filters, param); + if (param.redundancy == param.PYRAMID) { + q1xq2y = transform.dyadicAnalysesPyramid(image); + } + if (param.redundancy == param.REDUNDANT) { + q1xq2y = transform.dyadicAnalysesRedundant(image); + } + param.N = 1; + filters.FA[0].imag = temp1; + filters.FA[1].imag = temp2; + filters.setParameters(param); + filters.calculateFilters(); + transform = new DyadicTransform(filters, param); + if (param.redundancy == param.PYRAMID) { + pxpy = transform.dyadicAnalysesPyramid(image); + } + if (param.redundancy == param.REDUNDANT) { + pxpy = transform.dyadicAnalysesRedundant(image); + } + return q1xq2y; + } + + public ComplexImage[] computeModifiedWavelet() { + ComplexImage[] q1xq2y = null; + ComplexImage[] pxpy = null; + param.rieszfreq = 1; + param.N = 0; + param.order = order - 1; + double[] temp1 = filters.FA[0].imag; + filters.FA[0].imag = null; + double[] temp2 = filters.FA[1].imag; + filters.FA[1].imag = null; + filters.setParameters(param); + filters.calculateFilters(); + transform = new DyadicTransform(filters, param); + if (param.redundancy == param.PYRAMID) { + q1xq2y = transform.dyadicAnalysesPyramid(image); + } + if (param.redundancy == param.REDUNDANT) { + q1xq2y = transform.dyadicAnalysesRedundant(image); + } + param.N = 1; + filters.FA[0].imag = temp1; + filters.FA[1].imag = temp2; + filters.setParameters(param); + filters.calculateFilters(); + transform = new DyadicTransform(filters, param); + if (param.redundancy == param.PYRAMID) { + pxpy = transform.dyadicAnalysesPyramid(image); + } + if (param.redundancy == param.REDUNDANT) { + pxpy = transform.dyadicAnalysesRedundant(image); + } + return pxpy; + } + + /** + * Displays the local modulus on the screen. + */ + + public void displayModulus() { + if (param.redundancy == param.PYRAMID) { + ComplexImage disp = transform.displayDyadicPyramidReal(modulus, 0.0, false, false); + disp.showReal("Riesz modulus"); + } + if (param.redundancy == param.REDUNDANT) { + ComplexImage.displayStack(modulus, "Riesz modulus"); + } + } + + /** + * Displays the local wave number on the screen. + */ + + public void displayWaveNumber() { + if (param.redundancy == param.PYRAMID) { + ComplexImage disp = transform.displayDyadicPyramidReal(wavenumber, 0.0, false, false); + disp.showReal("Riesz wave number magnitude"); + } + if (param.redundancy == param.REDUNDANT) { + ComplexImage.displayStack(wavenumber, "Riesz wave number magnitude"); + } + } + + /** + * Displays the local phase on the screen. + */ + + public void displayPhase() { + if (param.redundancy == param.PYRAMID) { + ComplexImage disp = transform.displayDyadicPyramidReal(phase, 0.0, false, false); + disp.showReal("Riesz phase"); + } + if (param.redundancy == param.REDUNDANT) { + ComplexImage.displayStack(phase, "Riesz phase"); + } + } + + /** + * Displays the local orientation on the screen. + */ + + public void displayOrientation() { + if (param.redundancy == param.PYRAMID) { + ComplexImage disp = transform.displayDyadicPyramidReal(orientation, 0.0, false, false); + disp.showReal("Riesz orientation"); + } + if (param.redundancy == param.REDUNDANT) { + ComplexImage.displayStack(orientation, "Riesz orientation"); + } + } + + /** + * Displays the derivative in y direction. + */ + + public void displayRieszY() { + if (param.redundancy == param.PYRAMID) { + ComplexImage disp = transform.displayDyadicPyramidReal(q, 0.0, false, false); + disp.showReal("Riesz transform y"); + } + if (param.redundancy == param.REDUNDANT) { + ComplexImage.displayStack(q, "Riesz transform y"); + } + } + + /** + * Displays the derivative in x direction. + */ + + public void displayRieszX() { + if (param.redundancy == param.PYRAMID) { + ComplexImage disp = transform.displayDyadicPyramidReal(q, 0.0, false, false); + disp.showImag("Riesz transform x"); + } + if (param.redundancy == param.REDUNDANT) { + ComplexImage.displayStackImag(q, "Riesz transform x"); + } + } + + /** + * Displays the mother wavelet transform. + */ + + public void displayMother() { + if (param.redundancy == param.PYRAMID) { + ComplexImage disp = transform.displayDyadicPyramidReal(p, 0.0, false, false); + disp.showReal("Mother transform"); + } + if (param.redundancy == param.REDUNDANT) { + ComplexImage.displayStack(p, "Mother transform"); + } + } +} \ No newline at end of file diff --git a/src/bilib/src/wavelets/ComplexWaveFilter.java b/src/bilib/src/wavelets/ComplexWaveFilter.java new file mode 100644 index 0000000..c4ab89a --- /dev/null +++ b/src/bilib/src/wavelets/ComplexWaveFilter.java @@ -0,0 +1,283 @@ +package wavelets; + +/** + * This class generate the complex wavelets filter. + * <hr> + * <p> + * <b>Organisation</b>: <a href="http://bigwww.epfl.ch">Biomedical Imaging + * Group</a> (BIG), Ecole Polytechnique Federale de Lausanne (EPFL), Lausanne, + * Switzerland + * </p> + * <p> + * <b>Authors</b>: Jesse Berent, Daniel Sage + * </p> + * <p> + * <b>Reference</b>: B. Forster, D. Van De Ville, J. Berent, D. Sage, M. Unser, + * "<a href="http://bigwww.epfl.ch/publications/forster0404.html + * ">Complex Wavelets for Extended Depth-of-Field: A New Method for the Fusion of Multichannel Microscopy Images</a>," + * Microscopy Research and Technique, vol. 65, no. 1-2, pp. 33-42, September + * 2004. + * </p> + * <p> + * More information: http://bigwww.epfl.ch/demo/edf/index.html + * </p> + * <p> + * Other relevant information are available at: http://bigwww.epfl.ch/ + * </p> + * <hr> + * You'll be free to use this software for research purposes, but you should not + * redistribute it without our consent. In addition, we expect you to include a + * citation or acknowledgement whenever you present or publish results that are + * based on it. + */ + +public class ComplexWaveFilter { + + /** + * real lowpass filter. + */ + public double h[]; + + /** + * real highpass filter. + */ + public double g[]; + + /** + * imaginary lowpass filter. + */ + public double hi[]; + /** + * imaginary highpass filter. + */ + public double gi[]; + + /** + * The constructor generates the 4 filters for a giving length. + * + * @param length + * length of the filter, 6, 14 or 22 + */ + ComplexWaveFilter(int length) { + switch (length) { + + case 6: + // Complex Daubechies + // Real lowpass filter + h = new double[6]; + h[0] = -0.0662912607; + h[1] = 0.1104854346; + h[2] = 0.6629126074; + h[3] = 0.6629126074; + h[4] = 0.1104854346; + h[5] = -0.0662912607; + + // Real highpass filter + g = new double[6]; + g[5] = 0.0662912607; + g[4] = 0.1104854346; + g[3] = -0.6629126074; + g[2] = 0.6629126074; + g[1] = -0.1104854346; + g[0] = -0.0662912607; + + // imaginary lowpass filter + hi = new double[6]; + hi[0] = -0.0855816496; + hi[1] = -0.0855816496; + hi[2] = 0.1711632992; + hi[3] = 0.1711632992; + hi[4] = -0.0855816496; + hi[5] = -0.0855816496; + + // Imaginary highpass filter + gi = new double[6]; + gi[5] = -0.0855816496; + gi[4] = 0.0855816496; + gi[3] = 0.1711632992; + gi[2] = -0.1711632992; + gi[1] = -0.0855816496; + gi[0] = 0.0855816496; + break; + + case 14: + // Complex Daubechies + // Real lowpass filter + h = new double[14]; + h[0] = 0.0049120149; + h[1] = -0.0054111299; + h[2] = -0.0701089996; + h[3] = -0.0564377788; + h[4] = 0.1872348173; + h[5] = 0.3676385056; + h[6] = 0.2792793518; + h[7] = 0.2792793518; + h[8] = 0.3676385056; + h[9] = 0.1872348173; + h[10] = -0.0564377788; + h[11] = -0.0701089996; + h[12] = -0.0054111299; + h[13] = 0.0049120149; + + // Real highpass filter + g = new double[14]; + g[13] = -0.0049120149; + g[12] = -0.0054111299; + g[11] = 0.0701089996; + g[10] = -0.0564377788; + g[9] = -0.1872348173; + g[8] = 0.3676385056; + g[7] = -0.2792793518; + g[6] = 0.2792793518; + g[5] = -0.3676385056; + g[4] = 0.1872348173; + g[3] = 0.0564377788; + g[2] = -0.0701089996; + g[1] = 0.0054111299; + g[0] = 0.0049120149; + + // imaginary lowpass filter + hi = new double[14]; + hi[0] = 0.0018464710; + hi[1] = 0.0143947836; + hi[2] = 0.0079040001; + hi[3] = -0.1169376946; + hi[4] = -0.2596312614; + hi[5] = -0.0475928095; + hi[6] = 0.4000165107; + hi[7] = 0.4000165107; + hi[8] = -0.0475928095; + hi[9] = -0.2596312614; + hi[10] = -0.1169376946; + hi[11] = 0.0079040001; + hi[12] = 0.0143947836; + hi[13] = 0.0018464710; + + // Imaginary highpass filter + gi = new double[14]; + gi[13] = 0.0018464710; + gi[12] = -0.0143947836; + gi[11] = 0.0079040001; + gi[10] = 0.1169376946; + gi[9] = -0.2596312614; + gi[8] = 0.0475928095; + gi[7] = 0.4000165107; + gi[6] = -0.4000165107; + gi[5] = -0.0475928095; + gi[4] = 0.2596312614; + gi[3] = -0.1169376946; + gi[2] = -0.0079040001; + gi[1] = 0.0143947836; + gi[0] = -0.0018464710; + break; + + case 22: + // Complex Daubechies + // Real lowpass filter + h = new double[22]; + h[0] = -0.0002890832; + h[1] = -0.0000935982; + h[2] = 0.0059961342; + h[3] = 0.0122232015; + h[4] = -0.0243700791; + h[5] = -0.1092940542; + h[6] = -0.0918847036; + h[7] = 0.1540094645; + h[8] = 0.4014277015; + h[9] = 0.3153022916; + h[10] = 0.0440795062; + h[11] = 0.0440795062; + h[12] = 0.3153022916; + h[13] = 0.4014277015; + h[14] = 0.1540094645; + h[15] = -0.0918847036; + h[16] = -0.1092940542; + h[17] = -0.0243700791; + h[18] = 0.0122232015; + h[19] = 0.0059961342; + h[20] = -0.0000935982; + h[21] = -0.0002890832; + + // Real highpass filter + g = new double[22]; + g[21] = 0.0002890832; + g[20] = -0.0000935982; + g[19] = -0.0059961342; + g[18] = 0.0122232015; + g[17] = 0.0243700791; + g[16] = -0.1092940542; + g[15] = 0.0918847036; + g[14] = 0.1540094645; + g[13] = -0.4014277015; + g[12] = 0.3153022916; + g[11] = -0.0440795062; + g[10] = 0.0440795062; + g[9] = -0.3153022916; + g[8] = 0.4014277015; + g[7] = -0.1540094645; + g[6] = -0.0918847036; + g[5] = 0.1092940542; + g[4] = -0.0243700791; + g[3] = -0.0122232015; + g[2] = 0.0059961342; + g[1] = 0.0000935982; + g[0] = -0.0002890832; + + // imaginary lowpass filter + hi = new double[22]; + hi[0] = 0.0000211708; + hi[1] = -0.0012780664; + hi[2] = -0.0029648612; + hi[3] = 0.0144283733; + hi[4] = 0.0503067404; + hi[5] = -0.0044659104; + hi[6] = -0.1999654035; + hi[7] = -0.2603015239; + hi[8] = 0.0013800055; + hi[9] = 0.2232934469; + hi[10] = 0.1795460286; + hi[11] = 0.1795460286; + hi[12] = 0.2232934469; + hi[13] = 0.0013800055; + hi[14] = -0.2603015239; + hi[15] = -0.1999654035; + hi[16] = -0.0044659104; + hi[17] = 0.0503067404; + hi[18] = 0.0144283733; + hi[19] = -0.0029648612; + hi[20] = -0.0012780664; + hi[21] = 0.0000211708; + + // Imaginary highpass filter + gi = new double[22]; + gi[21] = 0.0000211708; + gi[20] = 0.0012780664; + gi[19] = -0.0029648612; + gi[18] = -0.0144283733; + gi[17] = 0.0503067404; + gi[16] = 0.0044659104; + gi[15] = -0.1999654035; + gi[14] = 0.2603015239; + gi[13] = 0.0013800055; + gi[12] = -0.2232934469; + gi[11] = 0.1795460286; + gi[10] = -0.1795460286; + gi[9] = 0.2232934469; + gi[8] = -0.0013800055; + gi[7] = -0.2603015239; + gi[6] = 0.1999654035; + gi[5] = -0.0044659104; + gi[4] = -0.0503067404; + gi[3] = 0.0144283733; + gi[2] = 0.0029648612; + gi[1] = -0.0012780664; + gi[0] = -0.0000211708; + break; + + default: + throw (new RuntimeException("Invalid length")); + } + + } + +} \ No newline at end of file diff --git a/src/bilib/src/wavelets/ComplexWavelet.java b/src/bilib/src/wavelets/ComplexWavelet.java new file mode 100644 index 0000000..3cf90e1 --- /dev/null +++ b/src/bilib/src/wavelets/ComplexWavelet.java @@ -0,0 +1,509 @@ +package wavelets; + +/** + * This class generate the complex wavelets filter. + * <hr> + * <p> + * <b>Organisation</b>: <a href="http://bigwww.epfl.ch">Biomedical Imaging + * Group</a> (BIG), Ecole Polytechnique Federale de Lausanne (EPFL), Lausanne, + * Switzerland + * </p> + * <p> + * <b>Authors</b>: Jesse Berent, Daniel Sage + * </p> + * <p> + * <b>Reference</b>: B. Forster, D. Van De Ville, J. Berent, D. Sage, M. Unser, + * "<a href="http://bigwww.epfl.ch/publications/forster0404.html + * ">Complex Wavelets for Extended Depth-of-Field: A New Method for the Fusion of Multichannel Microscopy Images</a>," + * Microscopy Research and Technique, vol. 65, no. 1-2, pp. 33-42, September + * 2004. + * </p> + * <p> + * More information: http://bigwww.epfl.ch/demo/edf/index.html + * </p> + * <p> + * Other relevant information are available at: http://bigwww.epfl.ch/ + * </p> + * <hr> + * You'll be free to use this software for research purposes, but you should not + * redistribute it without our consent. In addition, we expect you to include a + * citation or acknowledgement whenever you present or publish results that are + * based on it. + */ + +public class ComplexWavelet { + + /** + * This public method computes the complex wavelets transform of a given + * image and a given number of scale. + * + * @param in + * input image + * @param n + * number of scale + * @param length + * @return the wavelets coefficients + */ + static public ImageAccess[] analysis(ImageAccess in, int n, int length) { + + // Compute the size to the fine and coarse levels + int nxfine = in.getWidth(); + int nyfine = in.getHeight(); + + // Declare the object image + ImageAccess sub1; + ImageAccess sub2; + ImageAccess sub3; + ImageAccess sub4; + ImageAccess subre; + ImageAccess subim; + ImageAccess outRe; + ImageAccess outIm; + + // Initialization + int nx = nxfine; + int ny = nyfine; + outRe = in.duplicate(); + outIm = in.duplicate(); + + int re = 0; + int im = 1; + + // From fine to coarse main loop + // first iteration + + subre = new ImageAccess(nx, ny); + sub1 = new ImageAccess(nx, ny); + sub2 = new ImageAccess(nx, ny); + + // Copy in[] into image[] + outRe.getSubImage(0, 0, subre); + + // Apply the Wavelet splitting + sub1 = split(subre, re, re, length); + sub2 = split(subre, im, im, length); + + sub1.subtract(sub1, sub2); + + // Put the result image[] into in[] + outRe.putSubImage(0, 0, sub1); + + // Apply the Wavelet splitting + sub1 = split(subre, re, im, length); + sub2 = split(subre, im, re, length); + + sub1.add(sub1, sub2); + + outIm.putSubImage(0, 0, sub1); + + // Reduce the size by a factor of 2 + nx = nx / 2; + ny = ny / 2; + + for (int i = 1; i < n; i++) { + + // Create a new image array of size [nx,ny] + subre = new ImageAccess(nx, ny); + subim = new ImageAccess(nx, ny); + sub1 = new ImageAccess(nx, ny); + sub2 = new ImageAccess(nx, ny); + sub3 = new ImageAccess(nx, ny); + sub4 = new ImageAccess(nx, ny); + + // Copy in[] into image[] + outRe.getSubImage(0, 0, subre); + outIm.getSubImage(0, 0, subim); + + sub1 = split(subre, re, re, length); + sub2 = split(subre, im, im, length); + sub3 = split(subim, re, im, length); + sub4 = split(subim, im, re, length); + + sub1.subtract(sub1, sub2); + sub1.subtract(sub1, sub3); + sub1.subtract(sub1, sub4); + + outRe.putSubImage(0, 0, sub1); + + sub1 = split(subre, re, im, length); + sub2 = split(subre, im, re, length); + sub3 = split(subim, re, re, length); + sub4 = split(subim, im, im, length); + + sub1.add(sub1, sub2); + sub1.add(sub1, sub3); + sub1.subtract(sub1, sub4); + + outIm.putSubImage(0, 0, sub1); + + // Reduce the size by a factor of 2 + nx = nx / 2; + ny = ny / 2; + } + ImageAccess[] outComplex = new ImageAccess[2]; + outComplex[0] = outRe.duplicate(); + outComplex[1] = outIm.duplicate(); + return outComplex; + } + + /** + * Perform 1 iteration of the wavelet transformation of an ImageObject. The + * algorithm use the separability of the wavelet transformation. The result + * of the computation is put in the ImageObject calling this method. + * + * @param in + * an ImageAcess object provided by ImageJ + */ + static private ImageAccess split(ImageAccess in, int type1, int type2, int length) { + int nx = in.getWidth(); + int ny = in.getHeight(); + ImageAccess out = new ImageAccess(nx, ny); + + ComplexWaveFilter wf = new ComplexWaveFilter(length); + + if (nx >= 1) { + double rowin[] = new double[nx]; + double rowout[] = new double[nx]; + for (int y = 0; y < ny; y++) { + in.getRow(y, rowin); + + if (type1 == 0) + split_1D(rowin, rowout, wf.h, wf.g); + + if (type1 == 1) + split_1D(rowin, rowout, wf.hi, wf.gi); + + out.putRow(y, rowout); + } + } + else { + // out.copy(in); + out = in.duplicate(); + } + + if (ny > 1) { + double colin[] = new double[ny]; + double colout[] = new double[ny]; + for (int x = 0; x < nx; x++) { + out.getColumn(x, colin); + + if (type2 == 0) + split_1D(colin, colout, wf.h, wf.g); + + if (type2 == 1) + split_1D(colin, colout, wf.hi, wf.gi); + + out.putColumn(x, colout); + } + } + + return out; + } + + /** + * Perform 1 iteration of the wavelet transformation of a 1D vector using + * the wavelet transformation. The output vector has the same size of the + * input vector and it contains first the low pass part of the wavelet + * transform and then the high pass part of the wavelet transformation. + * + * @param vin + * input, a double 1D vector + * @param vout + * output, a double 1D vector + * @param h + * input, a double 1D vector, lowpass filter + * @param g + * input, a double 1D vector, highpass filter + */ + static private void split_1D(double vin[], double vout[], double h[], double g[]) { + int n = vin.length; + int n2 = n / 2; + int nh = h.length; + int ng = g.length; + + double voutL[] = new double[n]; + double voutH[] = new double[n]; + double pix; + int j1; + + for (int i = 0; i < n; i++) { + pix = 0.0; + for (int k = 0; k < nh; k++) { // Low pass part + j1 = i + k - (nh / 2); + if (j1 < 0) { // Periodic conditions + while (j1 < n) + j1 = n + j1; + j1 = (j1) % n; + } + if (j1 >= n) { // Periodic conditions + j1 = (j1) % n; + } + pix = pix + h[k] * vin[j1]; + } + voutL[i] = pix; + } + + for (int i = 0; i < n; i++) { + pix = 0.0; + for (int k = 0; k < ng; k++) { // Low pass part + j1 = i + k - (ng / 2); + if (j1 < 0) { // Periodic conditions + while (j1 < n) + j1 = n + j1; + j1 = (j1) % n; + } + if (j1 >= n) { // Periodic conditions + j1 = (j1) % n; + } + pix = pix + g[k] * vin[j1]; + } + voutH[i] = pix; + } + + for (int k = 0; k < n2; k++) + vout[k] = voutL[2 * k]; + for (int k = n2; k < n; k++) + vout[k] = voutH[2 * k - n]; + + } + + /** + * Perform an inverse wavelet transformation of the ImageObject calling this + * method with n scale. The size of image should be a interger factor of 2 + * at the power n. The input is the results of a wavelet transformation. The + * result is the reconstruction. It is put in the ImageObject calling this + * method. + * + * @param inRe + * the real part of the wavelets coefficients + * @param inIm + * the imaginary part of the wavelets coefficients + * @param n + * a integer value giving the number of scale + * @param length + * @return the reconstructed image + */ + + static public ImageAccess[] synthesis(ImageAccess inRe, ImageAccess inIm, int n, int length) { + // Compute the size to the fine and coarse levels + int div = (int) Math.pow(2.0, (double) (n - 1)); + int nxcoarse = inRe.getWidth() / div; + int nycoarse = inRe.getHeight() / div; + + // Declare the object image + ImageAccess subre, subim, sub1, sub2, sub3, sub4; + ImageAccess outRe; + ImageAccess outIm; + + // Initialisazion + int nx = nxcoarse; + int ny = nycoarse; + + outRe = inRe.duplicate(); + outIm = inIm.duplicate(); + + int re = 0; + int im = 1; + + // From fine to coarse main loop + for (int i = 0; i < n; i++) { + // Create a new image array of size [nx,ny] + subre = new ImageAccess(nx, ny); + subim = new ImageAccess(nx, ny); + sub1 = new ImageAccess(nx, ny); + sub2 = new ImageAccess(nx, ny); + sub3 = new ImageAccess(nx, ny); + sub4 = new ImageAccess(nx, ny); + // Copy in[] into image[] + outRe.getSubImage(0, 0, subre); + outIm.getSubImage(0, 0, subim); + + // Apply the Wavelet splitting + sub1 = merge(subre, re, re, length); + sub2 = merge(subre, im, im, length); + sub3 = merge(subim, re, im, length); + sub4 = merge(subim, im, re, length); + + sub1.subtract(sub1, sub2); + sub1.add(sub1, sub3); + sub1.add(sub1, sub4); + + outRe.putSubImage(0, 0, sub1); + + // Apply the Wavelet splitting + sub1 = merge(subre, re, im, length); + sub2 = merge(subre, im, re, length); + sub3 = merge(subim, re, re, length); + sub4 = merge(subim, im, im, length); + + sub3.subtract(sub3, sub1); + sub3.subtract(sub3, sub2); + sub3.subtract(sub3, sub4); + outIm.putSubImage(0, 0, sub3); + // Enlarge the size by a factor of 2 + nx = nx * 2; + ny = ny * 2; + + } + ImageAccess[] ReconstComplex = new ImageAccess[2]; + ReconstComplex[0] = outRe.duplicate(); + ReconstComplex[1] = outIm.duplicate(); + return ReconstComplex; + } + + /** + * Perform 1 iteration of the inverse wavelet transformation of an + * ImageObject. The algorithm use the separability of the wavelet + * transformation. The result of the computation is put in the ImageAccess + * calling this method. + * + * @param in + * an ImageAcess object provided by ImageJ + * @param type1 + * @param type2 + * @param length + * @return merge + */ + static private ImageAccess merge(ImageAccess in, int type1, int type2, int length) { + int nx = in.getWidth(); + int ny = in.getHeight(); + ImageAccess out = new ImageAccess(nx, ny); + ComplexWaveFilter wf = new ComplexWaveFilter(length); + + if (nx >= 1) { + double rowin[] = new double[nx]; + double rowout[] = new double[nx]; + for (int y = 0; y < ny; y++) { + in.getRow(y, rowin); + + if (type1 == 0) + merge_1D(rowin, rowout, wf.h, wf.g); + if (type1 == 1) { + merge_1D(rowin, rowout, wf.hi, wf.gi); + } + out.putRow(y, rowout); + } + } + else { + out = in.duplicate(); + } + + if (ny > 1) { + double colin[] = new double[ny]; + double colout[] = new double[ny]; + for (int x = 0; x < nx; x++) { + out.getColumn(x, colin); + + if (type2 == 0) + merge_1D(colin, colout, wf.h, wf.g); + if (type2 == 1) { + merge_1D(colin, colout, wf.hi, wf.gi); + } + out.putColumn(x, colout); + } + } + return out; + } + + /** + * Perform 1 iteration of the inverse wavelet transformation of a 1D vector + * using the Spline wavelet transformation. The output vector has the same + * size of the input vector and it contains the reconstruction of the input + * signal. The input vector constains first the low pass part of the wavelet + * transform and then the high pass part of the wavelet transformation. + * + * @param vin + * input, a double 1D vector + * @param vout + * output, a double 1D vector + * @param h + * input, a double 1D vector, lowpass filter + * @param g + * input, a double 1D vector, highpass filter + */ + static private void merge_1D(double vin[], double vout[], double h[], double g[]) { + int n = vin.length; + int n2 = n / 2; + int nh = h.length; + int ng = g.length; + int j1; + + double pix; + + // Upsampling + + double vinL[] = new double[n]; + double vinH[] = new double[n]; + for (int k = 0; k < n; k++) { + vinL[k] = 0; + vinH[k] = 0; + } + + for (int k = 0; k < n2; k++) { + vinL[2 * k] = vin[k]; + vinH[2 * k] = vin[k + n2]; + } + + // filtering + + for (int i = 0; i < n; i++) { + pix = 0.0; + for (int k = 0; k < nh; k++) { // Low pass part + j1 = i - k + (nh / 2); + if (j1 < 0) { // Periodic conditions + while (j1 < n) + j1 = n + j1; + j1 = (j1) % n; + } + if (j1 >= n) { // Periodic conditions + j1 = (j1) % n; + } + pix = pix + h[k] * vinL[j1]; + } + vout[i] = pix; + } + + for (int i = 0; i < n; i++) { + pix = 0.0; + for (int k = 0; k < ng; k++) { // High pass part + j1 = i - k + (ng / 2); + if (j1 < 0) { // Periodic conditions + while (j1 < n) + j1 = n + j1; + j1 = (j1) % n; + } + if (j1 >= n) { // Periodic conditions + j1 = (j1) % n; + } + pix = pix + g[k] * vinH[j1]; + } + vout[i] = vout[i] + pix; + } + } + + /** + * This method computes the modulus from a real ImageAccess and a imaginary + * ImageAccess objects. + * + * @param inRe + * @param inIm + * @return modulus + */ + static public ImageAccess modulus(ImageAccess inRe, ImageAccess inIm) { + int nx = inRe.getWidth(); + int ny = inRe.getHeight(); + double m, r, i; + ImageAccess modulus = new ImageAccess(nx, ny); + int x, y; + for (x = 0; x < nx; x++) { + for (y = 0; y < ny; y++) { + r = inRe.getPixel(x, y); + i = inIm.getPixel(x, y); + m = Math.sqrt((r * r) + (i * i)); + modulus.putPixel(x, y, m); + } + } + return modulus; + } + +} diff --git a/src/bilib/src/wavelets/ImageAccess.java b/src/bilib/src/wavelets/ImageAccess.java new file mode 100644 index 0000000..cbb3634 --- /dev/null +++ b/src/bilib/src/wavelets/ImageAccess.java @@ -0,0 +1,1354 @@ +package wavelets; + +import ij.ImagePlus; +import ij.process.ByteProcessor; +import ij.process.ColorProcessor; +import ij.process.FloatProcessor; +import ij.process.ImageProcessor; + +/** + * ImageAccess is an interface layer to facilitate the access to the pixels of + * ImageJ images. Methods of ImageAccess provides an easy and robust way to + * access to the pixels of images. The data are stored in a double array. Many + * methods get/put allows to access to the data. If the user try to access + * outside of the image, the mirror boundary conditions are applied. + * <hr> + * <p> + * <b>Organisation</b>: <a href="http://bigwww.epfl.ch">Biomedical Imaging + * Group</a> (BIG), Ecole Polytechnique Federale de Lausanne (EPFL), Lausanne, + * Switzerland + * </p> + * <p> + * <b>Authors</b>: Daniel Sage + * </p> + * <p> + * <b>Reference</b>: D. Sage, M. Unser, "<a + * href="http://bigwww.epfl.ch/publications/sage0303.html">Teaching + * Image-Processing Programming in Java</a>," IEEE Signal Processing + * Magazine, vol. 20, no. 6, pp. 43-52, November 2003. + * </p> + * <p> + * More information: http://bigwww.epfl.ch/teaching/iplabsite/index.php + * </p> + * <p> + * Other relevant information are available at: http://bigwww.epfl.ch/ + * </p> + * <hr> + * You'll be free to use this software for research purposes, but you should not + * redistribute it without our consent. In addition, we expect you to include a + * citation or acknowledgement whenever you present or publish results that are + * based on it. + */ + +public class ImageAccess { + public static final int PATTERN_SQUARE_3x3 = 0; + public static final int PATTERN_CROSS_3x3 = 1; + + private double pixels[] = null; // store the pixel data + private int nx = 0; // size in X axis + private int ny = 0; // size in Y axis + private int size = 0; // size = nx*ny + + /** + * Creates a new ImageAccess object from a 2D double array of pixels. The + * size of the array determines the size of the image. + * + * @param array + * an array of pixel (2D) + */ + public ImageAccess(double[][] array) { + if (array == null) + throw new ArrayStoreException("Constructor: array == null."); + this.ny = array[0].length; + this.nx = array.length; + this.size = nx * ny; + pixels = new double[size]; + int k = 0; + for (int j = 0; j < ny; j++) + for (int i = 0; i < nx; i++) + pixels[k++] = array[i][j]; + } + + /** + * Creates a new object of the class ImageAccess from an ImageProcessor + * object. + * + * ImageProcessor object contains the image data, the size and the type of + * the image. The ImageProcessor is provided by ImageJ, it should by a + * 8-bit, 16-bit. + * + * @param ip + * an ImageProcessor object provided by ImageJ + */ + public ImageAccess(ImageProcessor ip) { + if (ip == null) + throw new ArrayStoreException("Constructor: ImageProcessor == null."); + nx = ip.getWidth(); + ny = ip.getHeight(); + size = nx * ny; + pixels = new double[size]; + if (ip.getPixels() instanceof byte[]) { + byte[] bsrc = (byte[]) ip.getPixels(); + for (int k = 0; k < size; k++) + pixels[k] = (double) (bsrc[k] & 0xFF); + + } + else if (ip.getPixels() instanceof short[]) { + short[] ssrc = (short[]) ip.getPixels(); + for (int k = 0; k < size; k++) + pixels[k] = (double) (ssrc[k] & 0xFFFF); + } + else if (ip.getPixels() instanceof float[]) { + float[] fsrc = (float[]) ip.getPixels(); + for (int k = 0; k < size; k++) + pixels[k] = (double) fsrc[k]; + } + else { + throw new ArrayStoreException("Constructor: Unexpected image type."); + } + } + + /** + * Creates a new object of the class ImageAccess from an ColorProcessor + * object. + * + * ImageProcessor object contains the image data, the size and the type of + * the image. The ColorProcessor is provided by ImageJ, The ImageAccess + * contains one plane (red, green or blue) selected with the colorPlane + * parameter. + * + * @param cp + * an ColorProcessor object + * @param colorPlane + * index of the color plane 0, 1 or 2 + */ + public ImageAccess(ColorProcessor cp, int colorPlane) { + if (cp == null) + throw new ArrayStoreException("Constructor: ColorProcessor == null."); + if (colorPlane < 0) + throw new ArrayStoreException("Constructor: colorPlane < 0."); + if (colorPlane > 2) + throw new ArrayStoreException("Constructor: colorPlane > 2."); + nx = cp.getWidth(); + ny = cp.getHeight(); + size = nx * ny; + pixels = new double[size]; + byte[] r = new byte[size]; + byte[] g = new byte[size]; + byte[] b = new byte[size]; + cp.getRGB(r, g, b); + if (colorPlane == 0) + for (int k = 0; k < size; k++) + pixels[k] = (double) (r[k] & 0xFF); + else if (colorPlane == 1) + for (int k = 0; k < size; k++) + pixels[k] = (double) (g[k] & 0xFF); + else if (colorPlane == 2) + for (int k = 0; k < size; k++) + pixels[k] = (double) (b[k] & 0xFF); + } + + /** + * Creates a new object of the class ImageAccess. + * + * The size of the image are given as parameter. The data pixels are empty + * and are not initialized. + * + * @param nx + * the size of the image along the X-axis + * @param ny + * the size of the image along the Y-axis + */ + public ImageAccess(int nx, int ny) { + if (nx < 1) + throw new ArrayStoreException("Constructor: nx < 1."); + if (ny < 1) + throw new ArrayStoreException("Constructor: ny < 1."); + this.nx = nx; + this.ny = ny; + size = nx * ny; + pixels = new double[size]; + } + + /** + * Return the width of the image. + * + * @return the image width + */ + public int getWidth() { + return nx; + } + + /** + * Return the height of the image. + * + * @return the image height + */ + public int getHeight() { + return ny; + } + + /** + * Return the maximum value of ImageAccess. + * + * @return the maximum value + */ + public double getMaximum() { + double maxi = pixels[0]; + for (int i = 1; i < size; i++) + if (pixels[i] > maxi) + maxi = pixels[i]; + return maxi; + } + + /** + * Return the minimum value of ImageAccess. + * + * @return the minimum value + */ + public double getMinimum() { + double mini = pixels[0]; + for (int i = 1; i < size; i++) + if (pixels[i] < mini) + mini = pixels[i]; + return mini; + } + + /** + * Return the mean value of ImageAccess. + * + * @return the mean value + */ + public double getMean() { + double mean = 0.0; + for (int i = 0; i < size; i++) + mean += pixels[i]; + mean /= (double) (size); + return mean; + } + + /** + * Returns a copy of the pixel data organize in a 2D array. + * + * @return the 2D double array + */ + public double[][] getArrayPixels() { + double[][] array = new double[nx][ny]; + int k = 0; + for (int j = 0; j < ny; j++) + for (int i = 0; i < nx; i++) + array[i][j] = pixels[k++]; + return array; + } + + /** + * Returns a reference to the pixel data in double (1D). + * + * @return the 1D double array + */ + public double[] getPixels() { + return pixels; + } + + /** + * Create a FloatProcessor from the pixel data. The double values of the + * pixel are simply casted in float. + * + * @return the FloatProcessor + */ + public FloatProcessor createFloatProcessor() { + FloatProcessor fp = new FloatProcessor(nx, ny); + float[] fsrc = new float[size]; + for (int k = 0; k < size; k++) + fsrc[k] = (float) (pixels[k]); + fp.setPixels(fsrc); + return fp; + } + + /** + * Create a ByteProcessor from the pixel data. The double values of the + * pixel are clipped in the [0..255] range. + * + * @return the ByteProcessor + */ + public ByteProcessor createByteProcessor() { + ByteProcessor bp = new ByteProcessor(nx, ny); + byte[] bsrc = new byte[size]; + double p; + for (int k = 0; k < size; k++) { + p = pixels[k]; + if (p < 0) + p = 0.0; + if (p > 255.0) + p = 255.0; + bsrc[k] = (byte) p; + } + bp.setPixels(bsrc); + return bp; + } + + /** + * Create a new ImageAccess object by duplication of the current the + * ImageAccess object. + * + * @return a new ImageAccess object + **/ + public ImageAccess duplicate() { + ImageAccess ia = new ImageAccess(nx, ny); + for (int i = 0; i < size; i++) + ia.pixels[i] = this.pixels[i]; + return ia; + } + + /** + * An ImageAccess object calls this method for getting the gray level of a + * selected pixel. + * + * Mirror border conditions are applied. + * + * @param x + * input, the integer x-coordinate of a pixel + * @param y + * input, the integer y-coordinate of a pixel + * @return the gray level of the pixel (double) + */ + public double getPixel(int x, int y) { + int periodx = 2 * nx - 2; + int periody = 2 * ny - 2; + if (x < 0) { + while (x < 0) + x += periodx; // Periodize + if (x >= nx) + x = periodx - x; // Symmetrize + } + else if (x >= nx) { + while (x >= nx) + x -= periodx; // Periodize + if (x < 0) + x = -x; // Symmetrize + } + + if (y < 0) { + while (y < 0) + y += periody; // Periodize + if (y >= ny) + y = periody - y; // Symmetrize + } + else if (y >= ny) { + while (y >= ny) + y -= periody; // Periodize + if (y < 0) + y = -y; // Symmetrize + } + return pixels[x + y * nx]; + } + + /** + * An ImageAccess object calls this method for getting the gray level of a + * selected pixel using a bilinear interpolation. The coordinates can be + * given in double and the bilinear interpolation is applied the find the + * gray level. + * + * Mirror border conditions are applied. + * + * @param x + * input, the double x-coordinate of a pixel + * @param y + * input, the double y-coordinate of a pixel + * @return the gray level of the pixel (double) + */ + public double getInterpolatedPixel(double x, double y) { + if (Double.isNaN(x)) + return 0; + if (Double.isNaN(y)) + return 0; + + if (x < 0) { + int periodx = 2 * nx - 2; + while (x < 0) + x += periodx; // Periodize + if (x >= nx) + x = periodx - x; // Symmetrize + } + else if (x >= nx) { + int periodx = 2 * nx - 2; + while (x >= nx) + x -= periodx; // Periodize + if (x < 0) + x = -x; // Symmetrize + } + + if (y < 0) { + int periody = 2 * ny - 2; + while (y < 0) + y += periody; // Periodize + if (y >= ny) + y = periody - y; // Symmetrize + } + else if (y >= ny) { + int periody = 2 * ny - 2; + while (y >= ny) + y -= periody; // Periodize + if (y < 0) + y = -y; // Symmetrize + } + int i; + if (x >= 0.0) + i = (int) x; + else { + final int iAdd = (int) x - 1; + i = ((int) (x - (double) iAdd) + iAdd); + } + int j; + if (y >= 0.0) + j = (int) y; + else { + final int iAdd = (int) y - 1; + j = ((int) (y - (double) iAdd) + iAdd); + } + + double dx = x - (double) i; + double dy = y - (double) j; + int di, dj; + if (i >= nx - 1) + di = -1; + else + di = 1; + int index = i + j * nx; + double v00 = pixels[index]; + double v10 = pixels[index + di]; + if (j >= ny - 1) + index -= nx; + else + index += nx; + double v01 = pixels[index]; + double v11 = pixels[index + di]; + return (dx * (v11 * dy - v10 * (dy - 1.0)) - (dx - 1.0) * (v01 * dy - v00 * (dy - 1.0))); + } + + /** + * An ImageAccess object calls this method for getting a whole column of the + * image. + * + * The column should already created with the correct size [ny]. + * + * @param x + * input, the integer x-coordinate of a column + * @param column + * output, an array of the type double + */ + public void getColumn(int x, double[] column) { + if (x < 0) + throw new IndexOutOfBoundsException("getColumn: x < 0."); + if (x >= nx) + throw new IndexOutOfBoundsException("getColumn: x >= nx."); + if (column == null) + throw new ArrayStoreException("getColumn: column == null."); + if (column.length != ny) + throw new ArrayStoreException("getColumn: column.length != ny."); + for (int i = 0; i < ny; i++) { + column[i] = pixels[x]; + x += nx; + } + } + + /** + * An ImageAccess object calls this method for getting a part of column. The + * starting point is given by the y parameter and the ending determine by + * the size of the column parameter. The column parameter should already + * created. + * + * @param x + * input, the integer x-coordinate of a column + * @param y + * input, starting point + * @param column + * output, an array of the type double + */ + public void getColumn(int x, int y, double[] column) { + if (x < 0) + throw new IndexOutOfBoundsException("getColumn: x < 0."); + if (x >= nx) + throw new IndexOutOfBoundsException("getColumn: x >= nx."); + if (column == null) + throw new ArrayStoreException("getColumn: column == null."); + int by = column.length; + if (y >= 0) + if (y < ny - by - 1) { + int index = y * nx + x; + for (int i = 0; i < by; i++) { + column[i] = pixels[index]; + index += nx; + } + return; + } + // Getting information outside of the image + int yt[] = new int[by]; + for (int k = 0; k < by; k++) { + int ya = y + k; + int periody = 2 * ny - 2; + while (ya < 0) + ya += periody; // Periodize + while (ya >= ny) { + ya = periody - ya; // Symmetrize + if (ya < 0) + ya = -ya; + } + yt[k] = ya; + } + int index = 0; + for (int i = 0; i < by; i++) { + index = yt[i] * nx + x; + column[i] = pixels[index]; + } + } + + /** + * An ImageAccess object calls this method for getting a whole row of the + * image. + * + * The row should already created with the correct size [nx]. + * + * @param y + * input, the integer y-coordinate of a row + * @param row + * output, an array of the type double + */ + public void getRow(int y, double[] row) { + if (y < 0) + throw new IndexOutOfBoundsException("getRow: y < 0."); + if (y >= ny) + throw new IndexOutOfBoundsException("getRow: y >= ny."); + if (row == null) + throw new ArrayStoreException("getColumn: row == null."); + if (row.length != nx) + throw new ArrayStoreException("getColumn: row.length != nx."); + y *= nx; + for (int i = 0; i < nx; i++) + row[i] = pixels[y++]; + } + + /** + * An ImageAccess object calls this method for getting a part of row. The + * starting point is given by the y parameter and the ending determine by + * the size of the row parameter. The row parameter should already created. + * + * @param x + * input, starting point + * @param y + * input, the integer y-coordinate of a row + * @param row + * output, an array of the type double + */ + public void getRow(int x, int y, double[] row) { + if (y < 0) + throw new IndexOutOfBoundsException("getRow: y < 0."); + if (y >= ny) + throw new IndexOutOfBoundsException("getRow: y >= ny."); + if (row == null) + throw new ArrayStoreException("getRow: row == null."); + int bx = row.length; + if (x >= 0) + if (x < nx - bx - 1) { + int index = y * nx + x; + for (int i = 0; i < bx; i++) { + row[i] = pixels[index++]; + } + return; + } + int periodx = 2 * nx - 2; + int xt[] = new int[bx]; + for (int k = 0; k < bx; k++) { + int xa = x + k; + while (xa < 0) + xa += periodx; // Periodize + while (xa >= nx) { + xa = periodx - xa; // Symmetrize + if (xa < 0) + xa = -xa; + } + xt[k] = xa; + } + int somme = 0; + int index = y * nx; + for (int i = 0; i < bx; i++) { + somme = index + xt[i]; + row[i] = pixels[somme]; + } + } + + /** + * An ImageAccess object calls this method for getting a neighborhood + * arround a pixel position. + * + * The neigh parameter should already created. The size of the array + * determines the neighborhood block. + * + * <br> + * Mirror border conditions are applied. <br> + * <br> + * The pixel value of (x-n/2, y-n/2) is put into neigh[0][0] <br> + * ... <br> + * The pixel value of (x+n/2, y+n/2) is put into neigh[n-1][n-1] <br> + * <br> + * For example if neigh is a double[4][4]: <br> + * The pixel value of (x-1, y-1) is put into neigh[0][0] <br> + * The pixel value of (x , y ) is put into neigh[1][1] <br> + * The pixel value of (x, y+1) is put into neigh[1][2] <br> + * The pixel value of (x+1, y-2) is put into neigh[2][0] <br> + * ... <br> + * For example if neigh is a double[5][5]: <br> + * The pixel value of (x-2, y-2) is put into neigh[0][0] <br> + * The pixel value of (x-1, y-1) is put into neigh[1][1] <br> + * The pixel value of (x , y ) is put into neigh[2][2] <br> + * The pixel value of (x, y+1) is put into neigh[2][3] <br> + * The pixel value of (x+2, y-2) is put into neigh[4][0] + * + * @param x + * the integer x-coordinate of a selected central pixel + * @param y + * the integer y-coordinate of a selected central pixel + * @param neigh + * output, a 2D array s + */ + public void getNeighborhood(int x, int y, double neigh[][]) { + int bx = neigh.length; + int by = neigh[0].length; + int bx2 = (bx - 1) / 2; + int by2 = (by - 1) / 2; + if (x >= bx2) + if (y >= by2) + if (x < nx - bx2 - 1) + if (y < ny - by2 - 1) { + int index = (y - by2) * nx + (x - bx2); + for (int j = 0; j < by; j++) { + for (int i = 0; i < bx; i++) { + neigh[i][j] = pixels[index++]; + } + index += (nx - bx); + } + return; + } + int xt[] = new int[bx]; + for (int k = 0; k < bx; k++) { + int xa = x + k - bx2; + int periodx = 2 * nx - 2; + while (xa < 0) + xa += periodx; // Periodize + while (xa >= nx) { + xa = periodx - xa; // Symmetrize + if (xa < 0) + xa = -xa; + } + xt[k] = xa; + } + int yt[] = new int[by]; + for (int k = 0; k < by; k++) { + int ya = y + k - by2; + int periody = 2 * ny - 2; + while (ya < 0) + ya += periody; // Periodize + while (ya >= ny) { + ya = periody - ya; // Symmetrize + if (ya < 0) + ya = -ya; + } + yt[k] = ya; + } + int sum = 0; + for (int j = 0; j < by; j++) { + int index = yt[j] * nx; + for (int i = 0; i < bx; i++) { + sum = index + xt[i]; + neigh[i][j] = pixels[sum]; + } + } + } + + /** + * An ImageAccess object calls this method for getting a neighborhood of a + * predefined pattern around a selected pixel (x,y). <br> + * The available patterns are: <br> + * - a 3*3 block: PATTERN_SQUARE_3x3 (8-connect) <br> + * - a 3*3 cross: PATTERN_CROSS_3x3 (4-connect) <br> + * <br> + * Mirror border conditions are applied. <br> + * The pixel is arranged in a 1D array according the following rules: <br> + * <br> + * If the pattern is PATTERN_SQUARE_3x3 (8-connect) <br> + * The pixel value of (x-1, y-1) are put into neigh[0] <br> + * The pixel value of (x , y-1) are put into neigh[1] <br> + * The pixel value of (x+1, y-1) are put into neigh[2] <br> + * The pixel value of (x-1, y ) are put into neigh[3] <br> + * The pixel value of (x , y ) are put into neigh[4] <br> + * The pixel value of (x+1, y ) are put into neigh[5] <br> + * The pixel value of (x-1, y+1) are put into neigh[6] <br> + * The pixel value of (x , y+1) are put into neigh[7] <br> + * The pixel value of (x+1, y+1) are put into neigh[8] <br> + * <br> + * If the pattern is PATTERN_CROSS_3x3 (4-connect) <br> + * The pixel value of (x , y-1) are put into neigh[0] <br> + * The pixel value of (x-1, y ) are put into neigh[1] <br> + * The pixel value of (x , y ) are put into neigh[2] <br> + * The pixel value of (x+1, y ) are put into neigh[3] <br> + * The pixel value of (x , y+1) are put into neigh[4] <br> + * <br> + * The neigh should already created as a double array of 9 elements for the + * PATTERN_SQUARE_3x3 or 5 elements for the PATTERN_CROSS_3x3. + * + * @param x + * x-coordinate of a selected central pixel + * @param y + * y-coordinate of a selected central pixel + * @param neigh + * output, an array consisting of 9 or 5 elements + * @param pattern + * PATTERN_SQUARE_3x3 or PATTERN_CROSS_3x3. + */ + public void getPattern(int x, int y, double neigh[], int pattern) { + if (neigh == null) + throw new ArrayStoreException("getPattern: neigh == null."); + switch (pattern) { + case PATTERN_SQUARE_3x3: + if (neigh.length != 9) + throw new ArrayStoreException("getPattern: neigh.length != 9."); + getPatternSquare3x3(x, y, neigh); + break; + case PATTERN_CROSS_3x3: + if (neigh.length != 5) + throw new ArrayStoreException("getPattern: neigh.length != 5"); + getPatternCross3x3(x, y, neigh); + break; + default: + throw new ArrayStoreException("getPattern: unexpected pattern."); + } + } + + /** + * An ImageAccess object calls this method for getting a 3x3 neighborhood of + * 8-connected pixels around a selected pixel. + * + * @param x + * input, the integer x-coordinate of a selected central pixel + * @param y + * input, the integer y-coordinate of a selected central pixel + * @param neigh + * output, an array consisting of 9 elements of the type double + */ + private void getPatternSquare3x3(int x, int y, double neigh[]) { + if (x >= 1) + if (y >= 1) + if (x < nx - 1) + if (y < ny - 1) { + int index = (y - 1) * nx + (x - 1); + neigh[0] = pixels[index++]; + neigh[1] = pixels[index++]; + neigh[2] = pixels[index]; + index += (nx - 2); + neigh[3] = pixels[index++]; + neigh[4] = pixels[index++]; + neigh[5] = pixels[index]; + index += (nx - 2); + neigh[6] = pixels[index++]; + neigh[7] = pixels[index++]; + neigh[8] = pixels[index]; + return; + } + int x1 = x - 1; + int x2 = x; + int x3 = x + 1; + int y1 = y - 1; + int y2 = y; + int y3 = y + 1; + if (x == 0) + x1 = x3; + if (y == 0) + y1 = y3; + if (x == nx - 1) + x3 = x1; + if (y == ny - 1) + y3 = y1; + int offset = y1 * nx; + neigh[0] = pixels[offset + x1]; + neigh[1] = pixels[offset + x2]; + neigh[2] = pixels[offset + x3]; + offset = y2 * nx; + neigh[3] = pixels[offset + x1]; + neigh[4] = pixels[offset + x2]; + neigh[5] = pixels[offset + x3]; + offset = y3 * nx; + neigh[6] = pixels[offset + x1]; + neigh[7] = pixels[offset + x2]; + neigh[8] = pixels[offset + x3]; + } + + /** + * An ImageAccess object calls this method for getting a 3x3 neighborhood of + * 4-connected pixels around a selected pixel. + * + * @param x + * input, the integer x-coordinate of a selected central pixel + * @param y + * input, the integer y-coordinate of a selected central pixel + * @param neigh + * output, an array consisting of 5 elements of the type double + */ + private void getPatternCross3x3(int x, int y, double neigh[]) { + if (x >= 1) + if (y >= 1) + if (x < nx - 1) + if (y < ny - 1) { + int index = (y - 1) * nx + x; + neigh[0] = pixels[index]; + index += (nx - 1); + neigh[1] = pixels[index++]; + neigh[2] = pixels[index++]; + neigh[3] = pixels[index]; + index += (nx - 1); + neigh[4] = pixels[index]; + return; + } + int x1 = x - 1; + int x2 = x; + int x3 = x + 1; + int y1 = y - 1; + int y2 = y; + int y3 = y + 1; + if (x == 0) + x1 = x3; + if (y == 0) + y1 = y3; + if (x == nx - 1) + x3 = x1; + if (y == ny - 1) + y3 = y1; + int offset = y1 * nx; + neigh[0] = pixels[offset + x2]; + offset = y2 * nx; + neigh[1] = pixels[offset + x1]; + neigh[2] = pixels[offset + x2]; + neigh[3] = pixels[offset + x3]; + offset = y3 * nx; + neigh[4] = pixels[offset + x2]; + } + + /** + * An ImageAccess object calls this method to get a sub-image with the upper + * left corner in the coordinate (x,y). + * + * The sub-image ouptut should be already created. + * + * @param x + * x-coordinate in the source image + * @param y + * y-coordinate in the source image + * @param output + * an ImageAccess object with the sub-image; + */ + public void getSubImage(int x, int y, ImageAccess output) { + if (output == null) + throw new ArrayStoreException("getSubImage: output == null."); + if (x < 0) + throw new ArrayStoreException("getSubImage: Incompatible image size"); + if (y < 0) + throw new ArrayStoreException("getSubImage: Incompatible image size"); + if (x >= nx) + throw new ArrayStoreException("getSubImage: Incompatible image size"); + if (y >= ny) + throw new ArrayStoreException("getSubImage: Incompatible image size"); + int nxcopy = output.getWidth(); + int nycopy = output.getHeight(); + double[][] neigh = new double[nxcopy][nycopy]; + int nx2 = (nxcopy - 1) / 2; + int ny2 = (nycopy - 1) / 2; + this.getNeighborhood(x + nx2, y + ny2, neigh); + output.putArrayPixels(neigh); + } + + /** + * An ImageAccess object calls this method in order a value of the gray + * level to be put to a position inside it given by the coordinates. + * + * @param x + * input, the integer x-coordinate of a pixel + * @param y + * input, the integer y-coordinate of a pixel + * @param value + * input, a value of the gray level of the type double + */ + public void putPixel(int x, int y, double value) { + if (x < 0) + return; + if (x >= nx) + return; + if (y < 0) + return; + if (y >= ny) + return; + pixels[x + y * nx] = value; + } + + /** + * An ImageAccess object calls this method to put a whole column in a + * specified position into the image. + * + * @param x + * input, the integer x-coordinate of a column + * @param column + * input, an array of the type double + */ + public void putColumn(int x, double[] column) { + if (x < 0) + throw new IndexOutOfBoundsException("putColumn: x < 0."); + if (x >= nx) + throw new IndexOutOfBoundsException("putColumn: x >= nx."); + if (column == null) + throw new ArrayStoreException("putColumn: column == null."); + if (column.length != ny) + throw new ArrayStoreException("putColumn: column.length != ny."); + for (int i = 0; i < ny; i++) { + pixels[x] = column[i]; + x += nx; + } + } + + /** + * An ImageAccess object calls this method to put a part of column into the + * image. The starting poisition in given by y and the ending position is + * determined by the size of the column array. + * + * @param x + * input, the integer x-coordinate of a column + * @param y + * input, the integer y-coordinate of a column + * @param column + * input, an array of the type double + */ + public void putColumn(int x, int y, double[] column) { + if (x < 0) + throw new IndexOutOfBoundsException("putColumn: x < 0."); + if (x >= nx) + throw new IndexOutOfBoundsException("putColumn: x >= nx."); + if (column == null) + throw new ArrayStoreException("putColumn: column == null."); + int by = column.length; + int index = y * nx + x; + int top = 0; + int bottom = 0; + if (y >= 0) { + if (y < ny - by) + bottom = by; + else + bottom = -y + ny; + for (int i = top; i < bottom; i++) { + pixels[index] = column[i]; + index += nx; + } + return; + } + else { + index = x; + top = -y; + if (y < ny - by) + bottom = by; + else + bottom = -y + ny; + for (int i = top; i < bottom; i++) { + pixels[index] = column[i]; + index += nx; + } + } + } + + /** + * An ImageAccess object calls this method to put a whole row in a specified + * position into the image. + * + * @param y + * input, the integer y-coordinate of a row + * @param row + * input, an array of the type double + */ + public void putRow(int y, double[] row) { + if (y < 0) + throw new IndexOutOfBoundsException("putRow: y < 0."); + if (y >= ny) + throw new IndexOutOfBoundsException("putRow: y >= ny."); + if (row == null) + throw new ArrayStoreException("putRow: row == null."); + if (row.length != nx) + throw new ArrayStoreException("putRow: row.length != nx."); + y *= nx; + for (int i = 0; i < nx; i++) { + pixels[y++] = row[i]; + } + + } + + /** + * An ImageAccess object calls this method to put a part of row into the + * image. The starting poisition in given by x and the ending position is + * determined by the size of the row array. + * + * @param x + * input, the integer x-coordinate of a column + * @param y + * input, the integer y-coordinate of a row + * @param row + * input, an array of the type double + */ + public void putRow(int x, int y, double[] row) { + if (y < 0) + throw new IndexOutOfBoundsException("putRow: y < 0."); + if (y >= ny) + throw new IndexOutOfBoundsException("putRow: y >= ny."); + if (row == null) + throw new ArrayStoreException("putRow: row == null."); + int bx = row.length; + int index = y * nx + x; + int left = 0; + int right = 0; + if (x >= 0) { + if (x < nx - bx) + right = bx; + else + right = -x + nx; + + for (int i = left; i < right; i++) { + pixels[index++] = row[i]; + } + return; + } + else { + index = y * nx; + left = -x; + + if (x < nx - bx) + right = bx; + else + right = -x + nx; + + for (int i = left; i < right; i++) { + pixels[index++] = row[i]; + } + } + } + + /** + * An ImageAccess object calls this method in order to put an 2D array of + * double in an ImageAccess. + * + * @param array + * input, the double array + */ + public void putArrayPixels(double[][] array) { + if (array == null) + throw new IndexOutOfBoundsException("putArrayPixels: array == null."); + int bx = array.length; + int by = array[0].length; + if (bx * by != size) + throw new IndexOutOfBoundsException("putArrayPixels: imcompatible size."); + int k = 0; + for (int j = 0; j < by; j++) + for (int i = 0; i < bx; i++) + pixels[k++] = array[i][j]; + } + + /** + * An ImageAccess object calls this method to put a sub-image with the upper + * left corner in the coordinate (x,y). + * + * The sub-image input should be already created. + * + * @param x + * x-coordinate in the source image + * @param y + * y-coordinate in the source image + * @param input + * an ImageAccess object that we want to put; + */ + public void putSubImage(int x, int y, ImageAccess input) { + if (input == null) + throw new ArrayStoreException("putSubImage: input == null."); + if (x < 0) + throw new IndexOutOfBoundsException("putSubImage: x < 0."); + if (y < 0) + throw new IndexOutOfBoundsException("putSubImage: y < 0."); + if (x >= nx) + throw new IndexOutOfBoundsException("putSubImage: x >= nx."); + if (y >= ny) + throw new IndexOutOfBoundsException("putSubImage: y >= ny."); + int nxcopy = input.getWidth(); + int nycopy = input.getHeight(); + // Reduces the size of the area to copy if it is too large + if (x + nxcopy > nx) + nxcopy = nx - x; + if (y + nycopy > ny) + nycopy = ny - y; + // Copies lines per lines + double[] dsrc = input.getPixels(); + for (int j = 0; j < nycopy; j++) + System.arraycopy(dsrc, j * nxcopy, pixels, (j + y) * nx + x, nxcopy); + } + + /** + * An ImageAccess object calls this method to set a constant value to all + * pixels of the image. + * + * @param constant + * a constant value + */ + public void setConstant(double constant) { + for (int k = 0; k < size; k++) + pixels[k] = constant; + } + + /** + * Stretches the contrast inside an image so that the gray levels are in the + * range 0 to 255. + */ + public void normalizeContrast() { + double minGoal = 0.0; + double maxGoal = 255.0; + // Search the min and max + double minImage = getMinimum(); + double maxImage = getMaximum(); + // Compute the parameter to rescale the gray levels + double a; + if (minImage - maxImage == 0) { + a = 1.0; + minImage = (maxGoal - minGoal) / 2.0; + } + else + a = (maxGoal - minGoal) / (maxImage - minImage); + for (int i = 0; i < size; i++) { + pixels[i] = (float) (a * (pixels[i] - minImage) + minGoal); + } + } + + /** + * Display an image at a specific position (x, y). + * + * @param title + * a string for the title + * @param loc + * Point for the location + */ + public void show(String title, java.awt.Point loc) { + FloatProcessor fp = createFloatProcessor(); + fp.resetMinAndMax(); + ImagePlus impResult = new ImagePlus(title, fp); + impResult.show(); + ij.gui.ImageWindow window = impResult.getWindow(); + window.setLocation(loc.x, loc.y); + impResult.show(); + } + + /** + * Display an image. + * + * @param title + * a string for the title of the window + */ + public void show(String title) { + FloatProcessor fp = createFloatProcessor(); + fp.resetMinAndMax(); + ImagePlus impResult = new ImagePlus(title, fp); + impResult.show(); + } + + /** + * Compute the absolute value. + */ + public void abs() { + for (int k = 0; k < size; k++) + pixels[k] = Math.abs(pixels[k]); + } + + /** + * Compute the square root of an ImageAccess. + */ + public void sqrt() { + for (int k = 0; k < size; k++) { + pixels[k] = Math.sqrt(pixels[k]); + } + } + + /** + * Raised an ImageAccess object to the power a. + * + * @param a + * input + */ + public void pow(final double a) { + for (int k = 0; k < size; k++) { + pixels[k] = Math.pow(pixels[k], a); + } + } + + /** + * An ImageAccess object calls this method for adding a constant to each + * pixel. + * + * @param constant + * a constant to be added + */ + public void add(double constant) { + for (int k = 0; k < size; k++) + pixels[k] += constant; + } + + /** + * An ImageAccess object calls this method for multiplying a constant to + * each pixel. + * + * @param constant + * a constant to be multiplied + */ + public void multiply(final double constant) { + for (int k = 0; k < size; k++) + pixels[k] *= constant; + } + + /** + * An ImageAccess object calls this method for adding a constant to each + * pixel. + * + * @param constant + * a constant to be subtracted + */ + public void subtract(final double constant) { + for (int k = 0; k < size; k++) + pixels[k] -= constant; + } + + /** + * An ImageAccess object calls this method for dividing a constant to each + * pixel. + * + * @param constant + * a constant to be divided + */ + public void divide(final double constant) { + if (constant == 0.0) + throw new ArrayStoreException("divide: Divide by 0"); + for (int k = 0; k < size; k++) + pixels[k] /= constant; + } + + /** + * An ImageAccess object calls this method for adding two ImageAccess + * objects. + * + * [this = im1 + im2] + * + * The resulting ImageAccess and the two operands should have the same size. + * + * @param im1 + * an ImageAccess object to be added + * @param im2 + * an ImageAccess object to be added + */ + public void add(ImageAccess im1, ImageAccess im2) { + if (im1.getWidth() != nx) + throw new ArrayStoreException("add: incompatible size."); + if (im1.getHeight() != ny) + throw new ArrayStoreException("add: incompatible size."); + if (im2.getWidth() != nx) + throw new ArrayStoreException("add: incompatible size."); + if (im2.getHeight() != ny) + throw new ArrayStoreException("add: incompatible size."); + double[] doubleOperand1 = im1.getPixels(); + double[] doubleOperand2 = im2.getPixels(); + for (int k = 0; k < size; k++) + pixels[k] = doubleOperand1[k] + doubleOperand2[k]; + } + + /** + * An ImageAccess object calls this method for multiplying two ImageAccess + * objects. + * + * The resulting ImageAccess and the two operands should have the same size. + * + * [this = im1 * im2] + * + * @param im1 + * an ImageAccess object to be multiplied + * @param im2 + * an ImageAccess object to be multiplied + */ + + public void multiply(ImageAccess im1, ImageAccess im2) { + if (im1.getWidth() != nx) + throw new ArrayStoreException("multiply: incompatible size."); + if (im1.getHeight() != ny) + throw new ArrayStoreException("multiply: incompatible size."); + if (im2.getWidth() != nx) + throw new ArrayStoreException("multiply: incompatible size."); + if (im2.getHeight() != ny) + throw new ArrayStoreException("multiply: incompatible size."); + double[] doubleOperand1 = im1.getPixels(); + double[] doubleOperand2 = im2.getPixels(); + for (int k = 0; k < size; k++) + pixels[k] = doubleOperand1[k] * doubleOperand2[k]; + } + + /** + * An ImageAccess object calls this method for subtracting two ImageAccess + * objects. + * + * The resulting ImageAccess and the two operands should have the same size. + * + * [this = im1 - im2] + * + * @param im1 + * an ImageAccess object to be subtracted + * @param im2 + * an ImageAccess object to be subtracted + */ + + public void subtract(ImageAccess im1, ImageAccess im2) { + if (im1.getWidth() != nx) + throw new ArrayStoreException("subtract: incompatible size."); + if (im1.getHeight() != ny) + throw new ArrayStoreException("subtract: incompatible size."); + if (im2.getWidth() != nx) + throw new ArrayStoreException("subtract: incompatible size."); + if (im2.getHeight() != ny) + throw new ArrayStoreException("subtract: incompatible size."); + double[] doubleOperand1 = im1.getPixels(); + double[] doubleOperand2 = im2.getPixels(); + for (int k = 0; k < size; k++) + pixels[k] = doubleOperand1[k] - doubleOperand2[k]; + } + + /** + * An ImageAccess object calls this method for dividing two ImageAccess + * objects. + * + * [this = im1 / im2] + * + * The resulting ImageAccess and the two operands should have the same size. + * + * @param im1 + * numerator + * @param im2 + * denominator + */ + + public void divide(ImageAccess im1, ImageAccess im2) { + if (im1.getWidth() != nx) + throw new ArrayStoreException("divide: incompatible size."); + if (im1.getHeight() != ny) + throw new ArrayStoreException("divide: incompatible size."); + if (im2.getWidth() != nx) + throw new ArrayStoreException("divide: incompatible size."); + if (im2.getHeight() != ny) + throw new ArrayStoreException("divide: incompatible size."); + double[] doubleOperand1 = im1.getPixels(); + double[] doubleOperand2 = im2.getPixels(); + for (int k = 0; k < size; k++) + pixels[k] = doubleOperand1[k] / doubleOperand2[k]; + } + +} diff --git a/src/bilib/src/wavelets/WaveSpline.java b/src/bilib/src/wavelets/WaveSpline.java new file mode 100644 index 0000000..9d16d4a --- /dev/null +++ b/src/bilib/src/wavelets/WaveSpline.java @@ -0,0 +1,423 @@ +package wavelets; + +/** + * Spline wavelets transformation + * <hr> + * <p> + * <b>Organisation</b>: <a href="http://bigwww.epfl.ch">Biomedical Imaging + * Group</a> (BIG), Ecole Polytechnique Federale de Lausanne (EPFL), Lausanne, + * Switzerland + * </p> + * <p> + * <b>Authors</b>: Daniel Sage + * </p> + * <p> + * <b>Reference</b>: D. Sage, M. Unser, "<a + * href="http://bigwww.epfl.ch/publications/sage0303.html">Teaching + * Image-Processing Programming in Java</a>," IEEE Signal Processing + * Magazine, vol. 20, no. 6, pp. 43-52, November 2003. + * </p> + * <p> + * More information: http://bigwww.epfl.ch/teaching/iplabsite/index.php + * </p> + * <p> + * Other relevant information are available at: http://bigwww.epfl.ch/ + * </p> + * <hr> + * You'll be free to use this software for research purposes, but you should not + * redistribute it without our consent. In addition, we expect you to include a + * citation or acknowledgement whenever you present or publish results that are + * based on it. + */ + +public class WaveSpline { + + /** + * Perform an wavelet transformation of the ImageObject calling this method + * with n scale. The size of image should be a interger factor of 2 at the + * power n. The input is an image. The result is the wavelet coefficients. + * It is put in the ImageObject calling this method. + * + * @param in + * an ImageAcess object provided by ImageJ + * @param n + * a integer value giving the number of scale + */ + static public ImageAccess analysis(ImageAccess in, int order, int n) { + + // Compute the size to the fine and coarse levels + int div = (int) Math.pow(2.0, (double) (n - 1)); + int nxfine = in.getWidth(); + int nyfine = in.getHeight(); + int nxcoarse = nxfine / div; + int nycoarse = nyfine / div; + + // Declare the object image + ImageAccess sub; + + // Initialization + int nx = nxfine; + int ny = nyfine; + ImageAccess out = in.duplicate(); + + // From fine to coarse main loop + for (int i = 0; i < n; i++) { + + // Create a new image array of size [nx,ny] + sub = new ImageAccess(nx, ny); + + // Copy in[] into image[] + out.getSubImage(0, 0, sub); + + // Apply the Wavelet splitting + sub = split(sub, order); + + // Put the result image[] into in[] + out.putSubImage(0, 0, sub); + + // Reduce the size by a factor of 2 + nx = nx / 2; + ny = ny / 2; + } + return out; + } + + /** + * Perform 1 iteration of the wavelet transformation of an ImageObject. The + * algorithm use the separability of the wavelet transformation. The result + * of the computation is put in the ImageObject calling this method. + * + * @param in + * an ImageAcess object provided by ImageJ + */ + static private ImageAccess split(ImageAccess in, int order) { + int nx = in.getWidth(); + int ny = in.getHeight(); + ImageAccess out = new ImageAccess(nx, ny); + + WaveSplineFilter wf = new WaveSplineFilter(order); + + if (nx >= 1) { + double rowin[] = new double[nx]; + double rowout[] = new double[nx]; + for (int y = 0; y < ny; y++) { + in.getRow(y, rowin); + split_1D(rowin, rowout, wf.h, wf.g); + out.putRow(y, rowout); + } + } + else { + // out.copy(in); + out = in.duplicate(); + } + + if (ny > 1) { + double colin[] = new double[ny]; + double colout[] = new double[ny]; + for (int x = 0; x < nx; x++) { + out.getColumn(x, colin); + split_1D(colin, colout, wf.h, wf.g); + out.putColumn(x, colout); + } + } + + return out; + } + + /** + * Perform 1 iteration of the wavelet transformation of a 1D vector using + * the spline wavelet transformation. The output vector has the same size of + * the input vector and it contains first the low pass part of the wavelet + * transform and then the high pass part of the wavelet transformation. + * + * @param vin + * input, a double 1D vector + * @param vout + * output, a double 1D vector + * @param h + * input, a double 1D vector, lowpass filter + * @param g + * input, a double 1D vector, highpass filter + */ + static private void split_1D(double vin[], double vout[], double h[], double g[]) { + int n = vin.length; + int n2 = n / 2; + int nh = h.length; + int ng = g.length; + + // /////////////////////////////////////////// + // Order is 0 -> Haar Transform + // /////////////////////////////////////////// + if ((nh <= 1) || (ng <= 1)) { + double sqrt2 = Math.sqrt(2); + int j; + for (int i = 0; i < n2; i++) { + j = 2 * i; + vout[i] = (vin[j] + vin[j + 1]) / sqrt2; + vout[i + n2] = (vin[j] - vin[j + 1]) / sqrt2; + } + return; + } + + // /////////////////////////////////////////// + // Order is higher than 0 + // /////////////////////////////////////////// + double pix; + int j, k, j1, j2; + int period = 2 * n - 2; // period for mirror boundary conditions + + for (int i = 0; i < n2; i++) { + j = i * 2; + pix = vin[j] * h[0]; + for (k = 1; k < nh; k++) { // Low pass part + j1 = j - k; + if (j1 < 0) { // Mirror conditions + while (j1 < 0) + j1 += period; // Periodize + if (j1 >= n) + j1 = period - j1; // Symmetrize + } + j2 = j + k; + if (j2 >= n) { // Mirror conditions + while (j2 >= n) + j2 -= period; // Periodize + if (j2 < 0) + j2 = -j2; // Symmetrize + } + pix = pix + h[k] * (vin[j1] + vin[j2]); + } + vout[i] = pix; + + j = j + 1; + pix = vin[j] * g[0]; // High pass part + for (k = 1; k < ng; k++) { + j1 = j - k; + if (j1 < 0) { // Mirror conditions + while (j1 < 0) + j1 += period; // Periodize + if (j1 >= n) + j1 = period - j1; // Symmetrize + } + j2 = j + k; + if (j2 >= n) { // Mirror conditions + while (j2 >= n) + j2 -= period; // Periodize + if (j2 < 0) + j2 = -j2; // Symmetrize + } + pix = pix + g[k] * (vin[j1] + vin[j2]); + } + vout[i + n2] = pix; + } + } + + /** + * Perform an inverse wavelet transformation of the ImageObject calling this + * method with n scale. The size of image should be a interger factor of 2 + * at the power n. The input is the results of a wavelet transformation. The + * result is the reconstruction. It is put in the ImageObject calling this + * method. + * + * @param in + * an ImageAcess object provided by ImageJ + * @param n + * a integer value giving the number of scale + */ + + static public ImageAccess synthesis(ImageAccess in, int order, int n) { + // Compute the size to the fine and coarse levels + int div = (int) Math.pow(2.0, (double) (n - 1)); + int nxcoarse = in.getWidth() / div; + int nycoarse = in.getHeight() / div; + + // Declare the object image + ImageAccess sub; + + // Initialisazion + int nx = nxcoarse; + int ny = nycoarse; + ImageAccess out = in.duplicate(); + + // From fine to coarse main loop + for (int i = 0; i < n; i++) { + + // Create a new image array of size [nx,ny] + sub = new ImageAccess(nx, ny); + + // Copy in[] into image[] + out.getSubImage(0, 0, sub); + + // Apply the Wavelet splitting + sub = merge(sub, order); + + // Put the result image[] into in[] + out.putSubImage(0, 0, sub); + // Reduce the size by a factor of 2 + nx = nx * 2; + ny = ny * 2; + } + return out; + } + + /** + * Perform 1 iteration of the inverse wavelet transformation of an + * ImageObject. The algorithm use the separability of the wavelet + * transformation. The result of the computation is put in the ImageObject + * calling this method. + * + * @param in + * an ImageAcess object provided by ImageJ + */ + static private ImageAccess merge(ImageAccess in, int order) { + int nx = in.getWidth(); + int ny = in.getHeight(); + ImageAccess out = new ImageAccess(nx, ny); + WaveSplineFilter wf = new WaveSplineFilter(order); + + if (nx >= 1) { + int nx2 = nx / 2; + double rowin[] = new double[nx]; + double rowout[] = new double[nx]; + for (int y = 0; y < ny; y++) { + in.getRow(y, rowin); + merge_1D(rowin, rowout, wf.h, wf.g); + out.putRow(y, rowout); + } + } + else { + out = in.duplicate(); + } + + if (ny > 1) { + int ny2 = ny / 2; + double colin[] = new double[ny]; + double colout[] = new double[ny]; + for (int x = 0; x < nx; x++) { + out.getColumn(x, colin); + merge_1D(colin, colout, wf.h, wf.g); + out.putColumn(x, colout); + } + } + return out; + } + + /** + * Perform 1 iteration of the inverse wavelet transformation of a 1D vector + * using the Spline wavelet transformation. The output vector has the same + * size of the input vector and it contains the reconstruction of the input + * signal. The input vector constains first the low pass part of the wavelet + * transform and then the high pass part of the wavelet transformation. + * + * @param vin + * input, a double 1D vector + * @param vout + * output, a double 1D vector + * @param h + * input, a double 1D vector, lowpass filter + * @param g + * input, a double 1D vector, highpass filter + */ + static private void merge_1D(double vin[], double vout[], double h[], double g[]) { + int n = vin.length; + int n2 = n / 2; + int nh = h.length; + int ng = g.length; + + // /////////////////////////////////////////// + // Order is 0 -> Haar Transform + // /////////////////////////////////////////// + if ((nh <= 1) || (ng <= 1)) { + double sqrt2 = Math.sqrt(2); + for (int i = 0; i < n2; i++) { + vout[2 * i] = (vin[i] + vin[i + n2]) / sqrt2; + vout[2 * i + 1] = (vin[i] - vin[i + n2]) / sqrt2; + } + return; + } + + // /////////////////////////////////////////// + // Order is higher than 0 + // /////////////////////////////////////////// + double pix1, pix2; + int j, k, kk, i1, i2; + int k01 = (nh / 2) * 2 - 1; + int k02 = (ng / 2) * 2 - 1; + + int period = 2 * n2 - 1; // period for mirror boundary conditions + + for (int i = 0; i < n2; i++) { + j = 2 * i; + pix1 = h[0] * vin[i]; + for (k = 2; k < nh; k += 2) { + i1 = i - (k / 2); + if (i1 < 0) { + i1 = (-i1) % period; + if (i1 >= n2) + i1 = period - i1; + } + i2 = i + (k / 2); + if (i2 > n2 - 1) { + i2 = i2 % period; + if (i2 >= n2) + i2 = period - i2; + } + pix1 = pix1 + h[k] * (vin[i1] + vin[i2]); + } + + pix2 = 0.; + for (k = -k02; k < ng; k += 2) { + kk = Math.abs(k); + i1 = i + (k - 1) / 2; + if (i1 < 0) { + i1 = (-i1 - 1) % period; + if (i1 >= n2) + i1 = period - 1 - i1; + } + if (i1 >= n2) { + i1 = i1 % period; + if (i1 >= n2) + i1 = period - 1 - i1; + } + pix2 = pix2 + g[kk] * vin[i1 + n2]; + } + + vout[j] = pix1 + pix2; + + j = j + 1; + pix1 = 0.; + for (k = -k01; k < nh; k += 2) { + kk = Math.abs(k); + i1 = i + (k + 1) / 2; + if (i1 < 0) { + i1 = (-i1) % period; + if (i1 >= n2) + i1 = period - i1; + } + if (i1 >= n2) { + i1 = (i1) % period; + if (i1 >= n2) + i1 = period - i1; + } + pix1 = pix1 + h[kk] * vin[i1]; + } + pix2 = g[0] * vin[i + n2]; + for (k = 2; k < ng; k += 2) { + i1 = i - (k / 2); + if (i1 < 0) { + i1 = (-i1 - 1) % period; + if (i1 >= n2) + i1 = period - 1 - i1; + } + i2 = i + (k / 2); + if (i2 > n2 - 1) { + i2 = i2 % period; + if (i2 >= n2) + i2 = period - 1 - i2; + } + pix2 = pix2 + g[k] * (vin[i1 + n2] + vin[i2 + n2]); + } + vout[j] = pix1 + pix2; + } + } + +} diff --git a/src/bilib/src/wavelets/WaveSplineFilter.java b/src/bilib/src/wavelets/WaveSplineFilter.java new file mode 100644 index 0000000..767390e --- /dev/null +++ b/src/bilib/src/wavelets/WaveSplineFilter.java @@ -0,0 +1,181 @@ +package wavelets; + +/** + * Spline wavelets transformation + * <hr> + * <p> + * <b>Organisation</b>: <a href="http://bigwww.epfl.ch">Biomedical Imaging + * Group</a> (BIG), Ecole Polytechnique Federale de Lausanne (EPFL), Lausanne, + * Switzerland + * </p> + * <p> + * <b>Authors</b>: Daniel Sage + * </p> + * <p> + * <b>Reference</b>: D. Sage, M. Unser, "<a + * href="http://bigwww.epfl.ch/publications/sage0303.html">Teaching + * Image-Processing Programming in Java</a>," IEEE Signal Processing + * Magazine, vol. 20, no. 6, pp. 43-52, November 2003. + * </p> + * <p> + * More information: http://bigwww.epfl.ch/teaching/iplabsite/index.php + * </p> + * <p> + * Other relevant information are available at: http://bigwww.epfl.ch/ + * </p> + * <hr> + * You'll be free to use this software for research purposes, but you should not + * redistribute it without our consent. In addition, we expect you to include a + * citation or acknowledgement whenever you present or publish results that are + * based on it. + */ + +public class WaveSplineFilter { + + /** + * real lowpass filter. + */ + public double h[]; + + /** + * real highpass filter. + */ + public double g[]; + + WaveSplineFilter(int order) { + switch (order) { + + case 0: + h = new double[1]; + h[0] = Math.sqrt(2.0); + break; + + case 1: + h = new double[47]; + h[0] = 0.81764640621546; + h[1] = 0.39729708810751; + h[2] = -0.06910098743038; + h[3] = -0.05194534825542; + h[4] = 0.01697104840045; + h[5] = 0.00999059568192; + h[6] = -0.00388326235731; + h[7] = -0.00220195129177; + h[8] = 0.00092337104427; + h[9] = 0.00051163604209; + h[10] = -0.00022429633694; + h[11] = -0.00012268632858; + h[12] = 0.00005535633860; + h[13] = 0.00003001119291; + h[14] = -0.00001381880394; + h[15] = -0.00000744435611; + h[16] = 0.00000347980027; + h[17] = 0.00000186561005; + h[18] = -0.00000088225856; + h[19] = -0.00000047122304; + h[20] = 0.00000022491351; + h[21] = 0.00000011976480; + h[22] = -0.00000005759525; + h[23] = -0.00000003059265; + h[24] = 0.00000001480431; + h[25] = 0.00000000784714; + h[26] = -0.00000000381742; + h[27] = -0.00000000201987; + h[28] = 0.00000000098705; + h[29] = 0.00000000052147; + h[30] = -0.00000000025582; + h[31] = -0.00000000013497; + h[32] = 0.00000000006644; + h[33] = 0.00000000003501; + h[34] = -0.00000000001729; + h[35] = -0.00000000000910; + h[36] = 0.00000000000451; + h[37] = 0.00000000000237; + h[38] = -0.00000000000118; + h[39] = -0.00000000000062; + h[40] = 0.00000000000031; + h[41] = 0.00000000000016; + h[42] = -0.00000000000008; + h[43] = -0.00000000000004; + h[44] = 0.00000000000002; + h[45] = 0.00000000000001; + break; + + case 3: + double temp[] = { 0.76613005375980, 0.43392263358931, -0.05020172467149, -0.11003701838811, 0.03208089747022, 0.04206835144072, -0.01717631549201, -0.01798232098097, + 0.00868529481309, 0.00820147720600, -0.00435383945777, -0.00388242526560, 0.00218671237015, 0.00188213352389, -0.00110373982039, -0.00092719873146, 0.00055993664336, + 0.00046211522752, -0.00028538371867, -0.00023234729403, 0.00014604186978, 0.00011762760216, -0.00007499842461, -0.00005987934057, 0.00003863216129, 0.00003062054907, + -0.00001995254847, -0.00001571784835, 0.00001032898225, 0.00000809408097, -0.00000535805976 - 0.00000417964096, 0.00000278450629, 0.00000216346143, -0.00000144942177, + -0.00000112219704, 0.00000075557065, 0.00000058316635, -0.00000039439119, -0.00000030355006, 0.00000020610937, 0.00000015823692, -0.00000010783016, -0.00000008259641, + 0.00000005646954, 0.00000004316539, -0.00000002959949, -0.00000002258313, 0.00000001552811, 0.00000001182675, -0.00000000815248, -0.00000000619931, 0.00000000428324, + 0.00000000325227, -0.00000000225188, -0.00000000170752, 0.00000000118465, 0.00000000089713, -0.00000000062357, -0.00000000047167, 0.00000000032841, 0.00000000024814, + -0.00000000017305, -0.00000000013062, 0.00000000009123, 0.00000000006879, -0.00000000004811, -0.00000000003625, 0.00000000002539, 0.00000000001911, -0.00000000001340, + -0.00000000001008, 0.00000000000708, 0.00000000000532, -0.00000000000374, -0.00000000000281, 0.00000000000198, 0.00000000000148, -0.00000000000104, -0.00000000000078, + 0.00000000000055, 0.00000000000041, -0.00000000000029, -0.00000000000022, 0.00000000000015, 0.00000000000012, -0.00000000000008 - 0.00000000000006, 0.00000000000004, + 0.00000000000003, -0.00000000000002, -0.00000000000002, 0.00000000000001, 0.00000000000001, -0.00000000000001, -0.00000000000000 }; + h = temp; + break; + + case 5: + h = new double[42]; + h[0] = 0.74729; + h[1] = 0.4425; + h[2] = -0.037023; + h[3] = -0.12928; + h[4] = 0.029477; + h[5] = 0.061317; + h[6] = -0.021008; + h[7] = -0.032523; + h[8] = 0.014011; + h[9] = 0.01821; + h[10] = -0.0090501; + h[11] = -0.010563; + h[12] = 0.0057688; + h[13] = 0.0062796; + h[14] = -0.0036605; + h[15] = -0.0037995; + h[16] = 0.0023214; + h[17] = 0.0023288; + h[18] = -0.0014738; + h[19] = -0.0014414; + h[20] = 0.00093747; + h[21] = 0.00089889; + h[22] = -0.00059753; + h[23] = -0.00056398; + h[24] = 0.00038165; + h[25] = 0.00035559; + h[26] = -0.00024423; + h[27] = -0.00022512; + h[28] = 0.00015658; + h[29] = 0.00014301; + h[30] = -0.00010055; + h[31] = -9.1113e-05; + h[32] = 6.4669e-05; + h[33] = 5.8198e-05; + h[34] = -4.1649e-05; + h[35] = -3.7256e-05; + h[36] = 2.729e-05; + h[37] = 2.458e-05; + h[38] = -2.2593e-05; + h[39] = -3.5791e-05; + h[40] = -1.7098e-05; + h[41] = -2.9619e-06; + break; + } + + g = new double[h.length]; + if (order > 0) { + g[0] = h[0]; + for (int k = 1; k < h.length; k++) { + if ((k / 2) * 2 == k) + g[k] = h[k]; + else + g[k] = -h[k]; + } + } + else { + g[0] = -h[0]; + } + + } + +} \ No newline at end of file diff --git a/src/src/eigenpsf/AdvancedSettingsPanel.java b/src/src/eigenpsf/AdvancedSettingsPanel.java index 68abe9e..c48457a 100644 --- a/src/src/eigenpsf/AdvancedSettingsPanel.java +++ b/src/src/eigenpsf/AdvancedSettingsPanel.java @@ -21,7 +21,6 @@ import eigenpsf.gui.SpinnerInfoRangeInteger; public class AdvancedSettingsPanel extends JPanel implements ChangeListener, KeyListener, ItemListener { - private SpinnerInfoRangeDouble spnQuantile = new SpinnerInfoRangeDouble(Params.quantile, 0.01, 1000, 0.1, "Threshold to discard bad beads from the quality score (see related column in the result table).", "http://bigwww.epfl.ch/"); private SpinnerInfoRangeInteger spnNumberPoints = new SpinnerInfoRangeInteger(Params.nthin, 1, 100, 1, "Number of thin-plate splines used to model the global background. The smaller it is, the smoother the estimated background will be..", "http://bigwww.epfl.ch/"); private SpinnerInfoRangeInteger spnScaleBack = new SpinnerInfoRangeInteger(Params.scaleBack, 1, 100, 1, "Data reduction factor to speed up computation (e.g., if set to 2, then the estimation of the global background will be performed on two-times subsampled images)", "http://bigwww.epfl.ch/"); private SpinnerInfoRangeDouble spnScaleMin = new SpinnerInfoRangeDouble(Params.smin, 0.1, 10, 0.1, "Minimum scaling factor of the basis used in SIFT detection.", "http://bigwww.epfl.ch/"); @@ -95,11 +94,7 @@ public class AdvancedSettingsPanel extends JPanel implements ChangeListener, Key i++; pn1.place(i, 0, 4, 1, "<html> <b> Compute EigenPSF </b> </html>"); i++; - pn1.place(i, 0, 4, 1, new JSeparator()); - i++; - pn1.place(i, 1,"Quality threshold"); - pn1.place(i, 2,spnQuantile); - pn1.place(i, 3,spnQuantile.info); + pn1.place(i, 0, 4, 1, new JSeparator()); i++; pn1.place(i, 1,"Beads normalization"); pn1.place(i, 2,cmbNormalization); @@ -190,7 +185,7 @@ public class AdvancedSettingsPanel extends JPanel implements ChangeListener, Key setLayout(new BorderLayout()); add(pn1, BorderLayout.CENTER); - spnQuantile.addChangeListener(this); + //spnQuantile.addChangeListener(this); spnNumberPoints.addChangeListener(this); spnScaleBack.addChangeListener(this); spnScaleMin.addChangeListener(this); @@ -199,7 +194,7 @@ public class AdvancedSettingsPanel extends JPanel implements ChangeListener, Key spnNbBasis.addChangeListener(this); cmbNormalization.addItemListener(this); - spnQuantile.addKeyListener(this); + //spnQuantile.addKeyListener(this); spnNumberPoints.addKeyListener(this); spnScaleBack.addKeyListener(this); spnScaleMin.addKeyListener(this); @@ -229,7 +224,6 @@ public class AdvancedSettingsPanel extends JPanel implements ChangeListener, Key private void updateParams() { try { - Params.quantile = spnQuantile.get(); Params.nthin = spnNumberPoints.get(); //Params.nthin2D = spnNumberPoints.get(); //Params.nthin3D = spnNumberPoints.get(); @@ -256,7 +250,7 @@ public class AdvancedSettingsPanel extends JPanel implements ChangeListener, Key private void setGUI() { try { - spnQuantile.set(Params.quantile); + //spnQuantile.set(Params.quantile); spnNumberPoints.set(Params.nthin); //spnNumberPoints.set(Params.nthin2D); //spnNumberPoints.set(Params.nthin3D); diff --git a/src/src/eigenpsf/Constants.java b/src/src/eigenpsf/Constants.java index 533af14..b581c6a 100644 --- a/src/src/eigenpsf/Constants.java +++ b/src/src/eigenpsf/Constants.java @@ -5,7 +5,7 @@ public class Constants { static final public String title = "(c) CNRS and BIG EPFL"; static final public String name = "Eigen PSF"; static final public String version = "0.3"; - static final public String url = "http://bigwww.epfl.ch/"; + static final public String url = "https://gitlab.irit.fr/mambo/eigenpsf-extractor"; static final public String copyright = "(c) CNRS and BIG EPFL"; } diff --git a/src/src/eigenpsf/SettingsDialog.java b/src/src/eigenpsf/SettingsDialog.java index e11e058..a6d124f 100644 --- a/src/src/eigenpsf/SettingsDialog.java +++ b/src/src/eigenpsf/SettingsDialog.java @@ -49,6 +49,8 @@ public class SettingsDialog extends JDialog implements ActionListener, KeyListen private SpinnerRangeInteger spnPSFZ = new SpinnerRangeInteger(Params.psf_visible_width[2], 1, 9999, 2); private JRadioButton bn2D = new JRadioButton("2D", true); private JRadioButton bn3D = new JRadioButton("3D"); + private SpinnerInfoRangeDouble spnQuantile = new SpinnerInfoRangeDouble(Params.quantile, 0.01, 1000, 0.1, "Threshold to discard bad beads from the quality score (see related column in the result table).", "http://bigwww.epfl.ch/"); + private JButton bnGetROI = new JButton("Get ROI"); private JTabbedPane tab = new JTabbedPane(); @@ -138,23 +140,18 @@ public class SettingsDialog extends JDialog implements ActionListener, KeyListen i++; pn.place(i, 0, 4, 1, new JSeparator()); i++; - pn.place(i, 0, 4, 1, "<html><b>Modality</b></html>"); - pn.place(i, 1, cmbModality); - pn.place(i, 2, bn2D); - pn.place(i, 3, bn3D); + pn.place(i, 0, 4, 1, "<html><b>2D/3D</b></html>"); + //pn.place(i, 1, cmbModality); + pn.place(i, 1, bn2D); + pn.place(i, 2, bn3D); i++; - pn.place(i, 0, "PSF Support (XY)"); + pn.place(i, 0, "PSF Support XY (px)"); pn.place(i, 1, spnPSFXY); - pn.place(i, 2, "px"); - pn.place(i, 3, bnGetROI); + pn.place(i, 2, bnGetROI); i++; - pn.place(i, 0, "PSF Support (Z)"); + pn.place(i, 0, "PSF Support Z (nb slices)"); pn.place(i, 1, spnPSFZ); - pn.place(i, 2, 2, 1, "number of slices"); - i++; - pn.place(i, 0, "Eigen Components"); - pn.place(i, 1, spnNbEigen); - pn.place(i, 2, spnNbEigen.info); + i++; pn.place(i, 0, 4, 1, new JSeparator()); @@ -163,12 +160,26 @@ public class SettingsDialog extends JDialog implements ActionListener, KeyListen i++; pn.place(i, 0, 4, 1, new JSeparator()); i++; - pn.place(i, 0, "<html><b>Detection Threshold</b></html>"); + pn.place(i, 0, "Detection Threshold"); //pn.place(i, 1, 3, 1, cmbDetection); //i++; //pn.place(i, 1, "Threshold"); pn.place(i, 1, spnThreshold); pn.place(i, 2, spnThreshold.info); + i++; + pn.place(i, 0, 4, 1, new JSeparator()); + i++; + pn.place(i, 0, 4, 1, "<html> <b> Compute EigenPSF </b> </html>"); + i++; + pn.place(i, 0, 4, 1, new JSeparator()); + i++; + pn.place(i, 0, "Eigen Components"); + pn.place(i, 1, spnNbEigen); + pn.place(i, 2, spnNbEigen.info); + i++; + pn.place(i, 0,"Quality threshold"); + pn.place(i, 1,spnQuantile); + pn.place(i, 2,spnQuantile.info); //i++; //pn.place(i, 0, "<html><b>Registration</b></html>"); @@ -250,6 +261,7 @@ public class SettingsDialog extends JDialog implements ActionListener, KeyListen Params.thresholdDetection = spnThreshold.get(); Params.NeigenElements = spnNbEigen.get(); Params.dim = bn2D.isSelected() ? 2 : 3; + Params.quantile = spnQuantile.get(); } catch (Exception ex) { } } diff --git a/src/src/eigenpsf/processing/Processing.java b/src/src/eigenpsf/processing/Processing.java index c4b416e..7c8afb8 100644 --- a/src/src/eigenpsf/processing/Processing.java +++ b/src/src/eigenpsf/processing/Processing.java @@ -117,6 +117,7 @@ public class Processing { // HyperMatrix tmp2 = large_patch.getPatch(center,Params.psf_visible_width); //tmp2.display("Before register patch",null); shift = reg_meth.process(large_patch); // Register patch + //System.out.printf("%f | %f | %f \n", shift[0],shift[1],shift[2]); // -- Update patch position double xx = patch.xp + shift[0]; double yy = patch.yp + shift[1]; @@ -124,28 +125,28 @@ public class Processing { patch.xp = (int) Math.round(xx); patch.yp = (int) Math.round(yy); patch.zp = (int) Math.round(zz); - patch.x = patch.xp - xx; - patch.y = patch.yp - yy; - patch.z = patch.zp - zz; + patch.x = xx - patch.xp; + patch.y = yy - patch.yp; + patch.z = zz - patch.zp; // -- Extract and store patch - int[] shift_fl = { (int) Math.round(shift[0]) + center[0], - (int) Math.round(shift[1]) + center[1], (int) Math.round(shift[2]) + center[2] }; + int[] shift_fl = { center[0] + (int) Math.round(shift[0]), + center[1] + (int) Math.round(shift[1]) , center[2] + (int) Math.round(shift[2]) }; patch.data = large_patch.getPatch(shift_fl, Params.psf_visible_width); //patch.data.display("After register patch",null); // -- Preprocess patch for SVD //project.patchesForSvd.add(large_patch.imtranslate(patch.x, patch.y, patch.z) // .getPatch(shift_fl, Params.psf_visible_width).normalize()); - patch.data_register = large_patch.imtranslate(patch.x, patch.y, patch.z).getPatch(shift_fl, Params.psf_visible_width).normalize(); + patch.data_register = large_patch.imtranslate(-patch.x, -patch.y,- patch.z).getPatch(shift_fl, Params.psf_visible_width).normalize(); //project.patchesForSvd.get(project.patchesForSvd.size()-1).display("After translation patch",null); } } } // Prune bad beads + project.isPreprocessed = true; pruneBeads(project); Log.progress("End Preprocess patches", 100); - project.isPreprocessed = true; } public static void pruneBeads(Project project) { diff --git a/src/src/eigenpsf/project/MainDialog.java b/src/src/eigenpsf/project/MainDialog.java index ea4f793..578b1bc 100644 --- a/src/src/eigenpsf/project/MainDialog.java +++ b/src/src/eigenpsf/project/MainDialog.java @@ -7,7 +7,10 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.util.ArrayList; import javax.swing.BorderFactory; @@ -23,12 +26,19 @@ import eigenpsf.Constants; import eigenpsf.Log; import eigenpsf.Params; import eigenpsf.SettingsDialog; +import eigenpsf.data.HyperMatrix; import eigenpsf.filemanager.IO; import eigenpsf.gui.WalkBar; import eigenpsf.lib.Loader; import eigenpsf.processing.ProcessingPanel; +import eigenpsf.stack.Patch; import eigenpsf.stack.ZStack; +import eigenpsf.stack.Patches; +import ij.IJ; +import ij.ImagePlus; import ij.gui.GUI; +import ij.process.Blitter; +import ij.process.ImageProcessor; public class MainDialog extends JDialog implements ActionListener, WindowListener { @@ -92,7 +102,7 @@ public class MainDialog extends JDialog implements ActionListener, WindowListene @Override public void actionPerformed(ActionEvent e) { - if (e.getSource() == toolbar.bnReset) { + /*if (e.getSource() == toolbar.bnReset) { int ret = JOptionPane.showConfirmDialog(null, "Do you want to clear all the project?", "Warning", JOptionPane.YES_NO_OPTION); if (ret == JOptionPane.YES_OPTION) { project.reset(); @@ -101,10 +111,11 @@ public class MainDialog extends JDialog implements ActionListener, WindowListene Log.clear(); } } - else if (e.getSource() == toolbar.bnSetting) { + else */ + if (e.getSource() == toolbar.bnSetting) { settingsDialog.setVisible(true); } - else if (e.getSource() == toolbar.bnOpen) { + /*else if (e.getSource() == toolbar.bnOpen) { project.path = IO.browseOpenProject(project.path); String filename = project.path + File.separator + "listfiles.csv"; File file = new File(filename); @@ -116,12 +127,109 @@ public class MainDialog extends JDialog implements ActionListener, WindowListene else { Log.write(project.path + " is empty"); } - } + }*/ else if (e.getSource() == toolbar.bnSave) { project.path = IO.browseSaveProject(project.path); - if (project.path != null) + if (project.path != null) { project.save(project.path); - + pnProcessing.table.update(project); + + File file = new File(project.path + File.separator + "patchesTable.csv"); + try { + System.out.printf("Coucou"); + BufferedWriter buffer = new BufferedWriter(new FileWriter(file)); + int nrows = pnProcessing.table.getRowCount(); + int ncols = pnProcessing.table.getColumnCount(); + System.out.printf("Coucou-1"); + String row = ""; + //for (int c = 0; c < ncols; c++) + // row += pnProcessing.table.columns.get(c).getHeader() + (c == ncols - 1 ? "" : "\t "); + //buffer.write(row + "\n"); + // Add header manually ... + buffer.write("Image name \t ID \t X \t Y \t Z \t Quality \t Dist \t Max \t Valid \n"); + + for (int r = 0; r < nrows; r++) { + row = ""; + for (int c = 0; c < ncols; c++) { + Object O = pnProcessing.table.getModel().getValueAt(r, c); + row += O + (c == ncols - 1 ? "" : "\t "); + } + buffer.write(row + "\n"); + } + buffer.close(); + } + catch (Exception ex) { + System.out.println(ex); + } + + int[] sz; + if (Params.dim == 2) { + sz = new int[] { Params.psf_visible_width[0], Params.psf_visible_width[1],1 }; + } + else { + sz = new int[] { Params.psf_visible_width[0], Params.psf_visible_width[1], Params.psf_visible_width[2] }; + } + Patches sel = project.getAllPatchesSortedByGlobId(); + ImagePlus imp_raw = IJ.createHyperStack("Raw Patches", sz[0], sz[1], 1, sz[2], sel.size(), 32); + ImagePlus imp_process = IJ.createHyperStack("Processed Patches", sz[0], sz[1], 1, sz[2], sel.size(), 32); + ImagePlus imp_back = IJ.createHyperStack("Background Patches", sz[0], sz[1], 1, sz[2], sel.size(), 32); + ImagePlus imp_proj = IJ.createHyperStack("Projected Patches", sz[0], sz[1], 1, sz[2], sel.size(), 32); + int i = 1; + for (Patch patch : sel) { + int pos[]; + if (Params.dim == 2) { + pos = new int[] { patch.xp, patch.yp, 0}; + } + else { + pos = new int[] { patch.xp, patch.yp, patch.zp }; + } + HyperMatrix im = patch.stack.getHyperMatrix(); + ImagePlus p_raw = HyperMatrix.HyperMatrix2ImagePlus(im.getPatch(pos, sz)); + ImagePlus p_back = HyperMatrix.HyperMatrix2ImagePlus(im.getPatch(pos, sz).sub(patch.data)); + ImagePlus p_proj = HyperMatrix.HyperMatrix2ImagePlus(project.eigen.projPatch(patch)); + ImagePlus p_process; + if (project.isPreprocessed) + p_process = HyperMatrix.HyperMatrix2ImagePlus(patch.data_register); + else + p_process = HyperMatrix.HyperMatrix2ImagePlus(patch.data); + if (p_raw != null) { + for (int z = 1; z <= Math.min(p_raw.getNSlices(), sz[2]); z++) { + // Raw + ImageProcessor ip = p_raw.getStack().getProcessor(z); + imp_raw.setPositionWithoutUpdate(1, z, i); + ImageProcessor op = imp_raw.getProcessor(); + op.copyBits(ip, 0, 0, Blitter.COPY); + // Processed + ip = p_process.getStack().getProcessor(z); + imp_process.setPositionWithoutUpdate(1, z, i); + op = imp_process.getProcessor(); + op.copyBits(ip, 0, 0, Blitter.COPY); + // Back + ip = p_back.getStack().getProcessor(z); + imp_back.setPositionWithoutUpdate(1, z, i); + op = imp_back.getProcessor(); + op.copyBits(ip, 0, 0, Blitter.COPY); + // Proj + ip = p_proj.getStack().getProcessor(z); + imp_proj.setPositionWithoutUpdate(1, z, i); + op = imp_proj.getProcessor(); + op.copyBits(ip, 0, 0, Blitter.COPY); + } + } + i++; + } + ij.IJ.save(imp_raw,project.path + File.separator + "RawPatches.tif"); + ij.IJ.save(imp_process,project.path + File.separator + "ProcessedPatches.tif"); + ij.IJ.save(imp_back,project.path + File.separator + "BackgroundPatches.tif"); + ij.IJ.save(imp_proj,project.path + File.separator + "ProjectedPatches.tif"); + + + ImagePlus eig = HyperMatrix.HyperMatrix2ImagePlus(project.eigen.eigenElements); + ij.IJ.save(eig,project.path + File.separator + "EigenPSF.tif"); + + + + } } else if (e.getSource() == bnAdd) { diff --git a/src/src/eigenpsf/project/Toolbar.java b/src/src/eigenpsf/project/Toolbar.java index 27355ca..95b990a 100644 --- a/src/src/eigenpsf/project/Toolbar.java +++ b/src/src/eigenpsf/project/Toolbar.java @@ -47,15 +47,15 @@ public class Toolbar extends JToolBar implements ActionListener { public Toolbar(MainDialog main) { this.setFloatable(false); JPanel tool1 = new JPanel(new GridLayout(1, 7, 0, 0)); - tool1.add(bnReset); - tool1.add(bnOpen); + //tool1.add(bnReset); + //tool1.add(bnOpen); tool1.add(bnSave); tool1.add(new JSeparator(SwingConstants.VERTICAL)); tool1.add(bnSetting); tool1.add(new JSeparator(SwingConstants.VERTICAL)); JPanel tool2 = new JPanel(new GridLayout(1, 4, 0, 0)); - tool2.add(bnGit); + //tool2.add(bnGit); tool2.add(bnHelp); tool2.add(bnAbout); setBorder(BorderFactory.createEtchedBorder()); @@ -65,13 +65,13 @@ public class Toolbar extends JToolBar implements ActionListener { add(new JLabel(""), BorderLayout.CENTER); add(tool2, BorderLayout.EAST); - bnReset.addActionListener(main); - bnOpen.addActionListener(main); + //bnReset.addActionListener(main); + //bnOpen.addActionListener(main); bnSave.addActionListener(main); bnSetting.addActionListener(main); bnAbout.addActionListener(this); bnHelp.addActionListener(this); - bnGit.addActionListener(this); + //bnGit.addActionListener(this); } @Override @@ -82,8 +82,8 @@ public class Toolbar extends JToolBar implements ActionListener { if (e.getSource() == bnHelp) WebBrowser.open(Constants.url); - if (e.getSource() == bnGit) - WebBrowser.open(Constants.url); + //if (e.getSource() == bnGit) + // WebBrowser.open(Constants.url); } public void showAbout() { diff --git a/src/src/eigenpsf/stack/Patch.java b/src/src/eigenpsf/stack/Patch.java index 52e4a60..d7140f0 100644 --- a/src/src/eigenpsf/stack/Patch.java +++ b/src/src/eigenpsf/stack/Patch.java @@ -1,7 +1,10 @@ package eigenpsf.stack; import java.awt.Point; + +import eigenpsf.Params; import eigenpsf.data.HyperMatrix; +import ij.ImagePlus; public class Patch { @@ -70,4 +73,5 @@ public class Patch { public String toString() { return "(id: " + id + " x:" + xp + " y:" + yp + ")"; } + } diff --git a/todo.txt b/todo.txt index 5291133..da3223d 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,3 @@ -* unvalid -> invalid (dans la table) * Option de sauvegarde de la table comme fichier json, xslx,.. * -- GitLab