1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.irurueta.ar.calibration.estimators;
18
19 import com.irurueta.algebra.AlgebraException;
20 import com.irurueta.algebra.Matrix;
21 import com.irurueta.algebra.SingularValueDecomposer;
22 import com.irurueta.algebra.Utils;
23 import com.irurueta.ar.calibration.DualAbsoluteQuadric;
24 import com.irurueta.geometry.BaseQuadric;
25 import com.irurueta.geometry.PinholeCamera;
26 import com.irurueta.geometry.estimators.LockedException;
27 import com.irurueta.geometry.estimators.NotReadyException;
28 import com.irurueta.numerical.NumericalException;
29 import com.irurueta.numerical.robust.WeightSelection;
30 import com.irurueta.sorting.SortingException;
31
32 import java.util.List;
33
34
35
36
37
38
39
40
41
42
43
44
45 @SuppressWarnings("DuplicatedCode")
46 public class WeightedDualAbsoluteQuadricEstimator extends DualAbsoluteQuadricEstimator {
47
48
49
50
51
52 public static final int DEFAULT_MAX_CAMERAS = 50;
53
54
55
56
57
58 public static final boolean DEFAULT_SORT_WEIGHTS = true;
59
60
61
62
63
64 private int maxCameras;
65
66
67
68
69
70 private boolean sortWeights;
71
72
73
74
75 private double[] weights;
76
77
78
79
80 public WeightedDualAbsoluteQuadricEstimator() {
81 super();
82 maxCameras = DEFAULT_MAX_CAMERAS;
83 sortWeights = DEFAULT_SORT_WEIGHTS;
84 weights = null;
85 }
86
87
88
89
90
91
92
93 public WeightedDualAbsoluteQuadricEstimator(final DualAbsoluteQuadricEstimatorListener listener) {
94 super(listener);
95 maxCameras = DEFAULT_MAX_CAMERAS;
96 sortWeights = DEFAULT_SORT_WEIGHTS;
97 weights = null;
98 }
99
100
101
102
103
104
105
106
107 public WeightedDualAbsoluteQuadricEstimator(final List<PinholeCamera> cameras) {
108 super(cameras);
109 maxCameras = DEFAULT_MAX_CAMERAS;
110 sortWeights = DEFAULT_SORT_WEIGHTS;
111 weights = null;
112 }
113
114
115
116
117
118
119
120
121
122
123 public WeightedDualAbsoluteQuadricEstimator(
124 final List<PinholeCamera> cameras, final DualAbsoluteQuadricEstimatorListener listener) {
125 super(cameras, listener);
126 maxCameras = DEFAULT_MAX_CAMERAS;
127 sortWeights = DEFAULT_SORT_WEIGHTS;
128 weights = null;
129 }
130
131
132
133
134
135
136
137
138
139
140
141
142 public WeightedDualAbsoluteQuadricEstimator(
143 final List<PinholeCamera> cameras, final double[] weights) {
144 super(cameras);
145 maxCameras = DEFAULT_MAX_CAMERAS;
146 sortWeights = DEFAULT_SORT_WEIGHTS;
147 try {
148 setWeights(weights);
149 } catch (final LockedException ignore) {
150
151 }
152 }
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 public WeightedDualAbsoluteQuadricEstimator(
168 final List<PinholeCamera> cameras, final double[] weights,
169 final DualAbsoluteQuadricEstimatorListener listener) {
170 super(cameras, listener);
171 maxCameras = DEFAULT_MAX_CAMERAS;
172 sortWeights = DEFAULT_SORT_WEIGHTS;
173 try {
174 setWeights(weights);
175 } catch (final LockedException ignore) {
176
177 }
178 }
179
180
181
182
183
184
185
186
187
188
189 public static boolean areValidCamerasAndWeights(final List<PinholeCamera> cameras, final double[] weights) {
190 return cameras != null && weights != null && cameras.size() == weights.length;
191 }
192
193
194
195
196
197
198
199
200 public double[] getWeights() {
201 return weights;
202 }
203
204
205
206
207
208
209
210
211
212
213
214 public final void setWeights(final double[] weights) throws LockedException {
215 if (!areValidCamerasAndWeights(cameras, weights)) {
216 throw new IllegalArgumentException("cameras and weights must have the same length");
217 }
218 if (isLocked()) {
219 throw new LockedException();
220 }
221
222 this.weights = weights;
223 }
224
225
226
227
228
229
230
231
232
233
234
235
236
237 public void setCamerasAndWeights(final List<PinholeCamera> cameras, final double[] weights) throws LockedException {
238 if (!areValidCamerasAndWeights(cameras, weights)) {
239 throw new IllegalArgumentException("cameras and weights must have the same length");
240 }
241 if (isLocked()) {
242 throw new LockedException();
243 }
244
245 this.cameras = cameras;
246 this.weights = weights;
247 }
248
249
250
251
252
253
254 public boolean areWeightsAvailable() {
255 return weights != null;
256 }
257
258
259
260
261
262
263
264 public int getMaxCameras() {
265 return maxCameras;
266 }
267
268
269
270
271
272
273
274
275
276 public void setMaxCameras(final int maxCameras) throws LockedException {
277 if (isLocked()) {
278 throw new LockedException();
279 }
280
281 this.maxCameras = maxCameras;
282 }
283
284
285
286
287
288
289
290 public boolean isSortWeightsEnabled() {
291 return sortWeights;
292 }
293
294
295
296
297
298
299
300
301
302 public void setSortWeightsEnabled(final boolean sortWeights) throws LockedException {
303 if (isLocked()) {
304 throw new LockedException();
305 }
306
307 this.sortWeights = sortWeights;
308 }
309
310
311
312
313
314
315 @Override
316 public boolean isReady() {
317 return super.isReady() && areWeightsAvailable() && maxCameras >= getMinNumberOfRequiredCameras();
318 }
319
320
321
322
323
324
325
326
327
328
329
330
331
332 @Override
333 public void estimate(final DualAbsoluteQuadric result) throws LockedException, NotReadyException,
334 DualAbsoluteQuadricEstimatorException {
335
336 if (isLocked()) {
337 throw new LockedException();
338 }
339 if (!isReady()) {
340 throw new NotReadyException();
341 }
342
343 try {
344 locked = true;
345 if (listener != null) {
346 listener.onEstimateStart(this);
347 }
348
349 if (principalPointAtOrigin) {
350 if (zeroSkewness) {
351 if (focalDistanceAspectRatioKnown) {
352 estimateZeroSkewnessPrincipalPointAtOriginAndKnownFocalDistanceAspectRatio(result);
353 } else {
354 estimateZeroSkewnessAndPrincipalPointAtOrigin(result);
355 }
356 } else {
357 estimatePrincipalPointAtOrigin(result);
358 }
359 }
360
361 if (listener != null) {
362 listener.onEstimateEnd(this);
363 }
364 } finally {
365 locked = false;
366 }
367 }
368
369
370
371
372
373
374 @Override
375 public DualAbsoluteQuadricEstimatorType getType() {
376 return DualAbsoluteQuadricEstimatorType.WEIGHTED_DUAL_ABSOLUTE_QUADRIC_ESTIMATOR;
377 }
378
379
380
381
382
383
384
385
386 @Override
387 public boolean areValidConstraints() {
388 return super.areValidConstraints() && isZeroSkewness() && isFocalDistanceAspectRatioKnown();
389 }
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404 private void estimateZeroSkewnessPrincipalPointAtOriginAndKnownFocalDistanceAspectRatio(
405 final DualAbsoluteQuadric result) throws DualAbsoluteQuadricEstimatorException {
406
407 try {
408 final var nCams = Math.min(cameras.size(), maxCameras);
409
410 final var selection = WeightSelection.selectWeights(weights, sortWeights, nCams);
411 final var selected = selection.getSelected();
412
413 final var a = new Matrix(BaseQuadric.N_PARAMS, BaseQuadric.N_PARAMS);
414 final var row = new Matrix(4, BaseQuadric.N_PARAMS);
415 final var transRow = new Matrix(BaseQuadric.N_PARAMS, 4);
416 final var tmp = new Matrix(BaseQuadric.N_PARAMS, BaseQuadric.N_PARAMS);
417
418 Matrix cameraMatrix;
419 double p11;
420 double p12;
421 double p13;
422 double p14;
423 double p21;
424 double p22;
425 double p23;
426 double p24;
427 double p31;
428 double p32;
429 double p33;
430 double p34;
431 int eqCounter;
432 var cameraCounter = 0;
433 double weight;
434 var previousNorm = 1.0;
435 for (final var camera : cameras) {
436 if (selected[cameraCounter]) {
437 eqCounter = 0;
438
439
440 camera.normalize();
441
442 cameraMatrix = camera.getInternalMatrix();
443
444 p11 = cameraMatrix.getElementAt(0, 0);
445 p21 = cameraMatrix.getElementAt(1, 0);
446 p31 = cameraMatrix.getElementAt(2, 0);
447
448 p12 = cameraMatrix.getElementAt(0, 1);
449 p22 = cameraMatrix.getElementAt(1, 1);
450 p32 = cameraMatrix.getElementAt(2, 1);
451
452 p13 = cameraMatrix.getElementAt(0, 2);
453 p23 = cameraMatrix.getElementAt(1, 2);
454 p33 = cameraMatrix.getElementAt(2, 2);
455
456 p14 = cameraMatrix.getElementAt(0, 3);
457 p24 = cameraMatrix.getElementAt(1, 3);
458 p34 = cameraMatrix.getElementAt(2, 3);
459
460 weight = weights[cameraCounter];
461
462
463 fill2ndRowAnd1stRowEquation(p11, p21, p12, p22, p13, p23, p14, p24, row, eqCounter);
464 applyWeight(row, eqCounter, weight);
465 eqCounter++;
466
467
468 fill3rdRowAnd1stRowEquation(p11, p31, p12, p32, p13, p33, p14, p34, row, eqCounter);
469 applyWeight(row, eqCounter, weight);
470 eqCounter++;
471
472
473 fill3rdRowAnd2ndRowEquation(p21, p31, p22, p32, p23, p33, p24, p34, row, eqCounter);
474 applyWeight(row, eqCounter, weight);
475 eqCounter++;
476
477
478 fill1stRowEqualTo2ndRowEquation(p11, p21, p12, p22, p13, p23, p14, p24, row, eqCounter);
479 applyWeight(row, eqCounter, weight);
480
481
482 row.transpose(transRow);
483 transRow.multiply(row, tmp);
484
485 tmp.multiplyByScalar(1.0 / previousNorm);
486
487
488 a.add(tmp);
489
490 previousNorm = Utils.normF(a);
491 a.multiplyByScalar(1.0 / previousNorm);
492 }
493
494 cameraCounter++;
495 }
496
497 final var decomposer = new SingularValueDecomposer(a);
498 enforceRank3IfNeeded(decomposer, result);
499
500 } catch (final AlgebraException | SortingException | NumericalException e) {
501 throw new DualAbsoluteQuadricEstimatorException(e);
502 }
503 }
504
505
506
507
508
509
510
511
512
513
514
515
516
517 private void estimateZeroSkewnessAndPrincipalPointAtOrigin(final DualAbsoluteQuadric result)
518 throws DualAbsoluteQuadricEstimatorException {
519
520 try {
521 final var nCams = Math.min(cameras.size(), maxCameras);
522
523 final var selection = WeightSelection.selectWeights(weights, sortWeights, nCams);
524 final var selected = selection.getSelected();
525
526 final var a = new Matrix(BaseQuadric.N_PARAMS, BaseQuadric.N_PARAMS);
527 final var row = new Matrix(3, BaseQuadric.N_PARAMS);
528 final var transRow = new Matrix(BaseQuadric.N_PARAMS, 3);
529 final var tmp = new Matrix(BaseQuadric.N_PARAMS, BaseQuadric.N_PARAMS);
530
531 Matrix cameraMatrix;
532 double p11;
533 double p12;
534 double p13;
535 double p14;
536 double p21;
537 double p22;
538 double p23;
539 double p24;
540 double p31;
541 double p32;
542 double p33;
543 double p34;
544 int eqCounter;
545 var cameraCounter = 0;
546 double weight;
547 var previousNorm = 1.0;
548 for (final var camera : cameras) {
549 if (selected[cameraCounter]) {
550 eqCounter = 0;
551
552
553 camera.normalize();
554
555 cameraMatrix = camera.getInternalMatrix();
556
557 p11 = cameraMatrix.getElementAt(0, 0);
558 p21 = cameraMatrix.getElementAt(1, 0);
559 p31 = cameraMatrix.getElementAt(2, 0);
560
561 p12 = cameraMatrix.getElementAt(0, 1);
562 p22 = cameraMatrix.getElementAt(1, 1);
563 p32 = cameraMatrix.getElementAt(2, 1);
564
565 p13 = cameraMatrix.getElementAt(0, 2);
566 p23 = cameraMatrix.getElementAt(1, 2);
567 p33 = cameraMatrix.getElementAt(2, 2);
568
569 p14 = cameraMatrix.getElementAt(0, 3);
570 p24 = cameraMatrix.getElementAt(1, 3);
571 p34 = cameraMatrix.getElementAt(2, 3);
572
573 weight = weights[cameraCounter];
574
575
576 fill2ndRowAnd1stRowEquation(p11, p21, p12, p22, p13, p23, p14, p24, a, eqCounter);
577 applyWeight(row, eqCounter, weight);
578 eqCounter++;
579
580
581 fill3rdRowAnd1stRowEquation(p11, p31, p12, p32, p13, p33, p14, p34, a, eqCounter);
582 applyWeight(row, eqCounter, weight);
583 eqCounter++;
584
585
586 fill3rdRowAnd2ndRowEquation(p21, p31, p22, p32, p23, p33, p24, p34, a, eqCounter);
587 applyWeight(row, eqCounter, weight);
588
589
590 row.transpose(transRow);
591 transRow.multiply(row, tmp);
592
593 tmp.multiplyByScalar(1.0 / previousNorm);
594
595
596 a.add(tmp);
597
598 previousNorm = Utils.normF(a);
599 a.multiplyByScalar(1.0 / previousNorm);
600 }
601
602 cameraCounter++;
603 }
604
605 final var decomposer = new SingularValueDecomposer(a);
606 enforceRank3IfNeeded(decomposer, result);
607
608 } catch (final AlgebraException | SortingException | NumericalException e) {
609 throw new DualAbsoluteQuadricEstimatorException(e);
610 }
611 }
612
613
614
615
616
617
618
619
620
621
622
623
624
625 private void estimatePrincipalPointAtOrigin(DualAbsoluteQuadric result)
626 throws DualAbsoluteQuadricEstimatorException {
627
628 try {
629 final var nCams = Math.min(cameras.size(), maxCameras);
630
631 final var selection = WeightSelection.selectWeights(weights, sortWeights, nCams);
632 final var selected = selection.getSelected();
633
634 final var a = new Matrix(BaseQuadric.N_PARAMS, BaseQuadric.N_PARAMS);
635 final var row = new Matrix(2, BaseQuadric.N_PARAMS);
636 final var transRow = new Matrix(BaseQuadric.N_PARAMS, 2);
637 final var tmp = new Matrix(BaseQuadric.N_PARAMS, BaseQuadric.N_PARAMS);
638
639 Matrix cameraMatrix;
640 double p11;
641 double p12;
642 double p13;
643 double p14;
644 double p21;
645 double p22;
646 double p23;
647 double p24;
648 double p31;
649 double p32;
650 double p33;
651 double p34;
652 int eqCounter;
653 var cameraCounter = 0;
654 double weight;
655 var previousNorm = 1.0;
656 for (final var camera : cameras) {
657 if (selected[cameraCounter]) {
658 eqCounter = 0;
659
660
661 camera.normalize();
662
663 cameraMatrix = camera.getInternalMatrix();
664
665 p11 = cameraMatrix.getElementAt(0, 0);
666 p21 = cameraMatrix.getElementAt(1, 0);
667 p31 = cameraMatrix.getElementAt(2, 0);
668
669 p12 = cameraMatrix.getElementAt(0, 1);
670 p22 = cameraMatrix.getElementAt(1, 1);
671 p32 = cameraMatrix.getElementAt(2, 1);
672
673 p13 = cameraMatrix.getElementAt(0, 2);
674 p23 = cameraMatrix.getElementAt(1, 2);
675 p33 = cameraMatrix.getElementAt(2, 2);
676
677 p14 = cameraMatrix.getElementAt(0, 3);
678 p24 = cameraMatrix.getElementAt(1, 3);
679 p34 = cameraMatrix.getElementAt(2, 3);
680
681 weight = weights[cameraCounter];
682
683
684 fill3rdRowAnd1stRowEquation(p11, p31, p12, p32, p13, p33, p14, p34, a, eqCounter);
685 applyWeight(row, eqCounter, weight);
686 eqCounter++;
687
688
689 fill3rdRowAnd2ndRowEquation(p21, p31, p22, p32, p23, p33, p24, p34, a, eqCounter);
690 applyWeight(row, eqCounter, weight);
691
692
693 row.transpose(transRow);
694 transRow.multiply(row, tmp);
695
696 tmp.multiplyByScalar(1.0 / previousNorm);
697
698
699 a.add(tmp);
700
701 previousNorm = Utils.normF(a);
702 a.multiplyByScalar(1.0 / previousNorm);
703 }
704
705 cameraCounter++;
706 }
707
708 final var decomposer = new SingularValueDecomposer(a);
709 enforceRank3IfNeeded(decomposer, result);
710
711 } catch (final AlgebraException | SortingException | NumericalException e) {
712 throw new DualAbsoluteQuadricEstimatorException(e);
713 }
714 }
715
716
717
718
719
720
721
722
723 private void applyWeight(final Matrix a, final int row, final double weight) {
724 final var cols = a.getColumns();
725 for (var i = 0; i < cols; i++) {
726 a.setElementAt(row, i, a.getElementAt(row, i) * weight);
727 }
728 }
729 }