1 /*
2 * Copyright (C) 2015 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.epipolar.estimators;
17
18 import com.irurueta.ar.epipolar.FundamentalMatrix;
19 import com.irurueta.geometry.Point2D;
20 import com.irurueta.geometry.estimators.LockedException;
21 import com.irurueta.geometry.estimators.NotReadyException;
22 import com.irurueta.numerical.robust.PROMedSRobustEstimator;
23 import com.irurueta.numerical.robust.PROMedSRobustEstimatorListener;
24 import com.irurueta.numerical.robust.RobustEstimator;
25 import com.irurueta.numerical.robust.RobustEstimatorException;
26 import com.irurueta.numerical.robust.RobustEstimatorMethod;
27
28 import java.util.ArrayList;
29 import java.util.List;
30
31 /**
32 * Finds the best fundamental matrix for provided collections of matched 2D
33 * points using PROMedS algorithm.
34 */
35 public class PROMedSFundamentalMatrixRobustEstimator extends FundamentalMatrixRobustEstimator {
36
37 /**
38 * Default non-robust method to estimate a fundamental matrix.
39 */
40 public static final FundamentalMatrixEstimatorMethod DEFAULT_PROMEDS_FUNDAMENTAL_MATRIX_ESTIMATOR_METHOD =
41 FundamentalMatrixEstimatorMethod.SEVEN_POINTS_ALGORITHM;
42
43 /**
44 * Default value to be used for stop threshold. Stop threshold can be used
45 * to keep the algorithm iterating in case that best estimated threshold
46 * using median of residuals is not small enough. Once a solution is found
47 * that generates a threshold below this value, the algorithm will stop.
48 * The stop threshold can be used to prevent the LMedS algorithm iterating
49 * too many times in cases where samples have a very similar accuracy.
50 * For instance, in cases where proportion of outliers is very small (close
51 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
52 * iterate for a long time trying to find the best solution when indeed
53 * there is no need to do that if a reasonable threshold has already been
54 * reached.
55 * Because of this behaviour the stop threshold can be set to a value much
56 * lower than the one typically used in RANSAC, and yet the algorithm could
57 * still produce even smaller thresholds in estimated results.
58 */
59 public static final double DEFAULT_STOP_THRESHOLD = 1e-3;
60
61 /**
62 * Minimum allowed stop threshold value.
63 */
64 public static final double MIN_STOP_THRESHOLD = 0.0;
65
66 /**
67 * Threshold to be used to keep the algorithm iterating in case that best
68 * estimated threshold using median of residuals is not small enough. Once
69 * a solution is found that generates a threshold below this value, the
70 * algorithm will stop.
71 * The stop threshold can be used to prevent the LMedS algorithm iterating
72 * too many times in cases where samples have a very similar accuracy.
73 * For instance, in cases where proportion of outliers is very small (close
74 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
75 * iterate for a long time trying to find the best solution when indeed
76 * there is no need to do that if a reasonable threshold has already been
77 * reached.
78 * Because of this behaviour the stop threshold can be set to a value much
79 * lower than the one typically used in RANSAC, and yet the algorithm could
80 * still produce even smaller thresholds in estimated results.
81 */
82 private double stopThreshold;
83
84 /**
85 * Quality scores corresponding to each provided point.
86 * The larger the score value the better the quality of the sample.
87 */
88 private double[] qualityScores;
89
90 /**
91 * Constructor.
92 *
93 * @param fundMatrixEstimatorMethod method for non-robust fundamental matrix
94 * estimator.
95 */
96 public PROMedSFundamentalMatrixRobustEstimator(final FundamentalMatrixEstimatorMethod fundMatrixEstimatorMethod) {
97 super(fundMatrixEstimatorMethod);
98 stopThreshold = DEFAULT_STOP_THRESHOLD;
99 }
100
101 /**
102 * Constructor.
103 *
104 * @param fundMatrixEstimatorMethod method for non-robust fundamental matrix
105 * estimator.
106 * @param listener listener to be notified of events such as when
107 * estimation starts, ends or its progress significantly changes.
108 */
109 public PROMedSFundamentalMatrixRobustEstimator(
110 final FundamentalMatrixEstimatorMethod fundMatrixEstimatorMethod,
111 final FundamentalMatrixRobustEstimatorListener listener) {
112 super(fundMatrixEstimatorMethod, listener);
113 stopThreshold = DEFAULT_STOP_THRESHOLD;
114 }
115
116 /**
117 * Constructor.
118 *
119 * @param fundMatrixEstimatorMethod method for non-robust fundamental matrix
120 * estimator.
121 * @param leftPoints 2D points on left view.
122 * @param rightPoints 2D points on right view.
123 * @throws IllegalArgumentException if provided list of points do not have
124 * the same length or their length is less than 8 points.
125 */
126 public PROMedSFundamentalMatrixRobustEstimator(
127 final FundamentalMatrixEstimatorMethod fundMatrixEstimatorMethod,
128 final List<Point2D> leftPoints, final List<Point2D> rightPoints) {
129 super(fundMatrixEstimatorMethod, leftPoints, rightPoints);
130 if (leftPoints.size() < EightPointsFundamentalMatrixEstimator.MIN_REQUIRED_POINTS) {
131 throw new IllegalArgumentException();
132 }
133 stopThreshold = DEFAULT_STOP_THRESHOLD;
134 }
135
136 /**
137 * Constructor.
138 *
139 * @param fundMatrixEstimatorMethod method for non-robust fundamental matrix
140 * estimator.
141 * @param leftPoints 2D points on left view.
142 * @param rightPoints 2D points on right view.
143 * @param listener listener to be notified of events such as when estimation
144 * starts, ends or its progress significantly changes.
145 * @throws IllegalArgumentException if provided list of points do not have
146 * the same length or their length is less than 8 points.
147 */
148 public PROMedSFundamentalMatrixRobustEstimator(
149 final FundamentalMatrixEstimatorMethod fundMatrixEstimatorMethod,
150 final List<Point2D> leftPoints, final List<Point2D> rightPoints,
151 final FundamentalMatrixRobustEstimatorListener listener) {
152 super(fundMatrixEstimatorMethod, leftPoints, rightPoints, listener);
153 if (leftPoints.size() < EightPointsFundamentalMatrixEstimator.MIN_REQUIRED_POINTS) {
154 throw new IllegalArgumentException();
155 }
156 stopThreshold = DEFAULT_STOP_THRESHOLD;
157 }
158
159 /**
160 * Constructor.
161 *
162 * @param fundMatrixEstimatorMethod method for non-robust fundamental matrix
163 * estimator.
164 * @param qualityScores quality scores corresponding to each provided pair
165 * of matched points.
166 * @throws IllegalArgumentException if provided quality scores length is
167 * smaller than required size (i.e. 7 matched pair of points).
168 */
169 public PROMedSFundamentalMatrixRobustEstimator(
170 final FundamentalMatrixEstimatorMethod fundMatrixEstimatorMethod, final double[] qualityScores) {
171 this(fundMatrixEstimatorMethod);
172 internalSetQualityScores(qualityScores);
173 }
174
175 /**
176 * Constructor.
177 *
178 * @param fundMatrixEstimatorMethod method for non-robust fundamental matrix
179 * estimator.
180 * @param qualityScores quality scores corresponding to each provided pair
181 * of matched points.
182 * @param listener listener to be notified of events such as when
183 * estimation starts, ends or its progress significantly changes.
184 * @throws IllegalArgumentException if provided quality scores length is
185 * smaller than required size (i.e. 7 matched pair of points).
186 */
187 public PROMedSFundamentalMatrixRobustEstimator(
188 final FundamentalMatrixEstimatorMethod fundMatrixEstimatorMethod, final double[] qualityScores,
189 final FundamentalMatrixRobustEstimatorListener listener) {
190 this(fundMatrixEstimatorMethod, listener);
191 internalSetQualityScores(qualityScores);
192 }
193
194 /**
195 * Constructor.
196 *
197 * @param fundMatrixEstimatorMethod method for non-robust fundamental matrix
198 * estimator.
199 * @param qualityScores quality scores corresponding to each provided pair
200 * of matched points.
201 * @param leftPoints 2D points on left view.
202 * @param rightPoints 2D points on right view.
203 * @throws IllegalArgumentException if provided list of points or quality
204 * scores do not have the same length or their length is less
205 * than 7 points.
206 */
207 public PROMedSFundamentalMatrixRobustEstimator(
208 final FundamentalMatrixEstimatorMethod fundMatrixEstimatorMethod, final double[] qualityScores,
209 final List<Point2D> leftPoints, final List<Point2D> rightPoints) {
210 this(fundMatrixEstimatorMethod, leftPoints, rightPoints);
211 internalSetQualityScores(qualityScores);
212 }
213
214 /**
215 * Constructor.
216 *
217 * @param fundMatrixEstimatorMethod method for non-robust fundamental matrix
218 * estimator.
219 * @param qualityScores quality scores corresponding to each provided pair
220 * of matched points.
221 * @param leftPoints 2D points on left view.
222 * @param rightPoints 2D points on right view.
223 * @param listener listener to be notified of events such as when estimation
224 * starts, ends or its progress significantly changes.
225 * @throws IllegalArgumentException if provided list of points or quality
226 * scores do not have the same length or their length is less
227 * than 7 points.
228 */
229 public PROMedSFundamentalMatrixRobustEstimator(
230 final FundamentalMatrixEstimatorMethod fundMatrixEstimatorMethod, final double[] qualityScores,
231 final List<Point2D> leftPoints, final List<Point2D> rightPoints,
232 final FundamentalMatrixRobustEstimatorListener listener) {
233 this(fundMatrixEstimatorMethod, leftPoints, rightPoints, listener);
234 internalSetQualityScores(qualityScores);
235 }
236
237 /**
238 * Constructor.
239 */
240 public PROMedSFundamentalMatrixRobustEstimator() {
241 this(DEFAULT_PROMEDS_FUNDAMENTAL_MATRIX_ESTIMATOR_METHOD);
242 }
243
244 /**
245 * Constructor.
246 *
247 * @param listener listener to be notified of events such as when
248 * estimation starts, ends or its progress significantly changes.
249 */
250 public PROMedSFundamentalMatrixRobustEstimator(final FundamentalMatrixRobustEstimatorListener listener) {
251 this(DEFAULT_PROMEDS_FUNDAMENTAL_MATRIX_ESTIMATOR_METHOD, listener);
252 }
253
254 /**
255 * Constructor.
256 *
257 * @param leftPoints 2D points on left view.
258 * @param rightPoints 2D points on right view.
259 * @throws IllegalArgumentException if provided list of points do not have
260 * the same length or their length is less than 8 points.
261 */
262 public PROMedSFundamentalMatrixRobustEstimator(final List<Point2D> leftPoints, final List<Point2D> rightPoints) {
263 this(DEFAULT_PROMEDS_FUNDAMENTAL_MATRIX_ESTIMATOR_METHOD, leftPoints, rightPoints);
264 }
265
266 /**
267 * Constructor.
268 *
269 * @param leftPoints 2D points on left view.
270 * @param rightPoints 2D points on right view.
271 * @param listener listener to be notified of events such as when estimation
272 * starts, ends or its progress significantly changes.
273 * @throws IllegalArgumentException if provided list of points do not have
274 * the same length or their length is less than 8 points.
275 */
276 public PROMedSFundamentalMatrixRobustEstimator(final List<Point2D> leftPoints, final List<Point2D> rightPoints,
277 final FundamentalMatrixRobustEstimatorListener listener) {
278 this(DEFAULT_PROMEDS_FUNDAMENTAL_MATRIX_ESTIMATOR_METHOD, leftPoints, rightPoints, listener);
279 }
280
281 /**
282 * Constructor.
283 *
284 * @param qualityScores quality scores corresponding to each provided pair
285 * of matched points.
286 * @throws IllegalArgumentException if provided quality scores length is
287 * smaller than required size (i.e. 7 matched pair of points).
288 */
289 public PROMedSFundamentalMatrixRobustEstimator(final double[] qualityScores) {
290 this(DEFAULT_PROMEDS_FUNDAMENTAL_MATRIX_ESTIMATOR_METHOD, qualityScores);
291 }
292
293 /**
294 * Constructor.
295 *
296 * @param qualityScores quality scores corresponding to each provided pair
297 * of matched points.
298 * @param listener listener to be notified of events such as when
299 * estimation starts, ends or its progress significantly changes.
300 * @throws IllegalArgumentException if provided quality scores length is
301 * smaller than required size (i.e. 7 matched pair of points).
302 */
303 public PROMedSFundamentalMatrixRobustEstimator(
304 final double[] qualityScores, final FundamentalMatrixRobustEstimatorListener listener) {
305 this(DEFAULT_PROMEDS_FUNDAMENTAL_MATRIX_ESTIMATOR_METHOD, qualityScores, listener);
306 }
307
308 /**
309 * Constructor.
310 *
311 * @param qualityScores quality scores corresponding to each provided pair
312 * of matched points.
313 * @param leftPoints 2D points on left view.
314 * @param rightPoints 2D points on right view.
315 * @throws IllegalArgumentException if provided list of points or quality
316 * scores do not have the same length or their length is less
317 * than 7 points.
318 */
319 public PROMedSFundamentalMatrixRobustEstimator(
320 final double[] qualityScores, final List<Point2D> leftPoints, final List<Point2D> rightPoints) {
321 this(DEFAULT_PROMEDS_FUNDAMENTAL_MATRIX_ESTIMATOR_METHOD, qualityScores, leftPoints, rightPoints);
322 }
323
324 /**
325 * Constructor.
326 *
327 * @param qualityScores quality scores corresponding to each provided pair
328 * of matched points.
329 * @param leftPoints 2D points on left view.
330 * @param rightPoints 2D points on right view.
331 * @param listener listener to be notified of events such as when estimation
332 * starts, ends or its progress significantly changes.
333 * @throws IllegalArgumentException if provided list of points or quality
334 * scores do not have the same length or their length is less
335 * than 7 points.
336 */
337 public PROMedSFundamentalMatrixRobustEstimator(
338 final double[] qualityScores, final List<Point2D> leftPoints, final List<Point2D> rightPoints,
339 final FundamentalMatrixRobustEstimatorListener listener) {
340 this(DEFAULT_PROMEDS_FUNDAMENTAL_MATRIX_ESTIMATOR_METHOD, qualityScores, leftPoints, rightPoints, listener);
341 }
342
343 /**
344 * Sets matched 2D points on both left and right views.
345 *
346 * @param leftPoints matched 2D points on left view.
347 * @param rightPoints matched 2D points on right view.
348 * @throws LockedException if this fundamental matrix estimator is locked.
349 * @throws IllegalArgumentException if provided matched points on left and
350 * right views do not have the same length or if their length is
351 * less than 8 points.
352 */
353 @Override
354 public void setPoints(final List<Point2D> leftPoints, final List<Point2D> rightPoints) throws LockedException {
355 if (isLocked()) {
356 throw new LockedException();
357 }
358 if (leftPoints.size() < EightPointsFundamentalMatrixEstimator.MIN_REQUIRED_POINTS) {
359 throw new IllegalArgumentException();
360 }
361 super.setPoints(leftPoints, rightPoints);
362 }
363
364
365 /**
366 * Returns threshold to be used to keep the algorithm iterating in case that
367 * best estimated threshold using median of residuals is not small enough.
368 * Once a solution is found that generates a threshold below this value, the
369 * algorithm will stop.
370 * The stop threshold can be used to prevent the LMedS algorithm iterating
371 * too many times in cases where samples have a very similar accuracy.
372 * For instance, in cases where proportion of outliers is very small (close
373 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
374 * iterate for a long time trying to find the best solution when indeed
375 * there is no need to do that if a reasonable threshold has already been
376 * reached.
377 * Because of this behaviour the stop threshold can be set to a value much
378 * lower than the one typically used in RANSAC, and yet the algorithm could
379 * still produce even smaller thresholds in estimated results.
380 *
381 * @return stop threshold to stop the algorithm prematurely when a certain
382 * accuracy has been reached.
383 */
384 public double getStopThreshold() {
385 return stopThreshold;
386 }
387
388 /**
389 * Sets threshold to be used to keep the algorithm iterating in case that
390 * best estimated threshold using median of residuals is not small enough.
391 * Once a solution is found that generates a threshold below this value, the
392 * algorithm will stop.
393 * The stop threshold can be used to prevent the LMedS algorithm iterating
394 * too many times in cases where samples have a very similar accuracy.
395 * For instance, in cases where proportion of outliers is very small (close
396 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
397 * iterate for a long time trying to find the best solution when indeed
398 * there is no need to do that if a reasonable threshold has already been
399 * reached.
400 * Because of this behaviour the stop threshold can be set to a value much
401 * lower than the one typically used in RANSAC, and yet the algorithm could
402 * still produce even smaller thresholds in estimated results.
403 *
404 * @param stopThreshold stop threshold to stop the algorithm prematurely
405 * when a certain accuracy has been reached.
406 * @throws IllegalArgumentException if provided value is zero or negative.
407 * @throws LockedException if robust estimator is locked because an
408 * estimation is already in progress.
409 */
410 public void setStopThreshold(final double stopThreshold) throws LockedException {
411 if (isLocked()) {
412 throw new LockedException();
413 }
414 if (stopThreshold <= MIN_STOP_THRESHOLD) {
415 throw new IllegalArgumentException();
416 }
417
418 this.stopThreshold = stopThreshold;
419 }
420
421 /**
422 * Returns quality scores corresponding to each provided pair of points.
423 * The larger the score value the better the quality of the sampled matched
424 * pair of points.
425 *
426 * @return quality scores corresponding to each pair of points.
427 */
428 @Override
429 public double[] getQualityScores() {
430 return qualityScores;
431 }
432
433 /**
434 * Sets quality scores corresponding to each provided pair of points.
435 * The larger the score value the better the quality of the sampled matched
436 * pair of points.
437 *
438 * @param qualityScores quality scores corresponding to each pair of points.
439 * @throws LockedException if robust estimator is locked because an
440 * estimation is already in progress.
441 * @throws IllegalArgumentException if provided quality scores length is
442 * smaller than MINIMUM_SIZE (i.e. 3 samples).
443 */
444 @Override
445 public void setQualityScores(final double[] qualityScores) throws LockedException {
446 if (isLocked()) {
447 throw new LockedException();
448 }
449 internalSetQualityScores(qualityScores);
450 }
451
452 /**
453 * Returns value indicating whether required data has been provided so that
454 * fundamental matrix estimation can start.
455 * This is true when input data (i.e. 7 pairs of matched 2D points and their
456 * quality scores) are provided.
457 * If true, estimator is ready to compute a fundamental matrix, otherwise
458 * more data needs to be provided.
459 *
460 * @return true if estimator is ready, false otherwise.
461 */
462 @Override
463 public boolean isReady() {
464 return super.isReady() && qualityScores != null && qualityScores.length == leftPoints.size();
465 }
466
467 /**
468 * Estimates a radial distortion using a robust estimator and
469 * the best set of matched 2D points found using the robust estimator.
470 *
471 * @return a radial distortion.
472 * @throws LockedException if robust estimator is locked because an
473 * estimation is already in progress.
474 * @throws NotReadyException if provided input data is not enough to start
475 * the estimation.
476 * @throws RobustEstimatorException if estimation fails for any reason
477 * (i.e. numerical instability, no solution available, etc).
478 */
479 @SuppressWarnings("DuplicatedCode")
480 @Override
481 public FundamentalMatrix estimate() throws LockedException, NotReadyException, RobustEstimatorException {
482 if (isLocked()) {
483 throw new LockedException();
484 }
485 if (!isReady()) {
486 throw new NotReadyException();
487 }
488
489 final var innerEstimator = new PROMedSRobustEstimator<FundamentalMatrix>(
490 new PROMedSRobustEstimatorListener<>() {
491
492 // subset of left points
493 private final List<Point2D> subsetLeftPoints = new ArrayList<>();
494
495 // subset of right points
496 private final List<Point2D> subsetRightPoints = new ArrayList<>();
497
498 @Override
499 public double getThreshold() {
500 return stopThreshold;
501 }
502
503 @Override
504 public int getTotalSamples() {
505 return leftPoints.size();
506 }
507
508 @Override
509 public int getSubsetSize() {
510 return getMinRequiredPoints();
511 }
512
513 @Override
514 public void estimatePreliminarSolutions(
515 final int[] samplesIndices, final List<FundamentalMatrix> solutions) {
516
517 subsetLeftPoints.clear();
518 subsetRightPoints.clear();
519 for (final var samplesIndex : samplesIndices) {
520 subsetLeftPoints.add(leftPoints.get(samplesIndex));
521 subsetRightPoints.add(rightPoints.get(samplesIndex));
522 }
523
524 nonRobustEstimate(solutions, subsetLeftPoints, subsetRightPoints);
525 }
526
527 @Override
528 public double computeResidual(final FundamentalMatrix currentEstimation, final int i) {
529 final var leftPoint = leftPoints.get(i);
530 final var rightPoint = rightPoints.get(i);
531 return residual(currentEstimation, leftPoint, rightPoint);
532 }
533
534 @Override
535 public boolean isReady() {
536 return PROMedSFundamentalMatrixRobustEstimator.this.isReady();
537 }
538
539 @Override
540 public void onEstimateStart(final RobustEstimator<FundamentalMatrix> estimator) {
541 if (listener != null) {
542 listener.onEstimateStart(PROMedSFundamentalMatrixRobustEstimator.this);
543 }
544 }
545
546 @Override
547 public void onEstimateEnd(final RobustEstimator<FundamentalMatrix> estimator) {
548 if (listener != null) {
549 listener.onEstimateEnd(PROMedSFundamentalMatrixRobustEstimator.this);
550 }
551 }
552
553 @Override
554 public void onEstimateNextIteration(
555 final RobustEstimator<FundamentalMatrix> estimator, final int iteration) {
556 if (listener != null) {
557 listener.onEstimateNextIteration(
558 PROMedSFundamentalMatrixRobustEstimator.this, iteration);
559 }
560 }
561
562 @Override
563 public void onEstimateProgressChange(
564 final RobustEstimator<FundamentalMatrix> estimator, final float progress) {
565 if (listener != null) {
566 listener.onEstimateProgressChange(
567 PROMedSFundamentalMatrixRobustEstimator.this, progress);
568 }
569 }
570
571 @Override
572 public double[] getQualityScores() {
573 return qualityScores;
574 }
575 });
576
577 try {
578 locked = true;
579 inliersData = null;
580 innerEstimator.setConfidence(confidence);
581 innerEstimator.setMaxIterations(maxIterations);
582 innerEstimator.setProgressDelta(progressDelta);
583 final var result = innerEstimator.estimate();
584 inliersData = innerEstimator.getInliersData();
585 return attemptRefine(result);
586 } catch (final com.irurueta.numerical.LockedException e) {
587 throw new LockedException(e);
588 } catch (final com.irurueta.numerical.NotReadyException e) {
589 throw new NotReadyException(e);
590 } finally {
591 locked = false;
592 }
593 }
594
595 /**
596 * Returns method being used for robust estimation.
597 *
598 * @return method being used for robust estimation.
599 */
600 @Override
601 public RobustEstimatorMethod getMethod() {
602 return RobustEstimatorMethod.PROMEDS;
603 }
604
605 /**
606 * Gets standard deviation used for Levenberg-Marquardt fitting during
607 * refinement.
608 * Returned value gives an indication of how much variance each residual
609 * has.
610 * Typically, this value is related to the threshold used on each robust
611 * estimation, since residuals of found inliers are within the range of
612 * such threshold.
613 *
614 * @return standard deviation used for refinement.
615 */
616 @Override
617 protected double getRefinementStandardDeviation() {
618 final var inliersData = (PROMedSRobustEstimator.PROMedSInliersData) getInliersData();
619 return inliersData.getEstimatedThreshold();
620 }
621
622 /**
623 * Sets quality scores corresponding to each provided pair of matched
624 * points.
625 * This method is used internally and does not check whether instance is
626 * locked or not.
627 *
628 * @param qualityScores quality scores to be set.
629 * @throws IllegalArgumentException if provided quality scores length is
630 * smaller than 8 points.
631 */
632 private void internalSetQualityScores(final double[] qualityScores) {
633 if (qualityScores.length < getMinRequiredPoints()) {
634 throw new IllegalArgumentException();
635 }
636
637 this.qualityScores = qualityScores;
638 }
639 }