View Javadoc
1   /*
2    * Copyright (C) 2013 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.geometry.estimators;
17  
18  import com.irurueta.geometry.InhomogeneousPoint2D;
19  import com.irurueta.geometry.InhomogeneousPoint3D;
20  import com.irurueta.geometry.PinholeCamera;
21  import com.irurueta.geometry.Quaternion;
22  
23  /**
24   * This class defines the interface for an estimator for pinhole cameras.
25   */
26  public abstract class PinholeCameraEstimator {
27  
28      /**
29       * Default estimator type.
30       */
31      public static final PinholeCameraEstimatorType DEFAULT_ESTIMATOR_TYPE =
32              PinholeCameraEstimatorType.DLT_POINT_PINHOLE_CAMERA_ESTIMATOR;
33  
34      /**
35       * Default value indicating whether skewness value is suggested or not.
36       * By default, this is disabled.
37       */
38      public static final boolean DEFAULT_SUGGEST_SKEWNESS_VALUE_ENABLED = false;
39  
40      /**
41       * Default value of skewness to be suggested when suggestion is enabled.
42       * By default suggested skewness is zero.
43       */
44      public static final double DEFAULT_SUGGESTED_SKEWNESS_VALUE = 0.0;
45  
46      /**
47       * Default value indicating whether horizontal focal length value is
48       * suggested or not. By default, this is disabled.
49       */
50      public static final boolean DEFAULT_SUGGEST_HORIZONTAL_FOCAL_LENGTH_ENABLED = false;
51  
52      /**
53       * Default value indicating whether vertical focal length value is suggested
54       * or not. By default, this is disabled.
55       */
56      public static final boolean DEFAULT_SUGGEST_VERTICAL_FOCAL_LENGTH_ENABLED = false;
57  
58      /**
59       * Default value indicating whether aspect ratio is suggested or not. By
60       * default, this is disabled.
61       */
62      public static final boolean DEFAULT_SUGGEST_ASPECT_RATIO_ENABLED = false;
63  
64      /**
65       * Default value of aspect ratio to be suggested when suggestion is enabled.
66       * By default, suggested aspect ratio is 1.0, although also -1.0 is a typical
67       * value when vertical coordinates increase downwards.
68       */
69      public static final double DEFAULT_SUGGESTED_ASPECT_RATIO_VALUE = 1.0;
70  
71      /**
72       * Default value indicating whether principal point is suggested or not. By
73       * default, this is disabled.
74       */
75      public static final boolean DEFAULT_SUGGEST_PRINCIPAL_POINT_ENABLED = false;
76  
77      /**
78       * Default value indicating whether rotation is suggested or not. By default,
79       * this is disabled.
80       */
81      public static final boolean DEFAULT_SUGGEST_ROTATION_ENABLED = false;
82  
83      /**
84       * Default value indicating whether center is suggested or not. By default,
85       * this is disabled.
86       */
87      public static final boolean DEFAULT_SUGGEST_CENTER_ENABLED = false;
88  
89      /**
90       * Default value for minimum suggestion weight. This weight is used to
91       * slowly draw original camera parameters into desired suggested values.
92       * Suggestion weight slowly increases each time Levenberg-Marquardt is used
93       * to find a solution so that the algorithm can converge into desired value.
94       * The faster the weights are increased the less likely that suggested
95       * values can be converged if they differ too much from the original ones.
96       */
97      public static final double DEFAULT_MIN_SUGGESTION_WEIGHT = 0.1;
98  
99      /**
100      * Default value for maximum suggestion weight. This weight is used to
101      * slowly draw original camera parameters into desired suggested values.
102      * Suggestion weight slowly increases each time Levenberg-Marquardt is used
103      * to find a solution so that the algorithm can converge into desired value.
104      * The faster the weights are increased the less likely that suggested
105      * values can be converged if they differ too much from the original ones.
106      */
107     public static final double DEFAULT_MAX_SUGGESTION_WEIGHT = 2.0;
108 
109     /**
110      * Default value for the step to increase suggestion weight. This weight is
111      * used to slowly draw original camera parameters into desired suggested
112      * values. Suggestion weight slowly increases each time Levenberg-Marquardt
113      * is used to find a solution so that the algorithm can converge into
114      * desired value. The faster the weights are increased the less likely that
115      * suggested values can be converged if they differ too much from the
116      * original ones.
117      */
118     public static final double DEFAULT_SUGGESTION_WEIGHT_STEP = 0.475;
119 
120     /**
121      * True when an estimator is estimating a camera.
122      */
123     protected boolean locked;
124 
125     /**
126      * Listener to be notified of events such as when estimation starts, ends
127      * or estimation progress changes.
128      */
129     protected PinholeCameraEstimatorListener listener;
130 
131     /**
132      * Indicates whether skewness value is suggested or not. When enabled, the
133      * estimator will attempt to enforce suggested value in an iterative manner
134      * starting from an initially estimated camera.
135      * Even when suggestion is enabled, the iterative algorithm might not reach
136      * suggested value if the initial value largely differs from the suggested
137      * value.
138      */
139     protected boolean suggestSkewnessValueEnabled = DEFAULT_SUGGEST_SKEWNESS_VALUE_ENABLED;
140 
141     /**
142      * Suggested skewness value to be reached when suggestion is enabled.
143      * Suggested value should be close to the initially estimated value
144      * otherwise the iterative refinement might not converge to provided
145      * value.
146      */
147     protected double suggestedSkewnessValue = DEFAULT_SUGGESTED_SKEWNESS_VALUE;
148 
149     /**
150      * Indicates whether horizontal focal length is suggested or not. When
151      * enabled, the estimator will attempt to enforce suggested value in an
152      * iterative manner starting from an initially estimated camera.
153      * Even when suggestion is enabled, the iterative algorithm might not reach
154      * suggested value if the initial value largely differs from the suggested
155      * value.
156      */
157     protected boolean suggestHorizontalFocalLengthEnabled = DEFAULT_SUGGEST_HORIZONTAL_FOCAL_LENGTH_ENABLED;
158 
159     /**
160      * Suggested horizontal focal length value to be reached when suggestion is
161      * enabled.
162      * Suggested value should be close to the initially estimated value
163      * otherwise the iterative refinement might not converge to provided value.
164      */
165     protected double suggestedHorizontalFocalLengthValue;
166 
167     /**
168      * Indicates whether vertical focal length is suggested or not. When
169      * enabled, the estimator will attempt to enforce suggested value in an
170      * iterative manner starting from an initially estimated camera.
171      * Even when suggestion is enabled, the iterative algorithm might not reach
172      * suggested value if the initial value largely differs from the suggested
173      * value.
174      */
175     protected boolean suggestVerticalFocalLengthEnabled = DEFAULT_SUGGEST_VERTICAL_FOCAL_LENGTH_ENABLED;
176 
177     /**
178      * Suggested vertical focal length value to be reached when suggestion is
179      * enabled.
180      * Suggested value should be close to the initially estimated value
181      * otherwise the iterative refinement might not converge to provided value.
182      */
183     protected double suggestedVerticalFocalLengthValue;
184 
185     /**
186      * Indicates whether aspect ratio is suggested or not. When enabled, the
187      * estimator will attempt to enforce suggested value in an iterative manner
188      * starting from an initially estimated camera.
189      * Even when suggestion is enabled, the iterative algorithm might not reach
190      * suggested value if the initial value largely differs from the suggested
191      * value.
192      */
193     protected boolean suggestAspectRatioEnabled = DEFAULT_SUGGEST_ASPECT_RATIO_ENABLED;
194 
195     /**
196      * Suggested aspect ratio value to be reached when suggestion is enabled.
197      * Suggested value should be close to the initially estimated value
198      * otherwise the iterative refinement might not converge to provided value.
199      */
200     protected double suggestedAspectRatioValue = DEFAULT_SUGGESTED_ASPECT_RATIO_VALUE;
201 
202     /**
203      * Indicates whether principal point is suggested or not. When enabled, the
204      * estimator will attempt to enforce suggested value in an iterative manner
205      * starting from an initially estimated camera.
206      * Even when suggestion is enabled, the iterative algorithm might not reach
207      * suggested value if the initial value largely differs from the suggested
208      * value.
209      */
210     protected boolean suggestPrincipalPointEnabled = DEFAULT_SUGGEST_PRINCIPAL_POINT_ENABLED;
211 
212     /**
213      * Suggested principal point value to be reached when suggestion is enabled.
214      * Suggested value should be close to the initially estimated value
215      * otherwise the iterative refinement might not converge to provided value.
216      */
217     protected InhomogeneousPoint2D suggestedPrincipalPointValue;
218 
219     /**
220      * Indicates whether camera rotation is suggested or not. When enabled, the
221      * estimator will attempt to enforce suggested value in an iterative manner
222      * starting from an initially estimated camera.
223      * Even when suggestion is enabled, the iterative algorithm might not reach
224      * suggested value if the initial value largely differs from the suggested
225      * value.
226      */
227     protected boolean suggestRotationEnabled = DEFAULT_SUGGEST_ROTATION_ENABLED;
228 
229     /**
230      * Suggested rotation to be reached when suggestion is enabled.
231      * Suggested value should be close to the initially estimated value
232      * otherwise the iterative refinement might not converge to provided value.
233      */
234     protected Quaternion suggestedRotationValue;
235 
236     /**
237      * Indicates whether camera center is suggested or not. When enabled, the
238      * estimator will attempt to enforce suggested value in an iterative manner
239      * starting from an initially estimated camera.
240      * Even when suggestion is enabled, the iterative algorithm might not reach
241      * suggested value if the initial value largely differs from the suggested
242      * value.
243      */
244     protected boolean suggestCenterEnabled;
245 
246     /**
247      * Suggested center to be reached when suggestion is enabled.
248      * Suggested value should be close to the initially estimated value
249      * otherwise the iterative refinement might not converge to provided value.
250      */
251     protected InhomogeneousPoint3D suggestedCenterValue;
252 
253     /**
254      * Minimum suggestion weight. This weight is used to slowly draw original
255      * camera parameters into desired suggested values.
256      * Suggestion weight slowly increases each time Levenberg-Marquardt is used
257      * to find a solution so that the algorithm can converge into desired value.
258      * The faster the weights are increased the less likely that suggested
259      * values can be converged if they differ too much from the original ones.
260      */
261     protected double minSuggestionWeight = DEFAULT_MIN_SUGGESTION_WEIGHT;
262 
263     /**
264      * Maximum suggestion weight. This weight is used to slowly draw original
265      * camera parameters into desired suggested values.
266      * Suggestion weight slowly increases each time Levenberg-Marquardt is used
267      * to find a solution so that the algorithm can converge into desired value.
268      * The faster the weights are increased the less likely that suggested
269      * values can be converged if they differ too much from the original ones.
270      */
271     protected double maxSuggestionWeight = DEFAULT_MAX_SUGGESTION_WEIGHT;
272 
273     /**
274      * Step to increase suggestion weight. This weight is used to slowly draw
275      * original camera parameters into desired suggested values. Suggestion
276      * weight slowly increases each time Levenberg-Marquardt is used to find a
277      * solution so that the algorithm can converge into desired value. The
278      * faster the weights are increased the less likely that suggested values
279      * can be converged if they differ too much from the original ones.
280      */
281     protected double suggestionWeightStep = DEFAULT_SUGGESTION_WEIGHT_STEP;
282 
283     /**
284      * Constructor.
285      */
286     protected PinholeCameraEstimator() {
287         locked = false;
288         listener = null;
289     }
290 
291     /**
292      * Constructor with listener.
293      *
294      * @param listener listener to be notified of events such as when estimation
295      *                 starts, ends or estimation progress changes.
296      */
297     protected PinholeCameraEstimator(final PinholeCameraEstimatorListener listener) {
298         locked = false;
299         this.listener = listener;
300     }
301 
302     /**
303      * Returns listener to be notified of events such as when estimation starts,
304      * ends or estimation progress changes.
305      *
306      * @return listener to be notified of events.
307      */
308     public PinholeCameraEstimatorListener getListener() {
309         return listener;
310     }
311 
312     /**
313      * Sets listener to be notified of events such as when estimation starts,
314      * ends or estimation progress changes.
315      *
316      * @param listener listener to be notified of events.
317      * @throws LockedException if estimator is locked.
318      */
319     public void setListener(final PinholeCameraEstimatorListener listener) throws LockedException {
320         if (isLocked()) {
321             throw new LockedException();
322         }
323         this.listener = listener;
324     }
325 
326     /**
327      * Indicates whether skewness value is suggested or not. When enabled, the
328      * estimator will attempt to enforce suggested value in an iterative manner
329      * starting from an initially estimated camera.
330      * Even when suggestion is enabled, the iterative algorithm might not reach
331      * suggested value if the initial value largely differs from the suggested
332      * value.
333      *
334      * @return true if skewness value is suggested, false otherwise.
335      */
336     public boolean isSuggestSkewnessValueEnabled() {
337         return suggestSkewnessValueEnabled;
338     }
339 
340     /**
341      * Specifies whether skewness value is suggested or not. When enabled, the
342      * estimator will attempt to enforce suggested value in an iterative manner
343      * starting from an initially estimated camera.
344      * Even when suggestion is enabled, the iterative algorithm might not reach
345      * suggested value if the initial value largely differs from the suggested
346      * value.
347      *
348      * @param suggestSkewnessValueEnabled true if skewness value is suggested,
349      *                                    false otherwise.
350      * @throws LockedException if estimator is locked.
351      */
352     public void setSuggestSkewnessValueEnabled(final boolean suggestSkewnessValueEnabled) throws LockedException {
353         if (isLocked()) {
354             throw new LockedException();
355         }
356         this.suggestSkewnessValueEnabled = suggestSkewnessValueEnabled;
357     }
358 
359     /**
360      * Gets suggested skewness value to be reached when suggestion is enabled.
361      * Suggested value should be close to the initially estimated value
362      * otherwise the iterative refinement might not converge to provided value.
363      *
364      * @return suggested skewness value.
365      */
366     public double getSuggestedSkewnessValue() {
367         return suggestedSkewnessValue;
368     }
369 
370     /**
371      * Sets suggested skewness value to be reached when suggestion is enabled.
372      * Suggested value should be close to the initially estimated value
373      * otherwise the iterative refinement might not converge to provided value.
374      *
375      * @param suggestedSkewnessValue suggested skewness value.
376      * @throws LockedException if estimator is locked.
377      */
378     public void setSuggestedSkewnessValue(final double suggestedSkewnessValue) throws LockedException {
379         if (isLocked()) {
380             throw new LockedException();
381         }
382         this.suggestedSkewnessValue = suggestedSkewnessValue;
383     }
384 
385     /**
386      * Indicates whether horizontal focal length is suggested or not. When
387      * enabled, the estimator will attempt to enforce suggested value in an
388      * iterative manner starting from an initially estimated camera.
389      * Even when suggestion is enabled, the iterative algorithm might not reach
390      * suggested value if the initial value largely differs from the suggested
391      * value.
392      *
393      * @return true if horizontal focal length is suggested, false otherwise.
394      */
395     public boolean isSuggestHorizontalFocalLengthEnabled() {
396         return suggestHorizontalFocalLengthEnabled;
397     }
398 
399     /**
400      * Specifies whether horizontal focal length is suggested or not. When
401      * enabled, the estimator will attempt to enforce suggested value in an
402      * iterative manner starting from an initially estimated camera.
403      * Even when suggestion is enabled, the iterative algorithm might not reach
404      * suggested value if the initial value largely differs from the suggested
405      * value.
406      *
407      * @param suggestHorizontalFocalLengthEnabled true if horizontal focal
408      *                                            length is suggested, false otherwise.
409      * @throws LockedException if estimator is locked.
410      */
411     public void setSuggestHorizontalFocalLengthEnabled(final boolean suggestHorizontalFocalLengthEnabled)
412             throws LockedException {
413         if (isLocked()) {
414             throw new LockedException();
415         }
416         this.suggestHorizontalFocalLengthEnabled =
417                 suggestHorizontalFocalLengthEnabled;
418     }
419 
420     /**
421      * Gets suggested horizontal focal length value to be reached when
422      * suggestion is enabled.
423      * Suggested value should be close to the initially estimated value
424      * otherwise the iterative refinement might not converge to provided value.
425      *
426      * @return suggested horizontal focal length value.
427      */
428     public double getSuggestedHorizontalFocalLengthValue() {
429         return suggestedHorizontalFocalLengthValue;
430     }
431 
432     /**
433      * Sets suggested horizontal focal length value to be reached when
434      * suggestion is enabled.
435      * Suggested value should be close to the initially estimated value
436      * otherwise the iterative refinement might not converge to provided value.
437      *
438      * @param suggestedHorizontalFocalLengthValue suggested horizontal focal
439      *                                            length value.
440      * @throws LockedException if estimator is locked.
441      */
442     public void setSuggestedHorizontalFocalLengthValue(final double suggestedHorizontalFocalLengthValue)
443             throws LockedException {
444         if (isLocked()) {
445             throw new LockedException();
446         }
447         this.suggestedHorizontalFocalLengthValue = suggestedHorizontalFocalLengthValue;
448     }
449 
450     /**
451      * Indicates whether vertical focal length is suggested or not. When
452      * enabled, the estimator will attempt to enforce suggested value in an
453      * iterative manner starting from an initially estimated camera.
454      * Even when suggestion is enabled, the iterative algorithm might not reach
455      * suggested value if the initial value largely differs from the suggested
456      * value.
457      *
458      * @return true if vertical focal length is suggested, false otherwise.
459      */
460     public boolean isSuggestVerticalFocalLengthEnabled() {
461         return suggestVerticalFocalLengthEnabled;
462     }
463 
464     /**
465      * Specifies whether vertical focal length is suggested or not. When
466      * enabled, the estimator will attempt to enforce suggested value in an
467      * iterative manner starting from an initially estimated camera.
468      * Even when suggestion is enabled, the iterative algorithm might not reach
469      * suggested value if the initial value largely differs from the suggested
470      * value.
471      *
472      * @param suggestVerticalFocalLengthEnabled true if vertical focal length is
473      *                                          suggested, false otherwise.
474      * @throws LockedException if estimator is locked.
475      */
476     public void setSuggestVerticalFocalLengthEnabled(final boolean suggestVerticalFocalLengthEnabled)
477             throws LockedException {
478         if (isLocked()) {
479             throw new LockedException();
480         }
481         this.suggestVerticalFocalLengthEnabled = suggestVerticalFocalLengthEnabled;
482     }
483 
484     /**
485      * Gets suggested vertical focal length value to be reached when suggestion
486      * is enabled.
487      * Suggested value should be close to the initially estimated value
488      * otherwise the iterative refinement might not converge to provided value.
489      *
490      * @return suggested vertical focal length.
491      */
492     public double getSuggestedVerticalFocalLengthValue() {
493         return suggestedVerticalFocalLengthValue;
494     }
495 
496     /**
497      * Sets suggested vertical focal length value to be reached when suggestion
498      * is enabled.
499      * Suggested value should be close to the initially estimated value
500      * otherwise the iterative refinement might not converge to provided value.
501      *
502      * @param suggestedVerticalFocalLengthValue suggested vertical focal length.
503      * @throws LockedException if estimator is locked.
504      */
505     public void setSuggestedVerticalFocalLengthValue(final double suggestedVerticalFocalLengthValue)
506             throws LockedException {
507         if (isLocked()) {
508             throw new LockedException();
509         }
510         this.suggestedVerticalFocalLengthValue = suggestedVerticalFocalLengthValue;
511     }
512 
513     /**
514      * Indicates whether aspect ratio is suggested or not. When enabled, the
515      * estimator will attempt to enforce suggested value in an iterative manner
516      * starting from an initially estimated camera.
517      * Even when suggestion is enabled, the iterative algorithm might not reach
518      * suggested value if the initial value largely differs from the suggested
519      * value.
520      *
521      * @return true if aspect ratio is suggested, false otherwise.
522      */
523     public boolean isSuggestAspectRatioEnabled() {
524         return suggestAspectRatioEnabled;
525     }
526 
527     /**
528      * Specifies whether aspect ratio is suggested or not. When enabled, the
529      * estimator will attempt to enforce suggested value in an iterative manner
530      * starting from an initially estimated camera.
531      * Even when suggestion is enabled, the iterative algorithm might not reach
532      * suggested value if the initial value largely differs from the suggested
533      * value.
534      *
535      * @param suggestAspectRatioEnabled true if aspect ratio is suggested, false
536      *                                  otherwise.
537      * @throws LockedException if estimator is locked.
538      */
539     public void setSuggestAspectRatioEnabled(final boolean suggestAspectRatioEnabled) throws LockedException {
540         if (isLocked()) {
541             throw new LockedException();
542         }
543         this.suggestAspectRatioEnabled = suggestAspectRatioEnabled;
544     }
545 
546     /**
547      * Gets suggested aspect ratio value to be reached when suggestion is
548      * enabled. Suggested value should be close to the initially estimated value
549      * otherwise the iterative refinement might not converge to provided value.
550      *
551      * @return suggested aspect ratio value.
552      */
553     public double getSuggestedAspectRatioValue() {
554         return suggestedAspectRatioValue;
555     }
556 
557     /**
558      * Sets suggested aspect ratio value to be reached when suggestion is
559      * enabled. Suggested value should be close to the initially estimated value
560      * otherwise the iterative refinement might not converge to provided value.
561      *
562      * @param suggestedAspectRatioValue suggested aspect ratio value.
563      * @throws LockedException if estimator is locked.
564      */
565     public void setSuggestedAspectRatioValue(final double suggestedAspectRatioValue) throws LockedException {
566         if (isLocked()) {
567             throw new LockedException();
568         }
569         this.suggestedAspectRatioValue = suggestedAspectRatioValue;
570     }
571 
572     /**
573      * Indicates whether principal point is suggested or not. When enabled, the
574      * estimator will attempt to enforce suggested value in an iterative manner
575      * starting from an initially estimated camera.
576      * Even when suggestion is enabled, the iterative algorithm might not reach
577      * suggested value if the initial value largely differs from the suggested
578      * value.
579      *
580      * @return true if principal point is suggested, false otherwise.
581      */
582     public boolean isSuggestPrincipalPointEnabled() {
583         return suggestPrincipalPointEnabled;
584     }
585 
586     /**
587      * Specifies whether principal point is suggested or not. When enabled, the
588      * estimator will attempt to enforce suggested value in an iterative manner
589      * starting from an initially estimated camera.
590      * Even when suggestion is enabled, the iterative algorithm might not reach
591      * suggested value if the initial value largely differs from the suggested
592      * value.
593      *
594      * @param suggestPrincipalPointEnabled true if principal point is suggested,
595      *                                     false otherwise.
596      * @throws LockedException if estimator is locked.
597      */
598     public void setSuggestPrincipalPointEnabled(final boolean suggestPrincipalPointEnabled) throws LockedException {
599         if (isLocked()) {
600             throw new LockedException();
601         }
602         this.suggestPrincipalPointEnabled = suggestPrincipalPointEnabled;
603         if (suggestPrincipalPointEnabled && suggestedPrincipalPointValue == null) {
604             suggestedPrincipalPointValue = new InhomogeneousPoint2D();
605         }
606     }
607 
608     /**
609      * Gets suggested principal point value to be reached when suggestion is
610      * enabled. Suggested value should be close to the initially estimated value
611      * otherwise the iterative refinement might not converge to provided value.
612      *
613      * @return suggested principal point value to be reached when suggestion is
614      * enabled.
615      */
616     public InhomogeneousPoint2D getSuggestedPrincipalPointValue() {
617         return suggestedPrincipalPointValue;
618     }
619 
620     /**
621      * Sets suggested principal point value to be reached when suggestion is
622      * enabled. Suggested value should be close to the initially estimated value
623      * otherwise the iterative refinement might not converge to provided value.
624      *
625      * @param suggestedPrincipalPointValue suggested principal point value to be
626      *                                     reached when suggestion is enabled.
627      * @throws LockedException if estimator is locked.
628      */
629     public void setSuggestedPrincipalPointValue(final InhomogeneousPoint2D suggestedPrincipalPointValue)
630             throws LockedException {
631         if (isLocked()) {
632             throw new LockedException();
633         }
634         this.suggestedPrincipalPointValue = suggestedPrincipalPointValue;
635     }
636 
637     /**
638      * Indicates whether camera rotation is suggested or not. When enabled, the
639      * estimator will attempt to enforce suggested value in an iterative manner
640      * starting from an initially estimated camera.
641      * Even when suggestion is enabled, the iterative algorithm might not reach
642      * suggested value if the initial value largely differs from the suggested
643      * value.
644      *
645      * @return true if camera rotation is suggested, false otherwise.
646      */
647     public boolean isSuggestRotationEnabled() {
648         return suggestRotationEnabled;
649     }
650 
651     /**
652      * Specifies whether camera rotation is suggested or not. When enabled, the
653      * estimator will attempt to enforce suggested value in an iterative manner
654      * starting from an initially estimated camera.
655      * Even when suggestion is enabled, the iterative algorithm might not reach
656      * suggested value if the initial value largely differs from the suggested
657      * value.
658      *
659      * @param suggestRotationEnabled true if camera rotation is suggested, false
660      *                               otherwise.
661      * @throws LockedException if estimator is locked.
662      */
663     public void setSuggestRotationEnabled(final boolean suggestRotationEnabled) throws LockedException {
664         if (isLocked()) {
665             throw new LockedException();
666         }
667         this.suggestRotationEnabled = suggestRotationEnabled;
668         if (suggestRotationEnabled && suggestedRotationValue == null) {
669             suggestedRotationValue = new Quaternion();
670         }
671     }
672 
673     /**
674      * Gets suggested rotation to be reached when suggestion is enabled.
675      * Suggested value should be close to the initially estimated value
676      * otherwise the iterative refinement might not converge to provided value.
677      *
678      * @return suggested rotation to be reached when suggestion is enabled.
679      */
680     public Quaternion getSuggestedRotationValue() {
681         return suggestedRotationValue;
682     }
683 
684     /**
685      * Sets suggested rotation to be reached when suggestion is enabled.
686      * Suggested value should be close to the initially estimated value
687      * otherwise the iterative refinement might not converge to provided value.
688      *
689      * @param suggestedRotationValue suggested rotation to be reached when
690      *                               suggestion is enabled.
691      * @throws LockedException if estimator is locked.
692      */
693     public void setSuggestedRotationValue(final Quaternion suggestedRotationValue) throws LockedException {
694         if (isLocked()) {
695             throw new LockedException();
696         }
697         this.suggestedRotationValue = suggestedRotationValue;
698     }
699 
700     /**
701      * Indicates whether camera center is suggested or not. When enabled, the
702      * estimator will attempt to enforce suggested value in an iterative manner
703      * starting from an initially estimated camera.
704      * Even when suggestion is enabled, the iterative algorithm might not reach
705      * suggested value if the initial value largely differs from the suggested
706      * value.
707      *
708      * @return true if camera center is suggested, false otherwise.
709      */
710     public boolean isSuggestCenterEnabled() {
711         return suggestCenterEnabled;
712     }
713 
714     /**
715      * Specifies whether camera center is suggested or not. When enabled, the
716      * estimator will attempt to enforce suggested value in an iterative manner
717      * starting from an initially estimated camera.
718      * Even when suggestion is enabled, the iterative algorithm might not reach
719      * suggested value if the initial value largely differs from the suggested
720      * value.
721      *
722      * @param suggestCenterEnabled true if camera is suggested, false otherwise.
723      * @throws LockedException if estimator is locked.
724      */
725     public void setSuggestCenterEnabled(final boolean suggestCenterEnabled) throws LockedException {
726         if (isLocked()) {
727             throw new LockedException();
728         }
729         this.suggestCenterEnabled = suggestCenterEnabled;
730         if (suggestCenterEnabled && suggestedCenterValue == null) {
731             suggestedCenterValue = new InhomogeneousPoint3D();
732         }
733     }
734 
735     /**
736      * Gets suggested center to be reached when suggestion is enabled.
737      * Suggested value should be close to the initially estimated value
738      * otherwise the iterative refinement might not converge to provided value.
739      *
740      * @return suggested center to be reached when suggestion is enabled.
741      */
742     public InhomogeneousPoint3D getSuggestedCenterValue() {
743         return suggestedCenterValue;
744     }
745 
746     /**
747      * Sets suggested center to be reached when suggestion is enabled.
748      * Suggested value should be close to the initially estimated value
749      * otherwise the iterative refinement might not converge to provided value.
750      *
751      * @param suggestedCenterValue suggested center to be reached when
752      *                             suggestion is enabled.
753      * @throws LockedException if estimator is locked.
754      */
755     public void setSuggestedCenterValue(final InhomogeneousPoint3D suggestedCenterValue) throws LockedException {
756         if (isLocked()) {
757             throw new LockedException();
758         }
759         this.suggestedCenterValue = suggestedCenterValue;
760     }
761 
762     /**
763      * Gets minimum suggestion weight. This weight is used to slowly draw
764      * original camera parameters into desired suggested values.
765      * Suggestion weight slowly increases each time Levenberg-Marquardt is used
766      * to find a solution so that the algorithm can converge into desired value.
767      * The faster the weights are increased the less likely that suggested
768      * values can be converged if they differ too much from the original ones.
769      *
770      * @return minimum suggestion weight.
771      */
772     public double getMinSuggestionWeight() {
773         return minSuggestionWeight;
774     }
775 
776     /**
777      * Sets minimum suggestion weight. This weight is used to slowly draw
778      * original camera parameters into desired suggested values.
779      * Suggestion weight slowly increases each time Levenberg-Marquardt is used
780      * to find a solution so that the algorithm can converge into desired value.
781      * The faster the weights are increased the less likely that suggested
782      * values can be converged if they differ too much from the original ones.
783      *
784      * @param minSuggestionWeight minimum suggestion weight.
785      * @throws LockedException if estimator is locked.
786      */
787     public void setMinSuggestionWeight(final double minSuggestionWeight) throws LockedException {
788         if (isLocked()) {
789             throw new LockedException();
790         }
791         this.minSuggestionWeight = minSuggestionWeight;
792     }
793 
794     /**
795      * Gets maximum suggestion weight. This weight is used to slowly draw
796      * original camera parameters into desired suggested values.
797      * Suggestion weight slowly increases each time Levenberg-Marquardt is used
798      * to find a solution so that the algorithm can converge into desired value.
799      * The faster the weights are increased the less likely that suggested
800      * values can be converged if they differ too much from the original ones.
801      *
802      * @return maximum suggestion weight.
803      */
804     public double getMaxSuggestionWeight() {
805         return maxSuggestionWeight;
806     }
807 
808     /**
809      * Sets maximum suggestion weight. This weight is used to slowly draw
810      * original camera parameters into desired suggested values.
811      * Suggestion weight slowly increases each time Levenberg-Marquardt is used
812      * to find a solution so that the algorithm can converge into desired value.
813      * The faster the weights are increased the less likely that suggested
814      * values can be converged if they differ too much from the original ones.
815      *
816      * @param maxSuggestionWeight maximum suggestion weight.
817      * @throws LockedException if estimator is locked.
818      */
819     public void setMaxSuggestionWeight(final double maxSuggestionWeight) throws LockedException {
820         if (isLocked()) {
821             throw new LockedException();
822         }
823         this.maxSuggestionWeight = maxSuggestionWeight;
824     }
825 
826     /**
827      * Sets minimum and maximum suggestion weights. Suggestion weight is used to
828      * slowly draw original camera parameters into desired suggested values.
829      * Suggestion weight slowly increases each time Levenberg-Marquardt is used
830      * to find a solution so that the algorithm can converge into desired value.
831      * The faster the weights are increased the less likely that suggested
832      * values can be converged if they differ too much from the original ones.
833      *
834      * @param minSuggestionWeight minimum suggestion weight.
835      * @param maxSuggestionWeight maximum suggestion weight.
836      * @throws LockedException          if estimator is locked.
837      * @throws IllegalArgumentException if minimum suggestion weight is greater
838      *                                  or equal than maximum value.
839      */
840     public void setMinMaxSuggestionWeight(final double minSuggestionWeight, final double maxSuggestionWeight)
841             throws LockedException {
842         if (isLocked()) {
843             throw new LockedException();
844         }
845         if (minSuggestionWeight >= maxSuggestionWeight) {
846             throw new IllegalArgumentException();
847         }
848 
849         this.minSuggestionWeight = minSuggestionWeight;
850         this.maxSuggestionWeight = maxSuggestionWeight;
851     }
852 
853     /**
854      * Gets step to increase suggestion weight. This weight is used to slowly
855      * draw original camera parameters into desired suggested values. Suggestion
856      * weight slowly increases each time Levenberg-Marquardt is used to find a
857      * solution so that the algorithm can converge into desired value. The
858      * faster the weights are increased the less likely that suggested values
859      * can be converged if they differ too much from the original ones.
860      *
861      * @return step to increase suggestion weight.
862      */
863     public double getSuggestionWeightStep() {
864         return suggestionWeightStep;
865     }
866 
867     /**
868      * Sets step to increase suggestion weight. This weight is used to slowly
869      * draw original camera parameters into desired suggested values. Suggestion
870      * weight slowly increases each time Levenberg-Marquardt is used to find a
871      * solution so that the algorithm can converge into desired value. The
872      * faster the weights are increased the less likely that suggested values
873      * can be converged if they differ too much from the original ones.
874      *
875      * @param suggestionWeightStep step to increase suggestion weight.
876      * @throws LockedException          if estimator is locked.
877      * @throws IllegalArgumentException if provided step is negative or zero.
878      */
879     public void setSuggestionWeightStep(final double suggestionWeightStep) throws LockedException {
880         if (isLocked()) {
881             throw new LockedException();
882         }
883         if (suggestionWeightStep <= 0.0) {
884             throw new IllegalArgumentException();
885         }
886 
887         this.suggestionWeightStep = suggestionWeightStep;
888     }
889 
890     /**
891      * Indicates whether this instance is locked.
892      *
893      * @return true if this estimator is busy estimating a camera, false
894      * otherwise.
895      */
896     public boolean isLocked() {
897         return locked;
898     }
899 
900     /**
901      * Indicates if this estimator is ready to start the estimation.
902      *
903      * @return true if estimator is ready, false otherwise.
904      */
905     public abstract boolean isReady();
906 
907     /**
908      * Estimates a pinhole camera.
909      *
910      * @return estimated pinhole camera.
911      * @throws LockedException                 if estimator is locked.
912      * @throws NotReadyException               if input has not yet been provided.
913      * @throws PinholeCameraEstimatorException if an error occurs during
914      *                                         estimation, usually because input data is not valid.
915      */
916     public abstract PinholeCamera estimate() throws LockedException, NotReadyException, PinholeCameraEstimatorException;
917 
918     /**
919      * Returns type of pinhole camera estimator.
920      *
921      * @return type of pinhole camera estimator.
922      */
923     public abstract PinholeCameraEstimatorType getType();
924 
925     /**
926      * Creates an instance of a pinhole camera estimator using default type.
927      *
928      * @return an instance of a pinhole camera estimator.
929      */
930     public static PinholeCameraEstimator create() {
931         return create(DEFAULT_ESTIMATOR_TYPE);
932     }
933 
934     /**
935      * Creates an instance of a pinhole camera estimator using provided type.
936      *
937      * @param type type of pinhole camera estimator.
938      * @return an instance of a pinhole camera estimator.
939      */
940     public static PinholeCameraEstimator create(final PinholeCameraEstimatorType type) {
941         return switch (type) {
942             case DLT_LINE_PLANE_PINHOLE_CAMERA_ESTIMATOR -> new DLTLinePlaneCorrespondencePinholeCameraEstimator();
943             case WEIGHTED_LINE_PLANE_PINHOLE_CAMERA_ESTIMATOR ->
944                     new WeightedLinePlaneCorrespondencePinholeCameraEstimator();
945             case WEIGHTED_POINT_PINHOLE_CAMERA_ESTIMATOR -> new WeightedPointCorrespondencePinholeCameraEstimator();
946             default -> new DLTPointCorrespondencePinholeCameraEstimator();
947         };
948     }
949 
950     /**
951      * Attempts to refine provided camera using requested suggestions.
952      * If no suggestions are requested or if refinement fails, provided
953      * camera is returned instead.
954      *
955      * @param pinholeCamera camera to be refined.
956      * @return refined camera.
957      */
958     protected abstract PinholeCamera attemptRefine(final PinholeCamera pinholeCamera);
959 
960     /**
961      * Indicates whether obtained solution requires refinement to apply provided
962      * suggestions.
963      *
964      * @return true if solution requires refinement to apply provided
965      * suggestions, false otherwise.
966      */
967     protected boolean hasSuggestions() {
968         return hasIntrinsicSuggestions() || hasExtrinsicSuggestions();
969     }
970 
971     /**
972      * Indicates whether suggestions for any intrinsic parameter are required
973      * or not.
974      *
975      * @return true if suggestions for any intrinsic parameters are required,
976      * false otherwise.
977      */
978     private boolean hasIntrinsicSuggestions() {
979         return suggestSkewnessValueEnabled || suggestHorizontalFocalLengthEnabled || suggestVerticalFocalLengthEnabled
980                 || suggestAspectRatioEnabled;
981     }
982 
983     /**
984      * Indicates whether suggestions for any extrinsic parameter are required
985      * or not.
986      *
987      * @return true if suggestions for any extrinsic parameter are required,
988      * false otherwise.
989      */
990     private boolean hasExtrinsicSuggestions() {
991         return suggestPrincipalPointEnabled || suggestRotationEnabled || suggestCenterEnabled;
992     }
993 }