001: /*
002: * $RCSfile: WarpPolynomial.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.2 $
009: * $Date: 2007/08/29 23:23:39 $
010: * $State: Exp $
011: */
012: package javax.media.jai;
013:
014: import java.awt.Rectangle;
015: import java.awt.geom.Point2D;
016: import com.sun.media.jai.util.PolyWarpSolver;
017:
018: /**
019: * A polynomial-based description of an image warp.
020: *
021: * <p>The mapping is defined by two bivariate polynomial functions
022: * X(x, y) and Y(x, y) that map destination (x, y) coordinates
023: * to source X and Y positions respectively
024: *
025: * <p>The functions X(x, y) and Y(x, y) have the form:
026: * <pre>
027: * SUM{i = 0 to n} {SUM{j = 0 to i}{a_ij*x^(i - j)*y^j}}
028: *
029: * where n is the degree os the polynomial
030: * </pre>
031: *
032: * <p>WarpAffine, WarpQuadratic, and WarpCubic are special cases of
033: * WarpPolynomial for n equal to 1, 2, and 3 respectively.
034: * WarpGeneralPolynomial provides a concrete implementation for
035: * polynomials of higher degree.
036: *
037: * @see WarpAffine
038: * @see WarpQuadratic
039: * @see WarpCubic
040: * @see WarpGeneralPolynomial
041: *
042: */
043: public abstract class WarpPolynomial extends Warp {
044:
045: /**
046: * An array of coefficients that maps a destination point to
047: * the source's X coordinate.
048: */
049: protected float[] xCoeffs;
050:
051: /**
052: * An array of coefficients that maps a destination point to
053: * the source's Y coordinate.
054: */
055: protected float[] yCoeffs;
056:
057: /**
058: * A scaling factor applied to input (dest) x coordinates to
059: * improve computational accuracy.
060: */
061: protected float preScaleX;
062:
063: /**
064: * A scaling factor applied to input (dest) y coordinates to
065: * improve computational accuracy.
066: */
067: protected float preScaleY;
068:
069: /**
070: * A scaling factor applied to the result of the X polynomial
071: * evaluation which compensates for the input scaling, so that
072: * the correctly scaled result is obtained.
073: */
074: protected float postScaleX;
075:
076: /**
077: * A scaling factor applied to the result of the Y polynomial
078: * evaluation which compensates for the input scaling, so that
079: * the correctly scaled result is obtained.
080: */
081: protected float postScaleY;
082:
083: /**
084: * The degree of the polynomial, determined by the number of
085: * coefficients supplied via the X and Y coefficients arrays.
086: */
087: protected int degree;
088:
089: /**
090: * Constructs a WarpPolynomial with a given transform mapping
091: * destination pixels into source space. Note that this is
092: * a backward mapping as opposed to the forward mapping used in
093: * AffineOpImage.
094: *
095: * <p>The <code>xCoeffs</code> and <code>yCoeffs</code> parameters
096: * must contain the same number of coefficients of the form
097: * <code>(n + 1)(n + 2)/2</code> for some <code>n</code>, where
098: * <code>n</code> is the non-negative degree power of the polynomial.
099: * The coefficients, in order, are associated with the terms:
100: *
101: * <pre>
102: * 1, x, y, x^2, x*y, y^2, ..., x^n, x^(n - 1)*y, ..., x*y^(n - 1), y^n
103: * </pre>
104: *
105: * and coefficients of value 0 cannot be omitted.
106: *
107: * <p>The destination (x, y) coordinates are multiplied by the
108: * factors preScaleX and preScaleY prior to the evaluation of the
109: * polynomial. The results of the polynomial evaluations are
110: * multiplied by postScaleX and postScaleY to produce the source
111: * pixel coordinates. This process allows for better precision of
112: * the results.
113: *
114: * @param xCoeffs The destination to source transform coefficients for
115: * the X coordinate.
116: * @param yCoeffs The destination to source transform coefficients for
117: * the Y coordinate.
118: * @param preScaleX The scale factor to apply to input (dest) X positions.
119: * @param preScaleY The scale factor to apply to input (dest) Y positions.
120: * @param postScaleX The scale factor to apply to the X polynomial output.
121: * @param postScaleY The scale factor to apply to the Y polynomial output.
122: * @throws IllegalArgumentException if xCoeff or yCoeff have an illegal number of entries.
123: */
124: public WarpPolynomial(float[] xCoeffs, float[] yCoeffs,
125: float preScaleX, float preScaleY, float postScaleX,
126: float postScaleY) {
127: if (xCoeffs == null || yCoeffs == null || xCoeffs.length < 1
128: || yCoeffs.length < 1
129: || xCoeffs.length != yCoeffs.length) {
130: throw new IllegalArgumentException(JaiI18N
131: .getString("WarpPolynomial0"));
132: }
133:
134: int numCoeffs = xCoeffs.length;
135: degree = -1;
136: while (numCoeffs > 0) {
137: degree++;
138: numCoeffs -= degree + 1;
139: }
140: if (numCoeffs != 0) {
141: throw new IllegalArgumentException(JaiI18N
142: .getString("WarpPolynomial0"));
143: }
144:
145: this .xCoeffs = (float[]) (xCoeffs.clone());
146: this .yCoeffs = (float[]) (yCoeffs.clone());
147: this .preScaleX = preScaleX;
148: this .preScaleY = preScaleY;
149: this .postScaleX = postScaleX;
150: this .postScaleY = postScaleY;
151: }
152:
153: /**
154: * Constructs a WarpPolynomial with pre- and post-scale factors of 1.
155: *
156: * @param xCoeffs The destination to source transform coefficients for
157: * the X coordinate.
158: * @param yCoeffs The destination to source transform coefficients for
159: * the Y coordinate.
160: */
161: public WarpPolynomial(float[] xCoeffs, float[] yCoeffs) {
162: this (xCoeffs, yCoeffs, 1.0F, 1.0F, 1.0F, 1.0F);
163: }
164:
165: /**
166: * Returns the raw coefficients array for the X coordinate mapping.
167: *
168: * @return A cloned array of <code>float</code>s giving the
169: * polynomial coefficients for the X coordinate mapping.
170: */
171: public float[] getXCoeffs() {
172: return (float[]) xCoeffs.clone();
173: }
174:
175: /**
176: * Returns the raw coefficients array for the Y coordinate mapping.
177: *
178: * @return A cloned array of <code>float</code>s giving the
179: * polynomial coefficients for the Y coordinate mapping.
180: */
181: public float[] getYCoeffs() {
182: return (float[]) yCoeffs.clone();
183: }
184:
185: /**
186: * Returns the raw coefficients array for both the X and Y coordinate mapping.
187: *
188: * @return A cloned two-dimensional array of <code>float</code>s giving the
189: * polynomial coefficients for the X and Y coordinate mapping.
190: */
191: public float[][] getCoeffs() {
192: float[][] coeffs = new float[2][];
193: coeffs[0] = (float[]) xCoeffs.clone();
194: coeffs[1] = (float[]) yCoeffs.clone();
195:
196: return coeffs;
197: }
198:
199: /** Returns the scaling factor applied to input (dest) X coordinates. */
200: public float getPreScaleX() {
201: return preScaleX;
202: }
203:
204: /** Returns the scaling factor applied to input (dest) Y coordinates. */
205: public float getPreScaleY() {
206: return preScaleY;
207: }
208:
209: /** Returns the scaling factor applied to the result of the X polynomial. */
210: public float getPostScaleX() {
211: return postScaleX;
212: }
213:
214: /** Returns the scaling factor applied to the result of the Y polynomial. */
215: public float getPostScaleY() {
216: return postScaleY;
217: }
218:
219: /**
220: * Returns the degree of the warp polynomials.
221: *
222: * @return The degree as an <code>int</code>.
223: */
224: public int getDegree() {
225: return degree;
226: }
227:
228: /**
229: * Returns an instance of <code>WarpPolynomial</code> or its
230: * subclasses that approximately maps the given scaled destination
231: * image coordinates into the given scaled source image
232: * coordinates. The mapping is given by:
233: *
234: * <pre>
235: * x' = postScaleX*(xpoly(x*preScaleX, y*preScaleY));
236: * x' = postScaleY*(ypoly(x*preScaleX, y*preScaleY));
237: * </pre>
238: *
239: * <p> Typically, it is useful to set <code>preScaleX</code> to
240: * <code>1.0F/destImage.getWidth()</code> and
241: * <code>postScaleX</code> to <code>srcImage.getWidth()</code> so
242: * that the input and output of the polynomials lie between 0 and
243: * 1.
244: *
245: * <p> The degree of the polynomial is supplied as an argument.
246: *
247: * @param sourceCoords An array of <code>float</code>s containing the
248: * source coordinates with X and Y alternating.
249: * @param sourceOffset the initial entry of <code>sourceCoords</code>
250: * to be used.
251: * @param destCoords An array of <code>float</code>s containing the
252: * destination coordinates with X and Y alternating.
253: * @param destOffset The initial entry of <code>destCoords</code>
254: * to be used.
255: * @param numCoords The number of coordinates from
256: * <code>sourceCoords</code> and <code>destCoords</code> to be used.
257: * @param preScaleX The scale factor to apply to input (dest) X positions.
258: * @param preScaleY The scale factor to apply to input (dest) Y positions.
259: * @param postScaleX The scale factor to apply to X polynomial output.
260: * @param postScaleY The scale factor to apply to the Y polynomial output.
261: * @param degree The desired degree of the warp polynomials.
262: *
263: * @return An instance of <code>WarpPolynomial</code>.
264: * @throws IllegalArgumentException if arrays sourceCoords or destCoords
265: * are too small
266: */
267: public static WarpPolynomial createWarp(float[] sourceCoords,
268: int sourceOffset, float[] destCoords, int destOffset,
269: int numCoords, float preScaleX, float preScaleY,
270: float postScaleX, float postScaleY, int degree) {
271:
272: int minNumPoints = (degree + 1) * (degree + 2);
273: if ((sourceOffset + minNumPoints) > sourceCoords.length
274: || (destOffset + minNumPoints) > destCoords.length) {
275:
276: throw new IllegalArgumentException(JaiI18N
277: .getString("WarpPolynomial1"));
278: }
279: float[] coeffs = PolyWarpSolver.getCoeffs(sourceCoords,
280: sourceOffset, destCoords, destOffset, numCoords,
281: preScaleX, preScaleY, postScaleX, postScaleY, degree);
282:
283: int numCoeffs = coeffs.length / 2;
284: float[] xCoeffs = new float[numCoeffs];
285: float[] yCoeffs = new float[numCoeffs];
286:
287: for (int i = 0; i < numCoeffs; i++) {
288: xCoeffs[i] = coeffs[i];
289: yCoeffs[i] = coeffs[i + numCoeffs];
290: }
291:
292: if (degree == 1) {
293: return new WarpAffine(xCoeffs, yCoeffs, preScaleX,
294: preScaleY, postScaleX, postScaleY);
295: } else if (degree == 2) {
296: return new WarpQuadratic(xCoeffs, yCoeffs, preScaleX,
297: preScaleY, postScaleX, postScaleY);
298: } else if (degree == 3) {
299: return new WarpCubic(xCoeffs, yCoeffs, preScaleX,
300: preScaleY, postScaleX, postScaleY);
301: } else {
302: return new WarpGeneralPolynomial(xCoeffs, yCoeffs,
303: preScaleX, preScaleY, postScaleX, postScaleY);
304: }
305: }
306:
307: /**
308: * Computes the source point corresponding to the supplied point.
309: *
310: * <p>This method returns the value of <code>pt</code> in the following
311: * code snippet:
312: *
313: * <pre>
314: * double dx = (destPt.getX() + 0.5)*preScaleX;
315: * double dy = (destPt.getY() + 0.5)*preScaleY;
316: *
317: * double sx = 0.0;
318: * double sy = 0.0;
319: * int c = 0;
320: *
321: * for(int nx = 0; nx <= degree; nx++) {
322: * for(int ny = 0; ny <= nx; ny++) {
323: * double t = Math.pow(dx, nx - ny)*Math.pow(dy, ny);
324: * sx += xCoeffs[c] * t;
325: * sy += yCoeffs[c] * t;
326: * c++;
327: * }
328: * }
329:
330: * Point2D pt = (Point2D)destPt.clone();
331: * pt.setLocation(sx*postScaleX - 0.5, sy*postScaleY - 0.5);
332: * </pre>
333: * </p>
334: *
335: * @param destPt the position in destination image coordinates
336: * to map to source image coordinates.
337: *
338: * @return a <code>Point2D</code> of the same class as
339: * <code>destPt</code>.
340: *
341: * @throws IllegalArgumentException if <code>destPt</code> is
342: * <code>null</code>.
343: *
344: * @since JAI 1.1.2
345: */
346: public Point2D mapDestPoint(Point2D destPt) {
347: if (destPt == null) {
348: throw new IllegalArgumentException(JaiI18N
349: .getString("Generic0"));
350: }
351:
352: double dx = (destPt.getX() + 0.5) * preScaleX;
353: double dy = (destPt.getY() + 0.5) * preScaleY;
354:
355: double sx = 0.0;
356: double sy = 0.0;
357: int c = 0;
358:
359: for (int nx = 0; nx <= degree; nx++) {
360: for (int ny = 0; ny <= nx; ny++) {
361: double t = Math.pow(dx, nx - ny) * Math.pow(dy, ny);
362: sx += xCoeffs[c] * t;
363: sy += yCoeffs[c] * t;
364: c++;
365: }
366: }
367:
368: Point2D pt = (Point2D) destPt.clone();
369: pt.setLocation(sx * postScaleX - 0.5, sy * postScaleY - 0.5);
370:
371: return pt;
372: }
373: }
|