All Downloads are FREE. Search and download functionalities are using the official Maven repository.

imagingbook.spectral.dft.Dft2d Maven / Gradle / Ivy

/*******************************************************************************
 * This software is provided as a supplement to the authors' textbooks on digital
 * image processing published by Springer-Verlag in various languages and editions.
 * Permission to use and distribute this software is granted under the BSD 2-Clause
 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause).
 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved.
 * Visit https://imagingbook.com for additional details.
 ******************************************************************************/
package imagingbook.spectral.dft;

import java.util.Arrays;

/**
 * 

* Common interface for all 2D DFT/FFT implementations. Data arrays are indexed * as {@code data[x][y]}, with 0 ≤ x < width and 0 ≤ y < height. * Based on associated one-dimensional DFT/FFT methods (see {@link Dft1d}). See * Ch. 19 of [1] for additional details. *

*

* [1] W. Burger, M.J. Burge, Digital Image Processing – An Algorithmic * Introduction, 3rd ed, Springer (2022). *

* * @author WB * @version 2022/10/21 * @see Dft1d */ public interface Dft2d { /** * Returns the 'width' of the 2D data array (length of dimension 0). * Data arrays are indexed as {@code data[x][y]}, with * 0 ≤ x < width and 0 ≤ y < height. * @return the width of the 2D data array */ public int getWidth(); /** * Returns the 'height' of the 2D data array (length of dimension 1). * Data arrays are indexed as {@code data[x][y]}, with * 0 ≤ x < width and 0 ≤ y < height. * @return the height of the 2D data array */ public int getHeight(); /** * Returns the scaling mode of this DFT (see {@link ScalingMode}). * @return the scaling mode of this DFT. */ public ScalingMode getScalingMode(); // ------------------------------------------------------------- /** * Sub-interface for 2D DFT implementations operating on {@code float} data. */ public interface Float extends Dft2d { /** * Returns a suitable 1D DFT of the specified size ({@code float}). * @param size the size of the DFT * @return a {@link Dft1d.Float} instance */ public Dft1d.Float get1dDft(int size); /** * Transforms the given 2D arrays 'in-place'. Separate arrays of identical size * must be supplied for the real and imaginary parts of the signal (forward) * or spectrum (inverse), neither of which may be null. * * @param inRe real part of the input signal or spectrum (modified) * @param inIm imaginary part of the input signal or spectrum (modified) * @param forward forward transformation if {@code true}, inverse transformation if {@code false} */ public default void transform(float[][] inRe, float[][] inIm, boolean forward) { checkSize(inRe); checkSize(inIm); final int width = this.getWidth(); final int height = this.getHeight(); // transform each row (in place): final float[] rowRe = new float[width]; final float[] rowIm = new float[width]; Dft1d.Float dftRow = get1dDft(width); for (int v = 0; v < height; v++) { extractRow(inRe, v, rowRe); extractRow(inIm, v, rowIm); dftRow.transform(rowRe, rowIm, forward); insertRow(inRe, v, rowRe); insertRow(inIm, v, rowIm); } // transform each column (in place): final float[] colRe = new float[height]; final float[] colIm = new float[height]; Dft1d.Float dftCol = get1dDft(height); for (int u = 0; u < width; u++) { extractCol(inRe, u, colRe); extractCol(inIm, u, colIm); dftCol.transform(colRe, colIm, forward); insertCol(inRe, u, colRe); insertCol(inIm, u, colIm); } } public default void extractRow(float[][] g, int v, float[] row) { for (int u = 0; u < row.length; u++) { row[u] = g[u][v]; } } public default void insertRow(float[][] g, int v, float[] row) { for (int u = 0; u < row.length; u++) { g[u][v] = row[u]; } } public default void extractCol(float[][] g, int u, float[] col) { for (int v = 0; v < col.length; v++) { col[v] = g[u][v]; } } public default void insertCol(float[][] g, final int u, float[] col) { for (int v = 0; v < col.length; v++) { g[u][v] = col[v]; } } /** * Performs an "in-place" forward DFT or FFT on the supplied 2D data. * The input signal is replaced by the associated DFT spectrum. * * @param gRe real part of the signal (modified) * @param gIm imaginary part of the signal (modified) * @see #transform(float[][], float[][], boolean) */ public default void forward(float[][] gRe, float[][] gIm) { transform(gRe, gIm, true); } /** * Performs an "in-place" inverse DFT or FFT on the supplied 2D spectrum. * The input spectrum is replaced by the associated signal. * * @param GRe real part of the spectrum (modified) * @param GIm imaginary part of the spectrum (modified) * @see #transform(float[][], float[][], boolean) */ public default void inverse(float[][] GRe, float[][] GIm) { transform(GRe, GIm, false); } public default void checkSize(float[][] A) { if (A.length != this.getWidth()) throw new IllegalArgumentException( String.format("wrong 2D array width %d (expected %d)", A.length, this.getWidth())); if (A[0].length != this.getHeight()) throw new IllegalArgumentException( String.format("wrong 2D array height %d (expected %d)", A[0].length, this.getHeight())); } /** * Calculates and returns the magnitude of the supplied complex-valued 2D data. * @param re the real part of the data * @param im the imaginary part of the data * @return a 2D array of magnitude values */ public default float[][] getMagnitude(float[][] re, float[][] im) { checkSize(re); checkSize(im); final int width = re.length; final int height = re[0].length; float[][] mag = new float[width][height]; for (int u = 0; u < width; u++) { for (int v = 0; v < height; v++) { float a = re[u][v]; float b = im[u][v]; mag[u][v] = (float) Math.hypot(a, b); } } return mag; } } // ------------------------------------------------------------- /** * Sub-interface for 2D DFT implementations operating on {@code double} data. */ public interface Double extends Dft2d { /** * Returns a suitable 1D DFT of the specified size ({@code double}). * @param size the size of the DFT * @return a {@link Dft1d.Double} instance */ public Dft1d.Double get1dDft(int size); /** * Transforms the given 2D arrays 'in-place'. Separate arrays of identical size * must be supplied for the real and imaginary parts of the signal (forward) * or spectrum (inverse), neither of which may be null. * * @param gRe real part of the input signal or spectrum (modified) * @param gIm imaginary part of the input signal or spectrum (modified) * @param forward forward transformation if {@code true}, inverse transformation if {@code false} */ public default void transform(double[][] gRe, double[][] gIm, boolean forward) { checkSize(gRe); checkSize(gIm); final int width = this.getWidth(); final int height = this.getHeight(); // transform each row (in place): final double[] rowRe = new double[width]; final double[] rowIm = new double[width]; Dft1d.Double dftRow = get1dDft(width); for (int v = 0; v < height; v++) { extractRow(gRe, v, rowRe); extractRow(gIm, v, rowIm); dftRow.transform(rowRe, rowIm, forward); insertRow(gRe, v, rowRe); insertRow(gIm, v, rowIm); } // transform each column (in place): final double[] colRe = new double[height]; final double[] colIm = new double[height]; Dft1d.Double dftCol = get1dDft(height); for (int u = 0; u < width; u++) { extractCol(gRe, u, colRe); extractCol(gIm, u, colIm); dftCol.transform(colRe, colIm, forward); insertCol(gRe, u, colRe); insertCol(gIm, u, colIm); } } public default void extractRow(double[][] g, int v, double[] row) { if (g == null) { // TODO: check if needed Arrays.fill(row, 0); } else { for (int u = 0; u < row.length; u++) { row[u] = g[u][v]; } } } public default void insertRow(double[][] g, int v, double[] row) { for (int u = 0; u < row.length; u++) { g[u][v] = row[u]; } } public default void extractCol(double[][] g, int u, double[] col) { if (g == null) { // TODO: check if needed Arrays.fill(col, 0); } else { for (int v = 0; v < col.length; v++) { col[v] = g[u][v]; } } } public default void insertCol(double[][] g, final int u, double[] col) { for (int v = 0; v < col.length; v++) { g[u][v] = col[v]; } } /** * Performs an "in-place" forward DFT or FFT on the supplied 2D data. * The input signal is replaced by the associated DFT spectrum. * * @param gRe real part of the signal (modified) * @param gIm imaginary part of the signal (modified) * @see #transform(double[][], double[][], boolean) */ public default void forward(double[][] gRe, double[][] gIm) { transform(gRe, gIm, true); } /** * Performs an "in-place" inverse DFT or FFT on the supplied 2D spectrum. * The input spectrum is replaced by the associated signal. * * @param GRe real part of the spectrum (modified) * @param GIm imaginary part of the spectrum (modified) * @see #transform(double[][], double[][], boolean) */ public default void inverse(double[][] GRe, double[][] GIm) { transform(GRe, GIm, false); } /** * Calculates and returns the magnitude of the supplied complex-valued 2D data. * * @param re the real part of the data * @param im the imaginary part of the data * @return a 2D array of magnitude values */ public default double[][] getMagnitude(double[][] re, double[][] im) { checkSize(re); checkSize(im); final int width = re.length; final int height = re[0].length; double[][] mag = new double[width][height]; for (int u = 0; u < width; u++) { for (int v = 0; v < height; v++) { double a = re[u][v]; double b = im[u][v]; mag[u][v] = Math.hypot(a, b); } } return mag; } public default void checkSize(double[][] A) { if (A.length != this.getWidth()) throw new IllegalArgumentException( String.format("wrong 2D array width %d (expected %d)", A.length, this.getWidth())); if (A[0].length != this.getHeight()) throw new IllegalArgumentException( String.format("wrong 2D array height %d (expected %d)", A[0].length, this.getHeight())); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy