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

net.algart.math.functions.ProjectiveOperator Maven / Gradle / Ivy

Go to download

Open-source Java libraries, supporting generalized smart arrays and matrices with elements of any types, including a wide set of 2D-, 3D- and multidimensional image processing and other algorithms, working with arrays and matrices.

There is a newer version: 1.4.23
Show newest version
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2007-2024 Daniel Alievsky, AlgART Laboratory (http://algart.net)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package net.algart.math.functions;

import net.algart.math.Point;

import java.util.Locale;
import java.util.Objects;
import java.util.Random;
import java.util.Arrays;

/**
 * 

Projective operator (projective transformation): * O f(x) = f(y), * yi = * (aix + bi) / (cx + d) * (ai means the line i of the matrix A), * where the numeric n x n matrix A, * the n-dimensional vectors b and c and the number d * are parameters of the transformation. * In other words, the argument of the function, the vector x, is mapped to the following vector y:

* *
* * * * * * * * * * *
 
y = y0
y1
...
yn−1
= *

* (A0x + b0) / (cx + d)
* (A1x + b1) / (cx + d)
* ...
* (An−1x + bn−1) / (cx + d) *

= *

* (a00x0 + a01x1 + ... * + a0,n−1xn−1 + b0) / * (c0x0 + c1x1 + ... * + cn−1xn−1 + d)
* (a10x0 + a11x1 + ... * + a1,n−1xn−1 + b1) / * (c0x0 + c1x1 + ... * + cn−1xn−1 + d)
* ...
* (an−1,0x0 * + an−1,1x1 + ... * + an−1,n−1xn−1 * + bn−1) / * (c0x0 + c1x1 + ... * + cn−1xn−1 + d) *

*
* *

However, please note: we do not guarantee that the divisions in the formulas above are performed strictly * by "c=a/b" Java operator. * They are possibly performed via the following code: "temp=1.0/b; c=a*temp;" * The difference here is very little and not important for most practical needs.

* *

Please note: if c vector is zero (all ci=0) — * in other words, if this transformation is really affine — then an instance of this class * is always an instance of its inheritor {@link LinearOperator}. * This rule is provided by the instantiation methods * {@link #getInstance(double[], double[], double[], double)} getInstance} and * {@link #getInstanceByPoints(net.algart.math.Point[], net.algart.math.Point[])} getInstanceByPoints}.

* *

This class is immutable and thread-safe: * there are no ways to modify settings of the created instance.

* * @author Daniel Alievsky * @see LinearOperator */ public class ProjectiveOperator extends AbstractCoordinateTransformationOperator implements CoordinateTransformationOperator { final double[] a; // null if diagonal (in particular, identity) final double[] diagonal; // null if non-diagonal or identity final double[] b; final double[] c; // null if all c[i]=0 (affine transformation) final double d; final int n; final boolean zeroB; ProjectiveOperator(double[] a, double[] diagonal, double[] b, double[] c, double d) { assert b != null; if (b.length == 0) { throw new IllegalArgumentException("Empty b vector (no coordinates)"); } if (a != null && a.length != (long) b.length * (long) b.length) { throw new IllegalArgumentException("Illegal size of A matrix: a.length=" + a.length + " must be equal to b.length^2=" + (long) b.length * (long) b.length); } if (diagonal != null && diagonal.length != b.length) { throw new IllegalArgumentException("b and diagonal vector lengths mismatch: diagonal.length=" + diagonal.length + ", b.length=" + b.length); } if (c != null) { if (c.length != b.length) { throw new IllegalArgumentException("b and c vector lengths mismatch: b.length=" + b.length + ", c.length=" + c.length); } } this.n = b.length; if (a != null) { boolean isDiag = true; boolean unitDiag = true; for (int i = 0, disp = 0; i < this.n; i++) { for (int j = 0; j < this.n; j++, disp++) { if (i == j) { unitDiag &= a[disp] == 1.0; } else { isDiag &= a[disp] == 0.0; } } } if (isDiag) { if (!unitDiag) { diagonal = new double[this.n]; for (int i = 0, disp = 0; i < this.n; i++, disp += this.n + 1) { diagonal[i] = a[disp]; } } a = null; } } else if (diagonal != null) { boolean unitDiag = true; for (double v : diagonal) { unitDiag &= v == 1.0; } if (unitDiag) { diagonal = null; } } this.a = a; this.diagonal = diagonal; this.b = b; boolean zeroC = true; if (c != null) { for (double v : c) { zeroC &= v == 0.0; } } this.c = zeroC ? null : c; this.d = d; boolean zeroB = true; for (double v : b) { zeroB &= v == 0.0; } this.zeroB = zeroB; } /** * Returns an instance of this class, describing the projective operator with the specified matrix A, * the vectors b and c and the number d. * See the {@link ProjectiveOperator comments to this class} for more details. * The coordinates of the vectors b and c must be listed in b and c arrays. * The elements of the matrix A must be listed, row by row, in the a array: * A={aij}, * aij=a[i*n+j], * i is the index of the row (0..n-1), * j is the index of the column (0..n-1), * n=b.length. * The lengths of b and c arrays must be the same: * b.length=c.length=n. * The length a.length of the a array must be equal to its square n2. * Empty arrays (n=0) are not allowed. * *

Please note: the returned operator can have another A, b, c, d parameters * (returned by {@link #a()}, {@link #b()}, {@link #c()}, {@link #d()} methods), * than specified in the arguments of this method. * Namely, all these numbers can be multiplied by some constant: such modification does not change * the projective transformation. * *

In particular, if the arguments of this method really describe * an affine transformation (c=0), then this method * returns an instance of {@link LinearOperator} class, where all elements of A matrix * and b vector are divided by d number. * *

The passed a, b and c Java arrays are cloned by this method: * no references to them are maintained by the created instance. * * @param a the elements of A matrix. * @param b the coordinates of b vector. * @param c the coordinates of c vector. * @param d the d parameter. * @return the projective operator described by these parameters. * @throws NullPointerException if one of the arguments of the method is {@code null}. * @throws IllegalArgumentException if b.length==0, c.length==0, * b.length!=c.length * or a.length!=b.length2. */ public static ProjectiveOperator getInstance(double[] a, double[] b, double[] c, double d) { Objects.requireNonNull(a, "Null A matrix"); Objects.requireNonNull(b, "Null b vector"); Objects.requireNonNull(c, "Null c vector"); if (c.length != b.length) { throw new IllegalArgumentException("b and c vector lengths mismatch: b.length=" + b.length + ", c.length=" + c.length); } if (a.length != ((long) b.length) * ((long) b.length)) { throw new IllegalArgumentException("Illegal size of A matrix: a.length=" + a.length + " must be equal to b.length^2=" + (b.length * b.length)); } // to be on the safe side, we check all this before following operations - we not wait for the constructor boolean zeroC = true; for (double v : c) { zeroC &= v == 0.0; } if (zeroC) { a = a.clone(); b = b.clone(); if (d != 1.0) { for (int k = 0; k < a.length; k++) { a[k] /= d; } for (int k = 0; k < b.length; k++) { b[k] /= d; } } return new LinearOperator(a, null, b); } return new ProjectiveOperator(a.clone(), null, b.clone(), c.clone(), d); } /** * Returns the n-dimensional projective operator, that transforms (maps) * the given n+2 points * p0, p1, ..., pn+1 to * the given another n+2 points * q0, q1, ..., qn+1 * of the n-dimensional space. The parameter d in the returned operator is 1.0. * In other words, the matrix A, the vectors b, c and the parameter d * in the returned operator fulfil the following conditions: * *

* * * * * * * * * * * *
 
d = 1.0;
*

* (A0pk + b0) / * (cpk + 1)
* (A1pk + b1) / * (cpk + 1)
* ...
* (An−1pk + bn−1) / * (cpk + 1) *

= qk0
qk1
...
* qk,n−1
= qk for k = 0, 1, ..., n+1
*
* *

(ai means the line i of the matrix A). * It is possible that there is no such operator * or there are many different solutions (degenerated cases). * In this case, this method still returns some operator, but some coefficients of A matrix, * b and c vectors in the returned operator will probably be Double.NaN, * Double.POSITIVE_INFINITY or Double.NEGATIVE_INFINITY. * *

All passed points must be n-dimensional, * where n+2=p.length=q.length. * * @param q the destination points. * @param p the source points. * @return the n-dimensional projective operator, which maps pi to * qi for all i=0,1,2,...,n+1. * @throws NullPointerException if one of arguments of this method or one of elements of * p and q arrays {@code null}. * @throws IllegalArgumentException if the lengths of the passed p and * q arrays are not equal, * or if for some k * p[k].{@link Point#coordCount() coordCount()}!=p.length-2 or * q[k].{@link Point#coordCount() coordCount()}!=p.length-2. * @throws OutOfMemoryError if there is not enough Java memory for storing Java array * double[n*(n+2)*n*(n+2)], * where n+2=p.length, * or if n*(n+2)*n*(n+2)>Integer.MAX_VALUE. */ public static ProjectiveOperator getInstanceByPoints(Point[] q, Point[] p) { Objects.requireNonNull(p, "Null p argument"); Objects.requireNonNull(q, "Null q argument"); if (p.length != q.length) { throw new IllegalArgumentException("p and q point arrays lengths mismatch: p.length=" + p.length + ", q.length=" + q.length); } if (p.length == 0) { throw new IllegalArgumentException("Empty p and q arrays"); } final int n = p.length - 2; long numberOfUnknowns = (long) n * (long) (n + 2); // number of unknowns if (numberOfUnknowns > Integer.MAX_VALUE || numberOfUnknowns * numberOfUnknowns > Integer.MAX_VALUE) { throw new OutOfMemoryError("Too large necessary matrix (more than Integer.MAX_VALUE elements)"); } for (int k = 0; k < p.length; k++) { if (p[k].coordCount() != n) { throw new IllegalArgumentException("n+2 n-dimensional points are necessary to " + "find the projective operator, but we have " + (n + 2) + " points, " + "and the source point #" + k + " is " + p[k].coordCount() + "-dimensional"); } if (q[k].coordCount() != n) { throw new IllegalArgumentException("n+2 n-dimensional points are necessary to " + "find the projective operator, but we have " + (n + 2) + " points, " + "and the destination point #" + k + " is " + q[k].coordCount() + "-dimensional"); } } // px0*a00 + py0*a01 + pz0*a02 + ... + bx - qx0*px0*cx - qx0*py0*cy - qx0*pz0*cz* - ... = qx0 (*d=1) // px1*a00 + py1*a01 + pz1*a02 + ... + bx - qx1*px1*cx - qx1*py1*cy - qx1*pz1*cz* - ... = qx1 // px2*a00 + py2*a01 + pz2*a02 + ... + bx - qx2*px2*cx - qx2*py2*cy - qx2*pz2*cz* - ... = qx2 // ... // px0*a10 + py0*a11 + pz0*a12 + ... + by - qy0*px0*cx - qy0*py0*cy - qy0*pz0*cz* - ... = qy0 (*d=1) // px1*a10 + py1*a11 + pz1*a12 + ... + by - qy1*px1*cx - qy1*py1*cy - qy1*pz1*cz* - ... = qy1 // px2*a10 + py2*a11 + pz2*a12 + ... + by - qy2*px2*cx - qy2*py2*cy - qy2*pz2*cz* - ... = qy2 // etc. // In other words, here is m*m equation system Sv=t, m=(n+2)*n, // for finding m unknowns a00, a01, ..., a10, a11, ..., bx, by, ..., cx, cy, .... final int m = (int) numberOfUnknowns; double[] s = new double[m * m]; // zero-filled double[] t = new double[m]; double[] v = new double[m]; for (int i = 0, k = 0; i < n; i++) { for (int pointIndex = 0; pointIndex < p.length; pointIndex++, k++) { // filling a line of S matrix and an element of t vector #k=0,1,...,m-1 for (int j = 0, sOfs = k * m + i * n; j < n; j++, sOfs++) { s[sOfs] = p[pointIndex].coord(j); // coefficient for aIJ } s[k * m + n * n + i] = 1.0; // coefficient for bx, by, ... for (int j = 0, sOfs = k * m + n * n + n; j < n; j++, sOfs++) { s[sOfs] = -q[pointIndex].coord(i) * p[pointIndex].coord(j); // coefficient for cx, cy, ... } t[k] = q[pointIndex].coord(i); } } // for (int i = 0; i < m; i++) { // for (int j = 0; j < n * n; j++) { // System.out.printf("%7.3f ", s[i * m + j]); // } // System.out.print(" | "); // for (int j = 0; j < n; j++) { // System.out.printf("%7.3f ", s[i * m + n * n + j]); // } // System.out.print(" | "); // for (int j = 0; j < n; j++) { // System.out.printf("%7.3f ", s[i * m + n * n + n + j]); // } // System.out.printf("= %.7f%n", t[i]); // } LinearOperator.solveLinearEquationsSet(v, s, t); // LinearOperator.solveLinearEquationsSetByCramerMethod(v, s, t); assert v.length == m; double[] a = new double[n * n]; double[] b = new double[n]; double[] c = new double[n]; System.arraycopy(v, 0, a, 0, n * n); System.arraycopy(v, n * n, b, 0, n); System.arraycopy(v, n * n + n, c, 0, n); return getInstance(a, b, c, 1.0); } /** * Returns an array containing A matrix. * *

The returned array is always newly created: it is not a reference * to some internal data stored in this object. * * @return A matrix. * @throws OutOfMemoryError if this instance was created by some creation method of * the {@link LinearOperator} class, * besides {@link LinearOperator#getInstance(double[], double[])}, * and the matrix is too large to be stored in Java memory * or its size is greater than Integer.MAX_VALUE. */ public final double[] a() { if (a != null) { return a.clone(); } if ((long) n * (long) n > Integer.MAX_VALUE) { throw new OutOfMemoryError("Too large matrix A (more than Integer.MAX_VALUE elements)"); } double[] result = new double[n * n]; for (int i = 0, disp = 0; i < n; i++, disp += n + 1) { result[disp] = diagonal == null ? 1.0 : diagonal[i]; } return result; } /** * Returns an array containing b vector. * *

The returned array is always newly created: it is not a reference * to some internal data stored in this object. * * @return b vector. */ public final double[] b() { return b.clone(); } /** * Returns an array containing c vector. * In a case of {@link LinearOperator}, the result is always zero-filled. * *

The returned array is always newly created: it is not a reference * to some internal data stored in this object. * * @return c vector. */ public final double[] c() { return c == null ? new double[n] : c.clone(); } /** * Returns the d parameter. * In a case of {@link LinearOperator}, the result is always 0.0. * * @return d parameter. */ public final double d() { return d; } /** * Returns an array containing the main diagonal of A matrix. * *

The returned array is always newly created: it is not a reference * to some internal data stored in this object. * * @return the main diagonal of A matrix. */ public final double[] diagonal() { if (diagonal != null) { return diagonal.clone(); } double[] result = new double[n]; if (a == null) { for (int i = 0; i < n; i++) { result[i] = 1.0; } } else { for (int i = 0, disp = 0; i < n; i++, disp += n + 1) { result[i] = a[i]; } } return result; } /** * Returns the number of dimensions. * The result is equal to the number of components in the b and c vectors. * * @return the number of dimensions. */ public final int n() { return n; } /** * Returns true if and only if A matrix is diagonal, * i.e. if aij=0.0 when i!=j. * * @return true if and only if A matrix is diagonal. */ public final boolean isDiagonal() { return a == null; } /** * Returns true if and only if A matrix is identity * (i.e. if aij=0.0 when i!=j and * aij=1.0 when i==j) * and c vector is zero. * In this case, this operator corresponds to a parallel shift. * In this case, this object is always an instance of {@link LinearOperator}. * * @return true if and only if this operator describes a parallel shift in the space. */ public final boolean isShift() { boolean result = a == null && diagonal == null && c == null; if (result && !(this instanceof LinearOperator)) { throw new AssertionError("Shift operator must be an instance of " + LinearOperator.class); } return result; } /** * Returns true if and only if the b vector is zero, * i.e. if bi=0.0 for all i. * If {@link #isZeroB()} && {@link #isShift()}, * this operator is identity: it doesn't change the passed function. * * @return true if and only if the b vector is zero. */ public final boolean isZeroB() { return zeroB; } /** * This implementation calculates destPoint by the formula * yi = * (aix + bi) / (cx + d), * where x=srcPoint and y=destPoint. * See more details in the comments to {@link ProjectiveOperator this class}. * * @param destPoint the coordinates of the destinated point y, filled by this method. * @param srcPoint the coordinates of the source point x. * @throws NullPointerException if one of the arguments is {@code null}. * @throws IllegalArgumentException if destPoint.length or srcPoint.length * is not equal to the {@link #n() number of dimensions}. */ public void map(double[] destPoint, double[] srcPoint) { calculateAxPlusB(destPoint, srcPoint); double divisor = d; if (c != null) { // to be on the safe side: null is impossible in the current implementation for (int i = 0; i < c.length; i++) { divisor += c[i] * srcPoint[i]; } } if (divisor != 1.0) { double multiplier = 1.0 / divisor; for (int i = 0; i < destPoint.length; i++) { destPoint[i] *= multiplier; } } } /** * Returns a brief string description of this object. * * @return a brief string description of this object. */ public String toString() { boolean shift = isShift(); StringBuilder sA = new StringBuilder(); if (isDiagonal()) { if (diagonal != null) { sA.append("diag["); for (int k = 0; k < diagonal.length; k++) { if (k > 0) { sA.append(","); } sA.append(LinearFunc.goodFormat(diagonal[k])); } sA.append("]"); } } else { sA.append("A"); } StringBuilder sB = new StringBuilder(); for (int k = 0; k < n; k++) { if (k > 0) { sB.append(","); } sB.append(LinearFunc.goodFormat(b[k])); } StringBuilder sC = new StringBuilder(); if (c != null) { // to be on the safe side: null is impossible in the current implementation for (int k = 0; k < c.length; k++) { if (k > 0) { sC.append(","); } sC.append(LinearFunc.goodFormat(c[k])); } } return "projective " + n + "-dimensional operator (" + sA + "x+b)/(cx+d), b=(" + sB + "), c=(" + sC + "), d=" + LinearFunc.goodFormat(d) + (shift ? " (shift)" : ""); } public int hashCode() { int result = (a != null ? Arrays.hashCode(a) : 0); result = 37 * result + (diagonal != null ? Arrays.hashCode(diagonal) : 0); result = 37 * result + Arrays.hashCode(b); result = 37 * result + (c != null ? Arrays.hashCode(c) : 0); result = 37 * result + Double.hashCode(d); return result; } public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof ProjectiveOperator po)) { return false; } if (n != po.n) { return false; } if (d != po.d) { return false; } if ((a == null) == (po.a != null)) { return false; } if (a != null) { for (int k = 0; k < a.length; k++) { if (a[k] != po.a[k]) { return false; } } } if ((diagonal == null) == (po.diagonal != null)) { return false; } if (diagonal != null) { for (int k = 0; k < diagonal.length; k++) { if (diagonal[k] != po.diagonal[k]) { return false; } } } for (int k = 0; k < b.length; k++) { if (b[k] != po.b[k]) { return false; } } if ((c == null) == (po.c != null)) { return false; } if (c != null) { for (int k = 0; k < c.length; k++) { if (c[k] != po.c[k]) { return false; } } } return true; } void calculateAxPlusB(double[] destPoint, double[] srcPoint) { Objects.requireNonNull(destPoint, "Null destPoint"); Objects.requireNonNull(srcPoint, "Null srcPoint"); if (destPoint.length != n) { throw new IllegalArgumentException("Illegal length of destPoint array: " + destPoint.length + " for " + this); } if (srcPoint.length != n) { throw new IllegalArgumentException("Illegal length of srcPoint array: " + srcPoint.length + " for " + this); } if (a != null) { System.arraycopy(b, 0, destPoint, 0, destPoint.length); for (int i = 0, disp = 0; i < n; i++) { double sum = 0.0; for (int j = 0; j < n; j++, disp++) { sum += a[disp] * srcPoint[j]; } destPoint[i] = sum + b[i]; // Note! It is possible here to change the order of summimg: calculate // b[i] + a[disp]*srcPoint[0]+... // But such an order will lead to little different results than declared by Ax+b formula, // because real sums in a computer are not commutative. // For more accurate and expected results, we calculate this in a usual order. } } else if (diagonal != null) { for (int i = 0; i < n; i++) { destPoint[i] = diagonal[i] * srcPoint[i] + b[i]; } } else { for (int i = 0; i < n; i++) { destPoint[i] = srcPoint[i] + b[i]; } } } /** * The simplest test for this class: finds a linear operator by pairs of points. */ static class Test { boolean verbose = false; int dimCount; int numberOfTests; long startSeed; Random rnd; Point[] p, q, r; final void init(String[] args) { int startArgIndex = 0; if (startArgIndex < args.length && args[startArgIndex].equalsIgnoreCase("-v")) { verbose = true; startArgIndex++; } if (args.length < startArgIndex + 2) { System.out.println("Usage: " + Test.class.getName() + " [-v] dimCount numberOfTests [randSeed]"); System.exit(0); } this.dimCount = Integer.parseInt(args[startArgIndex]); this.numberOfTests = Integer.parseInt(args[startArgIndex]); if (args.length < startArgIndex + 3) { this.startSeed = new Random().nextLong(); } else { this.startSeed = Long.parseLong(args[startArgIndex + 2]); } this.rnd = new Random(startSeed); System.out.printf(Locale.US, "%d tests, randSeed = %d%n", numberOfTests, startSeed); } final void mainTest() { long t1 = System.nanoTime(); double maxError = 0.0; for (int testCount = 1; testCount <= numberOfTests; testCount++) { newRndPoints(numberOfPoints(dimCount), dimCount); CoordinateTransformationOperator o = getOperator(); mapPoints(o); double error = maxDiff(); maxError = Math.max(maxError, error); if (verbose) { System.out.println(testCount + ": difference " + error + "; operator hash code " + o.hashCode()); } if (error > 0.001) { System.err.println(testCount + ": difference " + error + " is BIG: " + o + " incorrectly maps " + java.util.Arrays.asList(p) + " to " + java.util.Arrays.asList(r) + " instead of " + java.util.Arrays.asList(q)); } CoordinateTransformationOperator o2 = getOperator(); if (!o.equals(o2)) { throw new AssertionError("Error in equals"); } if (o2.hashCode() != o.hashCode()) { throw new AssertionError("Error in hashCode"); } } long t2 = System.nanoTime(); System.out.printf(Locale.US, "All tests done in %.3f seconds (%.2f mcs/test), maximal error = %g, randSeed = %d%n", (t2 - t1) * 1e-9, (t2 - t1) * 1e-3 / numberOfTests, maxError, startSeed); } final void newRndPoints(int numberOfPoints, int dimCount) { p = new Point[numberOfPoints]; q = new Point[numberOfPoints]; r = new Point[numberOfPoints]; double[] coordinates = new double[dimCount]; for (int k = 0; k < p.length; k++) { for (int j = 0; j < dimCount; j++) { coordinates[j] = rnd.nextDouble() - 0.5; } p[k] = Point.valueOf(coordinates); for (int j = 0; j < dimCount; j++) { coordinates[j] = rnd.nextDouble() - 0.5; } q[k] = Point.valueOf(coordinates); } } final void mapPoints(CoordinateTransformationOperator o) { for (int k = 0; k < p.length; k++) { double[] srcPoint = p[k].coordinates(); double[] destPoint = new double[srcPoint.length]; o.map(destPoint, srcPoint); r[k] = Point.valueOf(destPoint); } } final double maxDiff() { double result = 0.0; for (int k = 0; k < p.length; k++) { result = Math.max(result, q[k].subtract(r[k]).distanceFromOrigin()); } return result; } int numberOfPoints(int dimCount) { return dimCount + 2; } CoordinateTransformationOperator getOperator() { return ProjectiveOperator.getInstanceByPoints(q, p); } public static void main(String[] args) { Test test = new Test(); test.init(args); test.mainTest(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy