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