001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 * image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause).
006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved.
007 * Visit https://imagingbook.com for additional details.
008 ******************************************************************************/
009
010package imagingbook.spectral.dct;
011
012/**
013 * <p>
014 * Common interface for all 2D DCT implementations. Based on associated
015 * one-dimensional DCT methods (see {@link Dct1d}). Data arrays are indexed as
016 * {@code data[x][y]}, with 0 &le; x &lt; width and 0 &le; y &lt; height. See Ch.
017 * 20 of [1] for additional details.
018 * </p>
019 * <p>
020 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing &ndash; An Algorithmic
021 * Introduction</em>, 3rd ed, Springer (2022).
022 * </p>
023 * 
024 * @author WB
025 * @version 2022/10/21
026 * @see Dct1d
027 */
028public interface Dct2d {
029        
030        /**
031         * Returns the 'width' of the 2D data array (length of dimension 0).
032         * Data arrays are indexed as {@code data[x][y]}, with 
033         * 0 &le; x &lt; width and 0 &le; y &lt; height.
034         * @return the width of the 2D data array
035         */
036        public int getWidth();
037        
038        /**
039         * Returns the 'height' of the 2D data array (length of dimension 1).
040         * Data arrays are indexed as {@code data[x][y]}, with 
041         * 0 &le; x &lt; width and 0 &le; y &lt; height.
042         * @return the height of the 2D data array
043         */
044        public int getHeight();
045        
046        // ---------------------------------------------------------------------------------------
047
048        /**
049         * Sub-interface for 2D DCT implementations operating on {@code float} data.
050         */
051        public interface Float extends Dct2d {
052                
053                /**
054                 * Returns a suitable 1D DCT of the specified size ({@code float}).
055                 * @param size the size of the DCT
056                 * @return a {@link Dct1d.Float} instance
057                 */
058                public Dct1d.Float get1dDct(int size);
059                
060                /**
061                 * Performs an "in-place" 2D DCT forward transformation on the supplied data.
062                 * The input signal is replaced by the associated DCT spectrum.
063                 * @param g the signal to be transformed (modified)
064                 */
065                public default void forward(float[][] g) {
066                        transform(g, true);
067                }
068
069                /**
070                 * Performs an "in-place" 2D DCT inverse transformation on the supplied spectrum.
071                 * The input spectrum is replaced by the associated signal.
072                 * @param G the spectrum to be transformed (modified)
073                 */
074                public default void inverse(float[][] G) {
075                        transform(G, false);
076                }
077
078                /**
079                 * Transforms the given 2D array 'in-place'.
080                 * 
081                 * @param data the input signal or spectrum (modified)
082                 * @param forward forward transformation if {@code true}, inverse transformation if {@code false}
083                 */
084                default void transform(final float[][] data, boolean forward) {
085                        checkSize(data);
086                        final int width = data.length;
087                        final int height = data[0].length;
088
089                        // do the rows:
090                        float[] row = new float[width];
091                        Dct1d.Float dct1R = get1dDct(width);
092                        for (int v = 0; v < height; v++) {
093                                extractRow(data, v, row);
094                                if (forward)
095                                        dct1R.forward(row);
096                                else
097                                        dct1R.inverse(row);
098                                insertRow(data, v, row);
099                        }
100
101                        // do the columns:
102                        float[] col = new float[height];
103                        Dct1d.Float dct1C = get1dDct(height);
104                        for (int u = 0; u < width; u++) {
105                                extractCol(data, u, col);
106                                if (forward)
107                                        dct1C.forward(col);
108                                else
109                                        dct1C.inverse(col);
110                                insertCol(data, u, col);
111                        }
112                }
113
114                default void extractRow(float[][] data, int v, float[] row) {
115                        final int width = data.length;
116                        for (int u = 0; u < width; u++) {
117                                row[u] = data[u][v];
118                        }
119                }
120
121                default void insertRow(float[][] data, int v, float[] row) {
122                        final int width = data.length;
123                        for (int u = 0; u < width; u++) {
124                                data[u][v] = row[u];
125                        }
126                }
127
128                default void extractCol(float[][] data, int u, float[] column) {
129                        final int height = data[0].length;
130                        for (int v = 0; v < height; v++) {
131                                column[v] = data[u][v];
132                        }
133                }
134
135                default void insertCol(float[][] data, int u, float[] column) {
136                        final int height = data[0].length;
137                        for (int v = 0; v < height; v++) {
138                                data[u][v] = column[v];
139                        }
140                }
141                
142                public default void checkSize(float[][] A) {
143                        if (A.length != this.getWidth()) 
144                                throw new IllegalArgumentException(
145                                                String.format("wrong 2D array width %d (expected %d)", A.length, this.getWidth()));
146                        if (A[0].length != this.getHeight()) 
147                                throw new IllegalArgumentException(
148                                                String.format("wrong 2D array height %d (expected %d)", A[0].length, this.getHeight()));
149                }
150                
151        }
152        
153        /**
154         * Sub-interface for 2D DCT implementations operating on {@code double} data.
155         */
156        public interface Double extends Dct2d {
157                
158                /**
159                 * Returns a suitable 1D DCT of the specified size ({@code double}).
160                 * @param size the size of the DCT
161                 * @return a {@link Dct1d.Double} instance
162                 */
163                public Dct1d.Double get1dDct(int size);
164                
165                /**
166                 * Performs an "in-place" 2D DCT forward transformation on the supplied data.
167                 * The input signal is replaced by the associated DCT spectrum.
168                 * @param g the signal to be transformed (modified)
169                 */
170                public default void forward(double[][] g) {
171                        transform(g, true);
172                }
173
174                /**
175                 * Performs an "in-place" 2D DCT inverse transformation on the supplied spectrum.
176                 * The input spectrum is replaced by the associated signal.
177                 * @param G the spectrum to be transformed (modified)
178                 */
179                public default void inverse(double[][] G) {
180                        transform(G, false);
181                }
182                
183                /**
184                 * Transforms the given 2D array 'in-place'.
185                 * 
186                 * @param data the input signal or spectrum (modified)
187                 * @param forward forward transformation if {@code true}, inverse transformation if {@code false}
188                 */
189                default void transform(final double[][] data, boolean forward) {
190                        checkSize(data);
191                        final int width = data.length;
192                        final int height = data[0].length;
193
194                        // do the rows:
195                        double[] row = new double[width];
196                        Dct1d.Double dct1R = get1dDct(width);
197                        for (int v = 0; v < height; v++) {
198                                extractRow(data, v, row);
199                                if (forward)
200                                        dct1R.forward(row);
201                                else
202                                        dct1R.inverse(row);
203                                insertRow(data, v, row);
204                        }
205
206                        // do the columns:
207                        double[] col = new double[height];
208                        Dct1d.Double dct1C = get1dDct(height);
209                        for (int u = 0; u < width; u++) {
210                                extractCol(data, u, col);
211                                if (forward)
212                                        dct1C.forward(col);
213                                else
214                                        dct1C.inverse(col);
215                                insertCol(data, u, col);
216                        }
217                }
218                
219                default void extractRow(double[][] data, int v, double[] row) {
220                        final int width = data.length;
221                        for (int u = 0; u < width; u++) {
222                                row[u] = data[u][v];
223                        }
224                }
225
226                default void insertRow(double[][] data, int v, double[] row) {
227                        final int width = data.length;
228                        for (int u = 0; u < width; u++) {
229                                data[u][v] = row[u];
230                        }
231                }
232
233                default void extractCol(double[][] data, int u, double[] column) {
234                        final int height = data[0].length;
235                        for (int v = 0; v < height; v++) {
236                                column[v] = data[u][v];
237                        }
238                }
239
240                default void insertCol(double[][] data, int u, double[] column) {
241                        final int height = data[0].length;
242                        for (int v = 0; v < height; v++) {
243                                data[u][v] = column[v];
244                        }
245                }
246                
247                public default void checkSize(double[][] A) {
248                        if (A.length != this.getWidth()) 
249                                throw new IllegalArgumentException(
250                                                String.format("wrong 2D array width %d (expected %d)", A.length, this.getWidth()));
251                        if (A[0].length != this.getHeight()) 
252                                throw new IllegalArgumentException(
253                                                String.format("wrong 2D array height %d (expected %d)", A[0].length, this.getHeight()));
254                }
255                
256        }
257
258}