1 /* 2 * Copyright (C) 2016 Alberto Irurueta Carro (alberto@irurueta.com) 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.irurueta.ar.calibration.estimators; 17 18 import com.irurueta.algebra.AlgebraException; 19 import com.irurueta.algebra.Matrix; 20 import com.irurueta.algebra.SingularValueDecomposer; 21 import com.irurueta.algebra.Utils; 22 import com.irurueta.ar.calibration.DualAbsoluteQuadric; 23 import com.irurueta.geometry.PinholeCamera; 24 import com.irurueta.geometry.estimators.LockedException; 25 import com.irurueta.geometry.estimators.NotReadyException; 26 import com.irurueta.numerical.NumericalException; 27 import com.irurueta.numerical.polynomials.Polynomial; 28 29 import java.util.List; 30 31 /** 32 * This class defines the interface for an estimator of the Dual Absolute 33 * Quadric (DAQ) assuming equal vertical and horizontal focal length, no 34 * skewness and principal point at the center of the image. 35 * The projection of the Dual Absolute Quadric through a given 36 * pinhole camera P, becomes the Dual Image of Absolute Conic (DIAC). 37 * Since the DIAC is directly related to the intrinsic parameters K of the 38 * camera P used for projection, then by using a pair of arbitrary cameras, 39 * it is possible to solve their corresponding focal length, assuming that: 40 * - cameras are arbitrary (usually the initial camera is the identity and must 41 * be discarded) as it creates a numerical degeneracy. 42 * - all provided cameras have the same intrinsic parameters. 43 * - it is assumed that skewness is zero, the principal point is at the center 44 * of the image plane (zero), and both horizontal and vertical focal planes are 45 * equal. 46 */ 47 @SuppressWarnings("DuplicatedCode") 48 public abstract class DualAbsoluteQuadricEstimator { 49 50 /** 51 * Constant defining whether zero skewness is assumed. 52 */ 53 public static final boolean DEFAULT_ZERO_SKEWNESS = true; 54 55 /** 56 * Constant defining whether principal point is assumed to be at origin of 57 * coordinates. 58 */ 59 public static final boolean DEFAULT_PRINCIPAL_POINT_AT_ORIGIN = true; 60 61 /** 62 * Constant defining whether aspect ratio of focal distance (i.e. vertical 63 * focal distance divided by horizontal focal distance) is known or not. 64 * Notice that focal distance aspect ratio is not related to image size 65 * aspect ratio. Typically, LCD sensor cells are square and hence aspect 66 * ratio of focal distances is known and equal to 1. 67 */ 68 public static final boolean DEFAULT_FOCAL_DISTANCE_ASPECT_RATIO_KNOWN = true; 69 70 /** 71 * Constant defining default aspect ratio of focal distances. This constant 72 * takes into account that typically LCD sensor cells are square and hence 73 * aspect ratio of focal distances is known and equal to 1. 74 */ 75 public static final double DEFAULT_FOCAL_DISTANCE_ASPECT_RATIO = 1.0; 76 77 /** 78 * Constant defining whether rank 3 must be enforced in estimated Dual 79 * Absolute Quadric (DAQ), thus making it singular. 80 * DAQ is always singular in any arbitrary projective space, however, due to 81 * noise in samples, estimation might not be singular. 82 * By enforcing singularity an additional constraint is imposed, which might 83 * result in less cameras needed for DAQ estimation. 84 */ 85 public static final boolean DEFAULT_ENFORCE_SINGULARITY = true; 86 87 /** 88 * Indicates whether enforced singularity will be validated by checking that 89 * determinant of estimated Dual Absolute Quadric (DAQ) is below a certain 90 * threshold. 91 */ 92 public static final boolean DEFAULT_VALIDATE_ENFORCED_SINGULARITY = true; 93 94 /** 95 * Default threshold to determine whether estimated Dual Absolute Quadric 96 * (DAQ) has rank 3 or not. 97 */ 98 public static final double DEFAULT_DETERMINANT_THRESHOLD = 1e-6; 99 100 /** 101 * Minimum absolute value allowed for aspect ratio of focal distances. 102 */ 103 public static final double MIN_ABS_FOCAL_DISTANCE_ASPECT_RATIO = 1e-6; 104 105 /** 106 * Minimum number of required equations to solve DAQ. 107 */ 108 protected static final int MIN_REQUIRED_EQUATIONS = 9; 109 110 /** 111 * Default type for a DAQ estimator. 112 */ 113 public static final DualAbsoluteQuadricEstimatorType DEFAULT_ESTIMATOR_TYPE = 114 DualAbsoluteQuadricEstimatorType.LMSE_DUAL_ABSOLUTE_QUADRIC_ESTIMATOR; 115 116 /** 117 * Indicates whether camera skewness is assumed to be zero or not. 118 */ 119 protected boolean zeroSkewness; 120 121 /** 122 * Indicates whether principal point is assumed to be at origin of 123 * coordinates or not. 124 * If false, the principal point will be estimated, otherwise it will be 125 * assumed to be at image center (i.e. origin of coordinates). 126 */ 127 protected boolean principalPointAtOrigin; 128 129 /** 130 * Indicates whether aspect ratio of focal distances (i.e. vertical focal 131 * distance divided by horizontal focal distance) is known or not. 132 * Notice that focal distance aspect ratio is not related to image size 133 * aspect ratio. Typically, LCD sensor cells are square and hence aspect 134 * ratio of focal distances is known and equal to 1. 135 * This value is only taken into account if skewness is assumed to be zero, 136 * otherwise it is ignored. 137 */ 138 protected boolean focalDistanceAspectRatioKnown; 139 140 /** 141 * Contains aspect ratio of focal distances (i.e. vertical focal distance 142 * divided by horizontal focal distance). 143 * This value is only taken into account if skewness is assumed to be zero 144 * and focal distance aspect ratio is marked as known, otherwise it is 145 * ignored. 146 * By default, this is 1.0, since it is taken into account that typically 147 * LCD sensor cells are square and hence aspect ratio focal distances is 148 * known and equal to 1. 149 * Notice that focal distance aspect ratio is not related to image size 150 * aspect ratio. 151 */ 152 protected double focalDistanceAspectRatio; 153 154 /** 155 * Indicates whether a singular DAQ is enforced or not. 156 * Dual Absolute Quadric is singular (has rank 3) in any projective space, 157 * however, due to noise in samples, estimated DAQ might not be fully 158 * singular. 159 */ 160 protected boolean singularityEnforced; 161 162 /** 163 * Indicates whether enforced singularity will be validated by checking that 164 * determinant of estimated Dual Absolute Quadric (DAQ) is below a certain 165 * threshold. 166 */ 167 protected boolean validateEnforcedSingularity; 168 169 /** 170 * Threshold to determine whether estimated Dual Absolute Quadric (DAQ) 171 * has rank 3 or not when validation is enabled. 172 */ 173 protected double determinantThreshold; 174 175 /** 176 * True when an estimator is estimating the Dual Absolute Quadric (DAQ). 177 */ 178 protected boolean locked; 179 180 /** 181 * Listener to be notified of events such as when estimation starts, ends or 182 * estimation progress changes. 183 */ 184 protected DualAbsoluteQuadricEstimatorListener listener; 185 186 /** 187 * List of cameras used to estimate the Dual Absolute Quadric (DAQ). 188 */ 189 protected List<PinholeCamera> cameras; 190 191 /** 192 * Constructor. 193 */ 194 protected DualAbsoluteQuadricEstimator() { 195 zeroSkewness = DEFAULT_ZERO_SKEWNESS; 196 principalPointAtOrigin = DEFAULT_PRINCIPAL_POINT_AT_ORIGIN; 197 focalDistanceAspectRatioKnown = DEFAULT_FOCAL_DISTANCE_ASPECT_RATIO_KNOWN; 198 focalDistanceAspectRatio = DEFAULT_FOCAL_DISTANCE_ASPECT_RATIO; 199 singularityEnforced = DEFAULT_ENFORCE_SINGULARITY; 200 validateEnforcedSingularity = DEFAULT_VALIDATE_ENFORCED_SINGULARITY; 201 determinantThreshold = DEFAULT_DETERMINANT_THRESHOLD; 202 203 locked = false; 204 listener = null; 205 cameras = null; 206 } 207 208 /** 209 * Constructor with listener. 210 * 211 * @param listener listener to be notified of events such as when estimation 212 * starts, ends or estimation progress changes. 213 */ 214 protected DualAbsoluteQuadricEstimator(final DualAbsoluteQuadricEstimatorListener listener) { 215 this(); 216 this.listener = listener; 217 } 218 219 /** 220 * Constructor. 221 * 222 * @param cameras list of cameras used to estimate the Dual Absolute Quadric 223 * (DAQ). 224 * @throws IllegalArgumentException if list of cameras is null or invalid 225 * for default constraints. 226 */ 227 protected DualAbsoluteQuadricEstimator(final List<PinholeCamera> cameras) { 228 this(); 229 try { 230 setCameras(cameras); 231 } catch (final LockedException ignore) { 232 // never thrown 233 } 234 } 235 236 /** 237 * Constructor. 238 * 239 * @param cameras list of cameras used to estimate the Dual Absolute Quadric 240 * (DAQ). 241 * @param listener listener to be notified of events such as when estimation 242 * starts, ends or estimation progress changes. 243 * @throws IllegalArgumentException if list of cameras is null or invalid 244 * for default constraints. 245 */ 246 protected DualAbsoluteQuadricEstimator(final List<PinholeCamera> cameras, 247 final DualAbsoluteQuadricEstimatorListener listener) { 248 this(cameras); 249 this.listener = listener; 250 } 251 252 /** 253 * Returns boolean indicating whether camera skewness is assumed to be zero 254 * or not. 255 * Skewness determines whether LCD sensor cells are properly aligned or not, 256 * where zero indicates perfect alignment. 257 * Typically, skewness is a value equal or very close to zero. 258 * 259 * @return true if camera skewness is assumed to be zero, otherwise camera 260 * skewness is estimated 261 */ 262 public boolean isZeroSkewness() { 263 return zeroSkewness; 264 } 265 266 /** 267 * Sets boolean indicating whether camera skewness is assumed to be zero or 268 * not. 269 * Skewness determines whether LCD sensor cells are properly aligned or not, 270 * where zero indicates perfect alignment. 271 * Typically, skewness is a value equal or very close to zero. 272 * 273 * @param zeroSkewness true if camera skewness is assumed to be zero, 274 * otherwise camera skewness is estimated 275 * @throws LockedException if estimator is locked 276 */ 277 public void setZeroSkewness(final boolean zeroSkewness) throws LockedException { 278 if (isLocked()) { 279 throw new LockedException(); 280 } 281 282 this.zeroSkewness = zeroSkewness; 283 } 284 285 /** 286 * Returns boolean indicating whether principal point is assumed to be at 287 * origin of coordinates or not. 288 * Typically principal point is located at image center (origin of 289 * coordinates), and usually matches the center of radial distortion if 290 * it is taken into account. 291 * 292 * @return true if principal point is assumed to be at origin of 293 * coordinates, false if principal point must be estimated 294 */ 295 public boolean isPrincipalPointAtOrigin() { 296 return principalPointAtOrigin; 297 } 298 299 /** 300 * Sets boolean indicating whether principal point is assumed to be at 301 * origin of coordinates or not. 302 * Typically principal point is located at image center (origin of 303 * coordinates), and usually matches the center of radial distortion if it 304 * is taken into account. 305 * 306 * @param principalPointAtOrigin true if principal point is assumed to be at 307 * origin of coordinates, false if principal point must be estimated 308 * @throws LockedException if estimator is locked 309 */ 310 public void setPrincipalPointAtOrigin(final boolean principalPointAtOrigin) throws LockedException { 311 if (isLocked()) { 312 throw new LockedException(); 313 } 314 315 this.principalPointAtOrigin = principalPointAtOrigin; 316 } 317 318 /** 319 * Returns boolean indicating whether aspect ratio of focal distances (i.e. 320 * vertical focal distance divided by horizontal focal distance) is known or 321 * not. 322 * Notice that focal distance aspect ratio is not related to image size 323 * aspect ratio. Typically, LCD sensor cells are square and hence aspect 324 * ratio of focal distances is known and equal to 1. 325 * This value is only taken into account if skewness is assumed to be zero, 326 * otherwise it is ignored. 327 * 328 * @return true if focal distance aspect ratio is known, false otherwise 329 */ 330 public boolean isFocalDistanceAspectRatioKnown() { 331 return focalDistanceAspectRatioKnown; 332 } 333 334 /** 335 * Sets value indicating whether aspect ratio of focal distances (i.e. 336 * vertical focal distance divided by horizontal focal distance) is known or 337 * not. 338 * Notice that focal distance aspect ratio is not related to image size 339 * aspect ratio. Typically, LCD sensor cells are square and hence aspect 340 * ratio of focal distances is known and equal to 1. 341 * This value is only taken into account if skewness is assumed to be zero, 342 * otherwise it is ignored. 343 * 344 * @param focalDistanceAspectRatioKnown true if focal distance aspect ratio 345 * is known, false otherwise. 346 * @throws LockedException if estimator is locked. 347 */ 348 public void setFocalDistanceAspectRatioKnown(final boolean focalDistanceAspectRatioKnown) throws LockedException { 349 if (isLocked()) { 350 throw new LockedException(); 351 } 352 353 this.focalDistanceAspectRatioKnown = focalDistanceAspectRatioKnown; 354 } 355 356 /** 357 * Returns aspect ratio of focal distances (i.e. vertical focal distance 358 * divided by horizontal focal distance). 359 * This value is only taken into account if skewness is assumed to be zero 360 * and focal distance aspect ratio is marked as known, otherwise it is 361 * ignored. 362 * By default, this is 1.0, since it is taken into account that typically 363 * LCD sensor cells are square and hence aspect ratio focal distances is 364 * known and equal to 1. 365 * Notice that focal distance aspect ratio is not related to image size 366 * aspect ratio. 367 * Notice that a negative aspect ratio indicates that vertical axis is 368 * reversed. This can be useful in some situations where image vertical 369 * coordinates are reversed respect to the physical world (i.e. in computer 370 * graphics typically image vertical coordinates go downwards, while in 371 * physical world they go upwards). 372 * 373 * @return aspect ratio of focal distances. 374 */ 375 public double getFocalDistanceAspectRatio() { 376 return focalDistanceAspectRatio; 377 } 378 379 /** 380 * Sets aspect ratio of focal distances (i.e. vertical focal distance 381 * divided by horizontal focal distance). 382 * This value is only taken into account if skewness is assumed to be zero 383 * and focal distance aspect ratio is marked as known, otherwise it is 384 * ignored. 385 * By default, this is 1.0, since it is taken into account that typically 386 * LCD sensor cells are square and hence aspect ratio focal distances is 387 * known and equal to 1. 388 * Notice that focal distance aspect ratio is not related to image size 389 * aspect ratio. 390 * Notice that a negative aspect ratio indicates that vertical axis is 391 * reversed. This can be useful in some situations where image vertical 392 * coordinates are reversed respect to the physical world (i.e. in computer 393 * graphics typically image vertical coordinates go downwards, while in 394 * physical world they go upwards). 395 * 396 * @param focalDistanceAspectRatio aspect ratio of focal distances to be set. 397 * @throws LockedException if estimator is locked. 398 * @throws IllegalArgumentException if focal distance aspect ratio is too 399 * close to zero, as it might produce numerical instabilities. 400 */ 401 public void setFocalDistanceAspectRatio(final double focalDistanceAspectRatio) throws LockedException { 402 if (isLocked()) { 403 throw new LockedException(); 404 } 405 if (Math.abs(focalDistanceAspectRatio) < MIN_ABS_FOCAL_DISTANCE_ASPECT_RATIO) { 406 throw new IllegalArgumentException(); 407 } 408 409 this.focalDistanceAspectRatio = focalDistanceAspectRatio; 410 } 411 412 /** 413 * Indicates whether a singular DAQ is enforced or not. 414 * Dual Absolute Quadric is singular (has rank 3) in any projective space, 415 * however, due to noise in samples, estimated DAQ might not be fully 416 * singular. 417 * 418 * @return true when singular DAQ is enforced, false otherwise. 419 */ 420 public boolean isSingularityEnforced() { 421 return singularityEnforced; 422 } 423 424 /** 425 * Specifies whether a singular DAQ is enforced or not. 426 * Dual Absolute Quadric is singular (has rank 3) in any projective space, 427 * however, due to noise in samples, estimated DAQ might not be fully 428 * singular. 429 * 430 * @param singularityEnforced true when singular DAQ is enforced, false 431 * otherwise. 432 * @throws LockedException if estimator is locked. 433 */ 434 public void setSingularityEnforced(final boolean singularityEnforced) throws LockedException { 435 if (isLocked()) { 436 throw new LockedException(); 437 } 438 this.singularityEnforced = singularityEnforced; 439 } 440 441 /** 442 * Indicates whether enforced singularity will be validated by checking that 443 * determinant of estimated Dual Absolute Quadric (DAQ) is below a certain 444 * threshold. 445 * 446 * @return true if enforced singularity is validated, false otherwise. 447 */ 448 public boolean isEnforcedSingularityValidated() { 449 return validateEnforcedSingularity; 450 } 451 452 /** 453 * Specifies whether enforced singularity will be validated by checking that 454 * determinant of estimated Dual Absolute Quadric (DAQ) is below a certain 455 * threshold. 456 * 457 * @param validateEnforcedSingularity true if enforced singularity is 458 * validated, false otherwise. 459 * @throws LockedException if estimator is locked. 460 */ 461 public void setEnforcedSingularityValidated(final boolean validateEnforcedSingularity) throws LockedException { 462 if (isLocked()) { 463 throw new LockedException(); 464 } 465 this.validateEnforcedSingularity = validateEnforcedSingularity; 466 } 467 468 /** 469 * Returns threshold to determine whether estimated Dual Absolute Quadric 470 * (DAQ) has rank 3 or not when validation is enabled. 471 * 472 * @return threshold to determine whether estimated DAQ has rank 3 or not. 473 */ 474 public double getDeterminantThreshold() { 475 return determinantThreshold; 476 } 477 478 /** 479 * Sets threshold to determine whether estimated Dual Absolute Quadric (DAQ) 480 * has rank 3 or not when validation is enabled. 481 * 482 * @param determinantThreshold threshold to determine whether estimated DAQ 483 * has rank 3 or not. 484 * @throws IllegalArgumentException if provided value is zero or negative. 485 * @throws LockedException if estimator is locked. 486 */ 487 public void setDeterminantThreshold(final double determinantThreshold) throws LockedException { 488 if (isLocked()) { 489 throw new LockedException(); 490 } 491 if (determinantThreshold < 0.0) { 492 throw new IllegalArgumentException("threshold must be positive and greater than zero"); 493 } 494 this.determinantThreshold = determinantThreshold; 495 } 496 497 /** 498 * Indicates whether this instance is locked. 499 * 500 * @return true if this estimator is busy estimating the Dual Absolute 501 * Quadric, false otherwise. 502 */ 503 public boolean isLocked() { 504 return locked; 505 } 506 507 /** 508 * Obtains listener to be notified of events such as when estimation starts, 509 * ends or estimation progress changes. 510 * 511 * @return listener to be notified of events. 512 */ 513 public DualAbsoluteQuadricEstimatorListener getListener() { 514 return listener; 515 } 516 517 /** 518 * Sets listener to be notified of events such as when estimation starts, 519 * ends or estimation progress changes. 520 * 521 * @param listener listener to be notified of events. 522 */ 523 public void setListener(final DualAbsoluteQuadricEstimatorListener listener) { 524 this.listener = listener; 525 } 526 527 /** 528 * Obtains the list of cameras used to estimate the Dual Absolute Quadric 529 * (DAQ). 530 * 531 * @return list of cameras to estimate the DAQ. 532 */ 533 public List<PinholeCamera> getCameras() { 534 return cameras; 535 } 536 537 /** 538 * Sets the list of cameras used to estimate the Dual Absolute Quadric 539 * (DAQ). 540 * 541 * @param cameras list of cameras used to estimate the DAQ. 542 * @throws IllegalArgumentException if list is null. 543 * @throws LockedException if estimator is locked. 544 */ 545 public final void setCameras(final List<PinholeCamera> cameras) throws LockedException { 546 if (isLocked()) { 547 throw new LockedException(); 548 } 549 if (cameras == null || cameras.size() < getMinNumberOfRequiredCameras()) { 550 throw new IllegalArgumentException(); 551 } 552 this.cameras = cameras; 553 } 554 555 /** 556 * Returns minimum number of required cameras needed to estimate the 557 * Dual Absolute Quadric (DAQ). 558 * At least 8 equations are needed to solve the DAQ 559 * For each imposed constraint, one less equation is required. 560 * Depending on the number of constraints more or less cameras will be 561 * required. 562 * If zero skewness is enforced, a solution is available with 8 cameras. 563 * If zero skewness and focal distance aspect ratio is known, then a 564 * solution is available with 4 cameras. 565 * If principal point is located at origin, then a solution is available 566 * with 4 cameras. 567 * If zero skewness and principal point at origin are enforced, then a 568 * solution is available with 3 cameras. 569 * If zero skewness is enforced, focal distance aspect ratio is known and 570 * principal point is at origin, then a solution is available with 2 571 * cameras. 572 * NOTE: minimum number of cameras considers only the cameras providing 573 * additional information. If a camera is equivalent to another one or does 574 * not provide additional information (such as a camera at the origin with 575 * no rotation), then more cameras will be needed. 576 * 577 * @return minimum number of required cameras needed to estimate the Dual 578 * Absolute Quadric (DAQ) or -1 if constraints configurations is not valid. 579 */ 580 public int getMinNumberOfRequiredCameras() { 581 var numEquations = 0; 582 if (principalPointAtOrigin) { 583 numEquations += 2; 584 if (zeroSkewness) { 585 numEquations++; 586 587 // only a linear solution exists for known aspect ratio if 588 // skewness is zero 589 if (focalDistanceAspectRatioKnown) { 590 numEquations++; 591 } 592 } 593 } 594 595 if (numEquations == 0) { 596 return -1; 597 } 598 599 var minRequiredEquations = MIN_REQUIRED_EQUATIONS; 600 if (singularityEnforced) { 601 minRequiredEquations--; 602 } 603 return (minRequiredEquations / numEquations) + ((minRequiredEquations % numEquations != 0) ? 1 : 0); 604 } 605 606 /** 607 * Indicates whether current constraints are enough to start the estimation. 608 * In order to obtain a linear solution for the DAQ estimation, we need at 609 * least the principal point at origin constraint. 610 * 611 * @return true if constraints are valid, false otherwise. 612 */ 613 public boolean areValidConstraints() { 614 return principalPointAtOrigin && (focalDistanceAspectRatioKnown || !singularityEnforced); 615 } 616 617 618 /** 619 * Indicates if this estimator is ready to start the estimation. 620 * 621 * @return true if estimator is ready, false otherwise. 622 */ 623 public boolean isReady() { 624 return cameras != null && cameras.size() >= getMinNumberOfRequiredCameras() && areValidConstraints(); 625 } 626 627 628 /** 629 * Estimates the Dual Absolute Quadric using provided cameras. 630 * 631 * @return estimated Dual Absolute Quadric (DAQ). 632 * @throws LockedException if estimator is locked. 633 * @throws NotReadyException if no valid input data has already been 634 * provided. 635 * @throws DualAbsoluteQuadricEstimatorException if an error occurs during 636 * estimation, usually because input data is not valid 637 * or numerically unstable. 638 */ 639 public DualAbsoluteQuadric estimate() throws LockedException, NotReadyException, 640 DualAbsoluteQuadricEstimatorException { 641 final var result = new DualAbsoluteQuadric(); 642 estimate(result); 643 return result; 644 } 645 646 /** 647 * Estimates the Dual Absolute Quadric using provided cameras. 648 * 649 * @param result instance where estimated Dual Absolute Quadric (DAQ) will 650 * be stored. 651 * @throws LockedException if estimator is locked. 652 * @throws NotReadyException if no valid input data has already been 653 * provided. 654 * @throws DualAbsoluteQuadricEstimatorException if an error occurs during 655 * estimation, usually because input data is not valid 656 * or numerically unstable. 657 */ 658 public abstract void estimate(final DualAbsoluteQuadric result) throws LockedException, NotReadyException, 659 DualAbsoluteQuadricEstimatorException; 660 661 662 /** 663 * Returns type of Dual Absolute Quadric estimator. 664 * 665 * @return type of DAQ estimator. 666 */ 667 public abstract DualAbsoluteQuadricEstimatorType getType(); 668 669 /** 670 * Creates an instance of a DAQ estimator using default type. 671 * 672 * @return an instance of a DAQ estimator. 673 */ 674 public static DualAbsoluteQuadricEstimator create() { 675 return create(DEFAULT_ESTIMATOR_TYPE); 676 } 677 678 /** 679 * Creates an instance of Dual Absolute Quadric estimator using provided 680 * listener and default type. 681 * 682 * @param listener listener to be notified of events such as when estimation 683 * starts, ends or estimation progress changes. 684 * @return an instance of a DAQ estimator. 685 */ 686 public static DualAbsoluteQuadricEstimator create(final DualAbsoluteQuadricEstimatorListener listener) { 687 return create(listener, DEFAULT_ESTIMATOR_TYPE); 688 } 689 690 /** 691 * Creates an instance of Dual Absolute Quadric estimator using provided 692 * cameras. 693 * 694 * @param cameras list of cameras used to estimate the Dual Absolute Quadric 695 * (DAQ). 696 * @return an instance of a DAQ estimator. 697 * @throws IllegalArgumentException if list of cameras is null or invalid 698 * for default constraints. 699 */ 700 public static DualAbsoluteQuadricEstimator create(final List<PinholeCamera> cameras) { 701 return create(cameras, DEFAULT_ESTIMATOR_TYPE); 702 } 703 704 /** 705 * Creates an instance of Dual Absolute Quadric estimator using provided 706 * cameras and listener. 707 * 708 * @param cameras list of cameras used to estimate the Dual Absolute Quadric 709 * (DAQ). 710 * @param listener listener to be notified of events such as when estimation 711 * starts, ends or estimation progress changes. 712 * @return an instance of a DAQ estimator. 713 * @throws IllegalArgumentException if list of cameras is null or invalid 714 * for default constraints. 715 */ 716 public static DualAbsoluteQuadricEstimator create( 717 final List<PinholeCamera> cameras, final DualAbsoluteQuadricEstimatorListener listener) { 718 719 return create(cameras, listener, DEFAULT_ESTIMATOR_TYPE); 720 } 721 722 /** 723 * Creates an instance of a DAQ estimator using provided type. 724 * 725 * @param type type of DAQ estimator. 726 * @return an instance of a DAQ estimator. 727 */ 728 public static DualAbsoluteQuadricEstimator create(final DualAbsoluteQuadricEstimatorType type) { 729 return type == DualAbsoluteQuadricEstimatorType.WEIGHTED_DUAL_ABSOLUTE_QUADRIC_ESTIMATOR 730 ? new WeightedDualAbsoluteQuadricEstimator() : new LMSEDualAbsoluteQuadricEstimator(); 731 } 732 733 /** 734 * Creates an instance of a DAQ estimator using provided listener and type. 735 * 736 * @param listener listener to be notified of events such as when estimation 737 * starts, ends or estimation progress changes. 738 * @param type type of DAQ estimator. 739 * @return an instance of a DAQ estimator. 740 */ 741 public static DualAbsoluteQuadricEstimator create( 742 final DualAbsoluteQuadricEstimatorListener listener, final DualAbsoluteQuadricEstimatorType type) { 743 return type == DualAbsoluteQuadricEstimatorType.WEIGHTED_DUAL_ABSOLUTE_QUADRIC_ESTIMATOR 744 ? new WeightedDualAbsoluteQuadricEstimator(listener) : new LMSEDualAbsoluteQuadricEstimator(listener); 745 } 746 747 /** 748 * Creates an instance of a DAQ estimator using provided cameras and type. 749 * 750 * @param cameras list of cameras used to estimate the Dual Absolute Quadric 751 * (DAQ). 752 * @param type type of DAQ estimator. 753 * @return an instance of a DAQ estimator. 754 * @throws IllegalArgumentException if list of cameras is null or invalid 755 * for default constraints. 756 */ 757 public static DualAbsoluteQuadricEstimator create( 758 final List<PinholeCamera> cameras, final DualAbsoluteQuadricEstimatorType type) { 759 return type == DualAbsoluteQuadricEstimatorType.WEIGHTED_DUAL_ABSOLUTE_QUADRIC_ESTIMATOR 760 ? new WeightedDualAbsoluteQuadricEstimator(cameras) : new LMSEDualAbsoluteQuadricEstimator(cameras); 761 } 762 763 /** 764 * Creates an instance of a DAQ estimator using provided cameras, listener 765 * and type. 766 * 767 * @param cameras list of cameras used to estimate the Dual Absolute Quadric 768 * (DAQ). 769 * @param listener listener to be notified of events such as when estimation 770 * starts, ends or estimation progress changes. 771 * @param type type of DAQ estimator. 772 * @return an instance of DAQ estimator. 773 * @throws IllegalArgumentException if list of cameras is null or invalid 774 * for default constraints. 775 */ 776 public static DualAbsoluteQuadricEstimator create( 777 final List<PinholeCamera> cameras, final DualAbsoluteQuadricEstimatorListener listener, 778 final DualAbsoluteQuadricEstimatorType type) { 779 780 return type == DualAbsoluteQuadricEstimatorType.WEIGHTED_DUAL_ABSOLUTE_QUADRIC_ESTIMATOR 781 ? new WeightedDualAbsoluteQuadricEstimator(cameras, listener) 782 : new LMSEDualAbsoluteQuadricEstimator(cameras, listener); 783 } 784 785 /** 786 * Normalizes i-th row of provided matrix "a". 787 * 788 * @param a matrix whose row must be normalized. 789 * @param i row to be normalized. 790 */ 791 protected void normalizeRow(final Matrix a, final int i) { 792 var rowNorm = 0.0; 793 final var cols = a.getColumns(); 794 for (var j = 0; j < cols; j++) { 795 rowNorm += Math.pow(a.getElementAt(i, j), 2.0); 796 } 797 798 rowNorm = Math.sqrt(rowNorm); 799 800 for (var j = 0; j < cols; j++) { 801 a.setElementAt(i, j, a.getElementAt(i, j) / rowNorm); 802 } 803 } 804 805 /** 806 * Fills equation p3^t*Q*p1 in provided row of matrix "a". 807 * 808 * @param p11 element (1,1) of camera matrix. 809 * @param p31 element (3,1) of camera matrix. 810 * @param p12 element (1,2) of camera matrix. 811 * @param p32 element (3,2) of camera matrix. 812 * @param p13 element (1,3) of camera matrix. 813 * @param p33 element (3,3) of camera matrix. 814 * @param p14 element (1,4) of camera matrix. 815 * @param p34 element (3,4) of camera matrix. 816 * @param a matrix where data is stored. 817 * @param row row of matrix a where data is stored. 818 */ 819 protected void fill3rdRowAnd1stRowEquation( 820 final double p11, final double p31, final double p12, final double p32, final double p13, final double p33, 821 final double p14, final double p34, final Matrix a, final int row) { 822 823 // a 824 a.setElementAt(row, 0, p31 * p11); 825 // b 826 a.setElementAt(row, 1, p32 * p12); 827 // c 828 a.setElementAt(row, 2, p33 * p13); 829 // d 830 a.setElementAt(row, 3, p32 * p11 + p31 * p12); 831 // e 832 a.setElementAt(row, 4, p33 * p12 + p32 * p13); 833 // f 834 a.setElementAt(row, 5, p33 * p11 + p31 * p13); 835 // g 836 a.setElementAt(row, 6, p34 * p11 + p31 * p14); 837 // h 838 a.setElementAt(row, 7, p34 * p12 + p32 * p14); 839 // i 840 a.setElementAt(row, 8, p34 * p13 + p33 * p14); 841 // j 842 a.setElementAt(row, 9, p34 * p14); 843 844 normalizeRow(a, row); 845 } 846 847 /** 848 * Fills equation p3^t*Q*p2 in provided row of matrix "a". 849 * 850 * @param p21 element (2,1) of camera matrix. 851 * @param p31 element (3,1) of camera matrix. 852 * @param p22 element (2,2) of camera matrix. 853 * @param p32 element (3,2) of camera matrix. 854 * @param p23 element (2,3) of camera matrix. 855 * @param p33 element (3,3) of camera matrix. 856 * @param p24 element (2,4) of camera matrix. 857 * @param p34 element (3,4) of camera matrix. 858 * @param a matrix where data is stored. 859 * @param row row of matrix a where data is stored. 860 */ 861 protected void fill3rdRowAnd2ndRowEquation( 862 final double p21, final double p31, final double p22, final double p32, final double p23, final double p33, 863 final double p24, final double p34, final Matrix a, final int row) { 864 865 // a 866 a.setElementAt(row, 0, p31 * p21); 867 // b 868 a.setElementAt(row, 1, p32 * p22); 869 // c 870 a.setElementAt(row, 2, p33 * p23); 871 // d 872 a.setElementAt(row, 3, p32 * p21 + p31 * p22); 873 // e 874 a.setElementAt(row, 4, p33 * p22 + p32 * p23); 875 // f 876 a.setElementAt(row, 5, p33 * p21 + p31 * p23); 877 // g 878 a.setElementAt(row, 6, p34 * p21 + p31 * p24); 879 // h 880 a.setElementAt(row, 7, p34 * p22 + p32 * p24); 881 // i 882 a.setElementAt(row, 8, p34 * p23 + p33 * p24); 883 // j 884 a.setElementAt(row, 9, p34 * p24); 885 886 normalizeRow(a, row); 887 } 888 889 /** 890 * Fills equation p2^t*Q*p1 in provided row of matrix "a". 891 * 892 * @param p11 element (1,1) of camera matrix. 893 * @param p21 element (2,1) of camera matrix. 894 * @param p12 element (1,2) of camera matrix. 895 * @param p22 element (2,2) of camera matrix. 896 * @param p13 element (1,3) of camera matrix. 897 * @param p23 element (2,3) of camera matrix. 898 * @param p14 element (1,4) of camera matrix. 899 * @param p24 element (2,4) of camera matrix. 900 * @param a matrix where data is stored. 901 * @param row row of matrix a where data is stored. 902 */ 903 protected void fill2ndRowAnd1stRowEquation( 904 final double p11, final double p21, final double p12, final double p22, final double p13, final double p23, 905 final double p14, final double p24, final Matrix a, final int row) { 906 907 // a 908 a.setElementAt(row, 0, p21 * p11); 909 // b 910 a.setElementAt(row, 1, p22 * p12); 911 // c 912 a.setElementAt(row, 2, p23 * p13); 913 // d 914 a.setElementAt(row, 3, p22 * p11 + p21 * p12); 915 // e 916 a.setElementAt(row, 4, p23 * p12 + p22 * p13); 917 // f 918 a.setElementAt(row, 5, p23 * p11 + p21 * p13); 919 // g 920 a.setElementAt(row, 6, p24 * p11 + p21 * p14); 921 // h 922 a.setElementAt(row, 7, p24 * p12 + p22 * p14); 923 // i 924 a.setElementAt(row, 8, p24 * p13 + p23 * p14); 925 // j 926 a.setElementAt(row, 9, p24 * p14); 927 928 normalizeRow(a, row); 929 } 930 931 /** 932 * Fills equation p1^t*Q*p1 = r^2*p2^t*Q*p2 933 * 934 * @param p11 element (1,1) of camera matrix. 935 * @param p21 element (2,1) of camera matrix. 936 * @param p12 element (1,2) of camera matrix. 937 * @param p22 element (2,2) of camera matrix. 938 * @param p13 element (1,3) of camera matrix. 939 * @param p23 element (2,3) of camera matrix. 940 * @param p14 element (1,4) of camera matrix. 941 * @param p24 element (2,4) of camera matrix. 942 * @param a matrix where data is stored. 943 * @param row row of matrix a where data is stored. 944 */ 945 protected void fill1stRowEqualTo2ndRowEquation( 946 final double p11, final double p21, final double p12, final double p22, final double p13, final double p23, 947 final double p14, final double p24, final Matrix a, final int row) { 948 949 final var r2 = focalDistanceAspectRatio * focalDistanceAspectRatio; 950 951 // a 952 a.setElementAt(row, 0, p11 * p11 * r2 - p21 * p21); 953 // b 954 a.setElementAt(row, 1, p12 * p12 * r2 - p22 * p22); 955 // c 956 a.setElementAt(row, 2, p13 * p13 * r2 - p23 * p23); 957 // d 958 a.setElementAt(row, 3, 2.0 * (p12 * p11 * r2 - p22 * p21)); 959 // e 960 a.setElementAt(row, 4, 2.0 * (p13 * p12 * r2 - p23 * p22)); 961 // f 962 a.setElementAt(row, 5, 2.0 * (p13 * p11 * r2 - p23 * p21)); 963 // g 964 a.setElementAt(row, 6, 2.0 * (p14 * p11 * r2 - p24 * p21)); 965 // h 966 a.setElementAt(row, 7, 2.0 * (p14 * p12 * r2 - p24 * p22)); 967 // i 968 a.setElementAt(row, 8, 2.0 * (p14 * p13 * r2 - p24 * p23)); 969 // j 970 a.setElementAt(row, 9, p14 * p14 * r2 - p24 * p24); 971 972 normalizeRow(a, row); 973 } 974 975 /** 976 * Enforces (if needed) rank 3 of estimated quadric by building a polynomial 977 * out of the last columns of the singular value vector matrix to obtain a 978 * linear combination solution. 979 * 980 * @param decomposer decomposer containing possible solutions after 981 * decomposition. 982 * @param result instance where estimated Dual Absolute Quadrics (DAQs) with 983 * rank 3 enforced will be stored. 984 * @throws AlgebraException if there are numerical instabilities 985 * @throws NumericalException if a solution to the polynomial enforcing 986 * rank 3 cannot be found. 987 * @throws DualAbsoluteQuadricEstimatorException if something else fails. 988 */ 989 protected void enforceRank3IfNeeded(final SingularValueDecomposer decomposer, final DualAbsoluteQuadric result) 990 throws AlgebraException, NumericalException, DualAbsoluteQuadricEstimatorException { 991 992 decomposer.decompose(); 993 994 if (singularityEnforced) { 995 if (decomposer.getNullity() > 2) { 996 // provided cameras are degenerate and there is not a single 997 // solution for the DAQ (up to scale) 998 throw new DualAbsoluteQuadricEstimatorException(); 999 } 1000 1001 final var v = decomposer.getV(); 1002 1003 // last 2 columns of v contains parameters a, b, c, d, e, 1004 // f, g, h, i that can be obtained as a linear combination of 1005 // those last columns (i.e. a = a1 + alpha*a2). 1006 // By enforcing determinant zero, we can obtain a 4th degree 1007 // polynomial to determine alpha to find the unique linear 1008 // combination of last 2 columns that solve both DAQ equation and 1009 // generates rank 3 DAQ. 1010 final var a1 = v.getElementAt(0, 8); 1011 final var b1 = v.getElementAt(1, 8); 1012 final var c1 = v.getElementAt(2, 8); 1013 final var d1 = v.getElementAt(3, 8); 1014 final var e1 = v.getElementAt(4, 8); 1015 final var f1 = v.getElementAt(5, 8); 1016 final var g1 = v.getElementAt(6, 8); 1017 final var h1 = v.getElementAt(7, 8); 1018 final var i1 = v.getElementAt(8, 8); 1019 final var j1 = v.getElementAt(9, 8); 1020 1021 final var a2 = v.getElementAt(0, 9); 1022 final var b2 = v.getElementAt(1, 9); 1023 final var c2 = v.getElementAt(2, 9); 1024 final var d2 = v.getElementAt(3, 9); 1025 final var e2 = v.getElementAt(4, 9); 1026 final var f2 = v.getElementAt(5, 9); 1027 final var g2 = v.getElementAt(6, 9); 1028 final var h2 = v.getElementAt(7, 9); 1029 final var i2 = v.getElementAt(8, 9); 1030 final var j2 = v.getElementAt(9, 9); 1031 1032 final var poly = buildPolynomialToEnforceRank3(a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, a2, b2, c2, d2, e2, 1033 f2, g2, h2, i2, j2); 1034 1035 final var roots = poly.getRoots(); 1036 1037 if (roots != null) { 1038 // pick the best solution (closest real root) = evaluation closest to 1039 // zero 1040 var bestPolyEval = Double.MAX_VALUE; 1041 for (final var root : roots) { 1042 final var real = root.getReal(); 1043 final var polyEval = Math.abs(poly.evaluate(real)); 1044 if (polyEval < bestPolyEval) { 1045 bestPolyEval = polyEval; 1046 final var a = a1 + real * a2; 1047 final var b = b1 + real * b2; 1048 final var c = c1 + real * c2; 1049 final var d = d1 + real * d2; 1050 final var e = e1 + real * e2; 1051 final var f = f1 + real * f2; 1052 final var g = g1 + real * g2; 1053 final var h = h1 + real * h2; 1054 final var i = i1 + real * i2; 1055 final var j = j1 + real * j2; 1056 result.setParameters(a, b, c, d, e, f, g, h, i, j); 1057 } 1058 } 1059 } else { 1060 // if no roots could be found, it might be due to numerical 1061 // inaccuracies, so we find minimum or maximum of polynomial 1062 // which evaluates closest to zero 1063 final var extrema = poly.getExtrema(); 1064 if (extrema == null) { 1065 // polynomial has no extrema, which means it is degenerate 1066 throw new DualAbsoluteQuadricEstimatorException(); 1067 } 1068 1069 var bestPolyEval = Double.MAX_VALUE; 1070 for (final var extremum : extrema) { 1071 final var polyEval = Math.abs(poly.evaluate(extremum)); 1072 if (polyEval < bestPolyEval) { 1073 bestPolyEval = polyEval; 1074 final var a = a1 + extremum * a2; 1075 final var b = b1 + extremum * b2; 1076 final var c = c1 + extremum * c2; 1077 final var d = d1 + extremum * d2; 1078 final var e = e1 + extremum * e2; 1079 final var f = f1 + extremum * f2; 1080 final var g = g1 + extremum * g2; 1081 final var h = h1 + extremum * h2; 1082 final var i = i1 + extremum * i2; 1083 final var j = j1 + extremum * j2; 1084 result.setParameters(a, b, c, d, e, f, g, h, i, j); 1085 } 1086 } 1087 1088 if (validateEnforcedSingularity) { 1089 // check that determinant of estimated DAQ is below allowed 1090 // threshold 1091 final var absDet = Math.abs(Utils.det(result.asMatrix())); 1092 if (absDet > determinantThreshold) { 1093 // DAQ does not have rank 3 1094 throw new DualAbsoluteQuadricEstimatorException(); 1095 } 1096 } 1097 } 1098 1099 } else { 1100 if (decomposer.getNullity() > 1) { 1101 // provided cameras are degenerate and there is not a single 1102 // solution for the DAQ (up to scale) 1103 throw new DualAbsoluteQuadricEstimatorException(); 1104 } 1105 1106 final var v = decomposer.getV(); 1107 1108 // last column of v contains parameters a, b, c, d, e, f, g, h, i 1109 // defining the Dual Absolute Quadric (DAQ) as: 1110 final var a = v.getElementAt(0, 9); 1111 final var b = v.getElementAt(1, 9); 1112 final var c = v.getElementAt(2, 9); 1113 final var d = v.getElementAt(3, 9); 1114 final var e = v.getElementAt(4, 9); 1115 final var f = v.getElementAt(5, 9); 1116 final var g = v.getElementAt(6, 9); 1117 final var h = v.getElementAt(7, 9); 1118 final var i = v.getElementAt(8, 9); 1119 final var j = v.getElementAt(9, 9); 1120 1121 result.setParameters(a, b, c, d, e, f, g, h, i, j); 1122 } 1123 } 1124 1125 /** 1126 * Builds polynomial to enforce rank 3 (zero determinant). 1127 * 1128 * @param a1 a of 1st DAQ. 1129 * @param b1 b of 1st DAQ. 1130 * @param c1 c of 1st DAQ. 1131 * @param d1 d of 1st DAQ. 1132 * @param e1 e of 1st DAQ. 1133 * @param f1 f of 1st DAQ. 1134 * @param g1 g of 1st DAQ. 1135 * @param h1 h of 1st DAQ. 1136 * @param i1 i of 1st DAQ. 1137 * @param j1 j of 1st DAQ. 1138 * @param a2 a of 2nd DAQ. 1139 * @param b2 b of 2nd DAQ. 1140 * @param c2 c of 2nd DAQ. 1141 * @param d2 d of 2nd DAQ. 1142 * @param e2 e of 2nd DAQ. 1143 * @param f2 f of 2nd DAQ. 1144 * @param g2 g of 2nd DAQ. 1145 * @param h2 h of 2nd DAQ. 1146 * @param i2 i of 2nd DAQ. 1147 * @param j2 j of 2nd DAQ. 1148 * @return polynomial. 1149 */ 1150 private Polynomial buildPolynomialToEnforceRank3( 1151 final double a1, final double b1, final double c1, final double d1, final double e1, 1152 final double f1, final double g1, final double h1, final double i1, final double j1, 1153 final double a2, final double b2, final double c2, final double d2, final double e2, 1154 final double f2, final double g2, final double h2, final double i2, final double j2) { 1155 1156 final var polyA = new Polynomial(a1, a2); 1157 final var polyB = new Polynomial(b1, b2); 1158 final var polyC = new Polynomial(c1, c2); 1159 final var polyD = new Polynomial(d1, d2); 1160 final var polyE = new Polynomial(e1, e2); 1161 final var polyF = new Polynomial(f1, f2); 1162 final var polyG = new Polynomial(g1, g2); 1163 final var polyH = new Polynomial(h1, h2); 1164 final var polyI = new Polynomial(i1, i2); 1165 final var polyJ = new Polynomial(j1, j2); 1166 1167 final var result = new Polynomial(5); 1168 1169 // (a1 + x*a2) * (b1 + x*b2) * (c1 + x*c2) * (j1 + x*j2) 1170 var tmp = polyA.multiplyAndReturnNew(polyB); 1171 tmp.multiply(polyC); 1172 tmp.multiply(polyJ); 1173 1174 result.add(tmp); 1175 1176 // 2 * (a1 + x*a2) * (e1 + x*e2) * (h1 + x*h2) * (i1 + x*i2) 1177 tmp = polyA.multiplyAndReturnNew(polyE); 1178 tmp.multiply(polyH); 1179 tmp.multiply(polyI); 1180 tmp.multiplyByScalar(2.0); 1181 1182 result.add(tmp); 1183 1184 // -1 * (a1 + x*a2) * (c1 + x*c2) * (h1 + x*h2)^2 1185 tmp = polyA.multiplyAndReturnNew(polyC); 1186 tmp.multiply(polyH); 1187 tmp.multiply(polyH); 1188 tmp.multiplyByScalar(-1.0); 1189 1190 result.add(tmp); 1191 1192 // -1 * (a1 + x*a2) * (b1 + x*b2) * (i1 + x*i2)^2 1193 tmp = polyA.multiplyAndReturnNew(polyB); 1194 tmp.multiply(polyI); 1195 tmp.multiply(polyI); 1196 tmp.multiplyByScalar(-1.0); 1197 1198 result.add(tmp); 1199 1200 // -1 * (a1 + x*a2) * (e1 + x*e2)^2 * (j1 + x*j2) 1201 tmp = polyA.multiplyAndReturnNew(polyE); 1202 tmp.multiply(polyE); 1203 tmp.multiply(polyJ); 1204 tmp.multiplyByScalar(-1.0); 1205 1206 result.add(tmp); 1207 1208 // -1 * (c1 + x*c2) * (d1 + x*d2)^2 * (j1 + x*j2) 1209 tmp = polyC.multiplyAndReturnNew(polyD); 1210 tmp.multiply(polyD); 1211 tmp.multiply(polyJ); 1212 tmp.multiplyByScalar(-1.0); 1213 1214 result.add(tmp); 1215 1216 // -2 * (d1 + x*d2) * (f1 + x*f2) * (h1 + x*h2) * (i1 + x*i2) 1217 tmp = polyD.multiplyAndReturnNew(polyF); 1218 tmp.multiply(polyH); 1219 tmp.multiply(polyI); 1220 tmp.multiplyByScalar(-2.0); 1221 1222 result.add(tmp); 1223 1224 // -2 * (d1 + x*d2) * (e1 + x*e2) * (g1 + x*g2) * (i1 + x*i2) 1225 tmp = polyD.multiplyAndReturnNew(polyE); 1226 tmp.multiply(polyG); 1227 tmp.multiply(polyI); 1228 tmp.multiplyByScalar(-2.0); 1229 1230 result.add(tmp); 1231 1232 // 2 * (c1 + x*c2) * (d1 + x*d2) * (g1 + x*g2) * (h1 + x*h2) 1233 tmp = polyC.multiplyAndReturnNew(polyD); 1234 tmp.multiply(polyG); 1235 tmp.multiply(polyH); 1236 tmp.multiplyByScalar(2.0); 1237 1238 result.add(tmp); 1239 1240 // (d1 + x*d2)^2 * (i1 + x*i2)^2 1241 tmp = polyD.multiplyAndReturnNew(polyD); 1242 tmp.multiply(polyI); 1243 tmp.multiply(polyI); 1244 1245 result.add(tmp); 1246 1247 // 2 * (d1 + x*d2) * (e1 + x*e2) * (f1 + x*f2) * (j1 + x*j2) 1248 tmp = polyD.multiplyAndReturnNew(polyE); 1249 tmp.multiply(polyF); 1250 tmp.multiply(polyJ); 1251 tmp.multiplyByScalar(2.0); 1252 1253 result.add(tmp); 1254 1255 // (f1 + x*f2)^2 * (h1 + x*h2)^2 1256 tmp = polyF.multiplyAndReturnNew(polyF); 1257 tmp.multiply(polyH); 1258 tmp.multiply(polyH); 1259 1260 result.add(tmp); 1261 1262 // 2 * (b1 + x*b2) * (f1 + x*f2) * (g1 + x*g2) * (i1 + x*i2) 1263 tmp = polyB.multiplyAndReturnNew(polyF); 1264 tmp.multiply(polyG); 1265 tmp.multiply(polyI); 1266 tmp.multiplyByScalar(2.0); 1267 1268 result.add(tmp); 1269 1270 // -2 * (e1 + x*e2) * (f1 + x*r2) * (g1 + x*g2) * (h1 + x*h2) 1271 tmp = polyE.multiplyAndReturnNew(polyF); 1272 tmp.multiply(polyG); 1273 tmp.multiply(polyH); 1274 tmp.multiplyByScalar(-2.0); 1275 1276 result.add(tmp); 1277 1278 // -1 * (b1 + x*b2) * (f1 + x*f2)^2 * (j1 + x*j2) 1279 tmp = polyB.multiplyAndReturnNew(polyF); 1280 tmp.multiply(polyF); 1281 tmp.multiply(polyJ); 1282 tmp.multiplyByScalar(-1.0); 1283 1284 result.add(tmp); 1285 1286 // -1 * (b1 + x*b2) * (c1 + x*c2) * (g1 + x*g2)^2 1287 tmp = polyB.multiplyAndReturnNew(polyC); 1288 tmp.multiply(polyG); 1289 tmp.multiply(polyG); 1290 tmp.multiplyByScalar(-1.0); 1291 1292 result.add(tmp); 1293 1294 // (e1 + x*e2)^2 * (g1 + x*g2)^2 1295 tmp = polyE.multiplyAndReturnNew(polyE); 1296 tmp.multiply(polyG); 1297 tmp.multiply(polyG); 1298 1299 result.add(tmp); 1300 1301 return result; 1302 } 1303 }