001/*
002Copyright 2006 Jerry Huxtable
003
004Licensed under the Apache License, Version 2.0 (the "License");
005you may not use this file except in compliance with the License.
006You may obtain a copy of the License at
007
008   http://www.apache.org/licenses/LICENSE-2.0
009
010Unless required by applicable law or agreed to in writing, software
011distributed under the License is distributed on an "AS IS" BASIS,
012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013See the License for the specific language governing permissions and
014limitations under the License.
015*/
016
017package com.jhlabs.image;
018
019import java.awt.*;
020import java.awt.image.*;
021
022/**
023 * A warp grid.
024 * From "A simplified approach to Image Processing" by Randy Crane
025 */
026public class WarpGrid {
027
028        public float[] xGrid = null;
029        public float[] yGrid = null;
030        public int rows, cols;
031
032        public WarpGrid(int rows, int cols, int w, int h) {
033                this.rows = rows;
034                this.cols = cols;
035                xGrid = new float[rows*cols];
036                yGrid = new float[rows*cols];
037                int index = 0;
038                for (int row = 0; row < rows; row++) {
039                        for (int col = 0; col < cols; col++) {
040                                xGrid[index] = (float)col*(w-1)/(cols-1);
041                                yGrid[index] = (float)row*(h-1)/(rows-1);
042                                index++;
043                        }
044                }
045        }
046
047        /**
048         * Add a new row to the grid. "before" must be in the range 1..rows-1. i.e. you can only add rows inside the grid.
049         */
050        public void addRow( int before ) {
051                int size = (rows+1) * cols;
052                float[] x = new float[size];
053                float[] y = new float[size];
054
055                rows++;
056                int i = 0;
057                int j = 0;
058                for (int row = 0; row < rows; row++) {
059                        for (int col = 0; col < cols; col++) {
060                                int k = j+col;
061                                int l = i+col;
062                                if ( row == before ) {
063                                        x[k] = (xGrid[l]+xGrid[k])/2;
064                                        y[k] = (yGrid[l]+yGrid[k])/2;
065                                } else {
066                                        x[k] = xGrid[l];
067                                        y[k] = yGrid[l];
068                                }
069                        }
070                        if ( row != before-1 )
071                                i += cols;
072                        j += cols;
073                }
074                xGrid = x;
075                yGrid = y;
076        }
077
078        /**
079         * Add a new column to the grid. "before" must be in the range 1..cols-1. i.e. you can only add columns inside the grid.
080         */
081        public void addCol( int before ) {
082                int size = rows * (cols+1);
083                float[] x = new float[size];
084                float[] y = new float[size];
085
086                cols++;
087int i = 0;
088int j = 0;
089                for (int row = 0; row < rows; row++) {
090//                      int i = row*(cols-1);
091//                      int j = row*cols;
092                        for (int col = 0; col < cols; col++) {
093                                if ( col == before ) {
094                                        x[j] = (xGrid[i]+xGrid[i-1])/2;
095                                        y[j] = (yGrid[i]+yGrid[i-1])/2;
096                                } else {
097                                        x[j] = xGrid[i];
098                                        y[j] = yGrid[i];
099                                        i++;
100                                }
101                                j++;
102                        }
103                }
104                xGrid = x;
105                yGrid = y;
106        }
107
108        /**
109         * Remove a row from the grid.
110         */
111        public void removeRow( int r ) {
112                int size = (rows-1) * cols;
113                float[] x = new float[size];
114                float[] y = new float[size];
115
116                rows--;
117                int i = 0;
118                int j = 0;
119                for (int row = 0; row < rows; row++) {
120                        for (int col = 0; col < cols; col++) {
121                                int k = j+col;
122                                int l = i+col;
123                                x[k] = xGrid[l];
124                                y[k] = yGrid[l];
125                        }
126                        if ( row == r-1 )
127                                i += cols;
128                        i += cols;
129                        j += cols;
130                }
131                xGrid = x;
132                yGrid = y;
133        }
134
135        /**
136         * Remove a column from the grid.
137         */
138        public void removeCol( int r ) {
139                int size = rows * (cols+1);
140                float[] x = new float[size];
141                float[] y = new float[size];
142
143                cols--;
144                for (int row = 0; row < rows; row++) {
145                        int i = row*(cols+1);
146                        int j = row*cols;
147                        for (int col = 0; col < cols; col++) {
148                                x[j] = xGrid[i];
149                                y[j] = yGrid[i];
150                                if ( col == r-1 )
151                                        i++;
152                                i++;
153                                j++;
154                        }
155                }
156                xGrid = x;
157                yGrid = y;
158        }
159
160        public void lerp(float t, WarpGrid destination, WarpGrid intermediate) {
161                if (rows != destination.rows || cols != destination.cols)
162                        throw new IllegalArgumentException("source and destination are different sizes");
163                if (rows != intermediate.rows || cols != intermediate.cols)
164                        throw new IllegalArgumentException("source and intermediate are different sizes");
165                int index = 0;
166                for (int row = 0; row < rows; row++) {
167                        for (int col = 0; col < cols; col++) {
168                                intermediate.xGrid[index] = (float)ImageMath.lerp(t, xGrid[index], destination.xGrid[index]);
169                                intermediate.yGrid[index] = (float)ImageMath.lerp(t, yGrid[index], destination.yGrid[index]);
170                                index++;
171                        }
172                }
173        }
174        
175        public void warp(int[] inPixels, int cols, int rows, WarpGrid sourceGrid, WarpGrid destGrid, int[] outPixels) {
176try {
177                int x, y;
178                int u, v;
179                int[] intermediate;
180                WarpGrid splines;
181
182                if (sourceGrid.rows != destGrid.rows || sourceGrid.cols != destGrid.cols)
183                        throw new IllegalArgumentException("source and destination grids are different sizes");
184
185                int size = Math.max(cols, rows);
186                float[] xrow = new float[size];
187                float[] yrow = new float[size];
188                float[] scale  = new float[size + 1];
189                float[] interpolated = new float[size + 1];
190
191                int gridCols = sourceGrid.cols;
192                int gridRows = sourceGrid.rows;
193
194                splines = new WarpGrid(rows, gridCols, 1, 1);
195
196                for (u = 0; u < gridCols;u++) {
197                        int i = u;
198
199                        for (v = 0; v < gridRows;v++) {
200                                xrow[v] = sourceGrid.xGrid[i];
201                                yrow[v] = sourceGrid.yGrid[i];
202                                i += gridCols;
203                        }
204
205                        interpolateSpline(yrow, xrow, 0, gridRows, interpolated, 0, rows);
206
207                        i = u;
208                        for (y = 0;y < rows;y++) {
209                                splines.xGrid[i] = interpolated[y];
210                                i += gridCols;
211                        }
212                }
213
214                for (u = 0; u < gridCols;u++) {
215                        int i = u;
216
217                        for (v = 0; v < gridRows;v++) {
218                                xrow[v] = destGrid.xGrid[i];
219                                yrow[v] = destGrid.yGrid[i];
220                                i += gridCols;
221                        }
222
223                        interpolateSpline(yrow, xrow, 0, gridRows, interpolated, 0, rows);
224
225                        i = u;
226                        for (y = 0;y < rows; y++) {
227                                splines.yGrid[i] = interpolated[y];
228                                i += gridCols;
229                        }
230                }
231
232                /* first pass: warp x using splines */
233                intermediate = new int[rows*cols];
234
235                int offset = 0;
236                for (y = 0; y < rows; y++) {
237                        /* fit spline to x-intercepts;resample over all cols */
238                        interpolateSpline(splines.xGrid, splines.yGrid, offset, gridCols, scale, 0, cols);
239                        scale[cols] = cols;
240                        ImageMath.resample(inPixels, intermediate, cols, y*cols, 1, scale);
241                        offset += gridCols;
242                }
243                /* create table of y-intercepts for intermediate mesh's hor splines */
244
245                splines = new WarpGrid(gridRows, cols, 1, 1);
246
247                offset = 0;
248                int offset2 = 0;
249                for (v = 0; v < gridRows; v++) {
250                        interpolateSpline(sourceGrid.xGrid, sourceGrid.yGrid, offset, gridCols, splines.xGrid, offset2, cols);
251                        offset += gridCols;
252                        offset2 += cols;
253                }
254
255                offset = 0;
256                offset2 = 0;
257                for (v = 0; v < gridRows; v++) {
258                        interpolateSpline(destGrid.xGrid, destGrid.yGrid, offset, gridCols, splines.yGrid, offset2, cols);
259                        offset += gridCols;
260                        offset2 += cols;
261                }
262
263                /* second pass: warp y */
264
265                for (x = 0; x < cols; x++) {
266                        int i = x;
267                        
268                        for (v = 0; v < gridRows; v++) {
269                                xrow[v] = splines.xGrid[i];;
270                                yrow[v] = splines.yGrid[i];;
271                                i += cols;
272                        }
273
274                        interpolateSpline(xrow, yrow, 0, gridRows, scale, 0, rows);
275                        scale[rows] = rows;
276                        ImageMath.resample(intermediate, outPixels, rows, x, cols, scale);
277                }
278}
279catch (Exception e) {
280        e.printStackTrace();
281}
282        }
283
284        private final static float m00 = -0.5f;
285        private final static float m01 =  1.5f;
286        private final static float m02 = -1.5f;
287        private final static float m03 =  0.5f;
288        private final static float m10 =  1.0f;
289        private final static float m11 = -2.5f;
290        private final static float m12 =  2.0f;
291        private final static float m13 = -0.5f;
292        private final static float m20 = -0.5f;
293        private final static float m22 =  0.5f;
294        private final static float m31 =  1.0f;
295
296        protected void interpolateSpline(float[] xKnots, float[] yKnots, int offset, int length, float[] splineY, int splineOffset, int splineLength) {
297                int index = offset;
298                int end = offset+length-1;
299                float x0, x1;
300                float k0, k1, k2, k3;
301                float c0, c1, c2, c3;
302
303                x0 = xKnots[index];
304                k0 = k1 = k2 = yKnots[index];
305                x1 = xKnots[index+1];
306                k3 = yKnots[index+1];
307
308                for (int i = 0;i < splineLength;i++) {
309                        if (index <= end && i > xKnots[index]) {
310                                k0 = k1;
311                                k1 = k2;
312                                k2 = k3;
313                                x0 = xKnots[index];
314                                index++;
315                                if ( index <= end )
316                                        x1 = xKnots[index];
317                                if ( index < end )
318                                        k3 = yKnots[index+1];
319                                else
320                                        k3 = k2;
321                        }
322                        float t = (i - x0) / (x1 - x0);
323                        c3 = m00*k0 + m01*k1 + m02*k2 + m03*k3;
324                        c2 = m10*k0 + m11*k1 + m12*k2 + m13*k3;
325                        c1 = m20*k0 + m22*k2;
326                        c0 = m31*k1;
327                        
328                        splineY[splineOffset+i] = ((c3*t + c2)*t + c1)*t + c0;
329                }
330        }
331
332        protected void interpolateSpline2(float[] xKnots, float[] yKnots, int offset, float[] splineY, int splineOffset, int splineLength) {
333                int index = offset;
334                float leftX, rightX;
335                float leftY, rightY;
336
337                leftX = xKnots[index];
338                leftY = yKnots[index];
339                rightX = xKnots[index+1];
340                rightY = yKnots[index+1];
341
342                for (int i = 0;i < splineLength;i++) {
343                        if (i > xKnots[index]) {
344                                leftX = xKnots[index];
345                                leftY = yKnots[index];
346                                index++;
347                                rightX = xKnots[index];
348                                rightY = yKnots[index];
349                        }
350                        float f = (i - leftX) / (rightX - leftX);
351                        splineY[splineOffset+i] = leftY + f * (rightY - leftY);
352                }
353        }
354}