CR2RE Pipeline Reference Manual 1.6.7
irplib_distortion.c
1/* $Id: irplib_distortion.c,v 1.52 2013-01-29 08:43:33 jtaylor Exp $
2 *
3 * This file is part of the irplib package
4 * Copyright (C) 2002,2003 European Southern Observatory
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
19 */
20
21/*
22 * $Author: jtaylor $
23 * $Date: 2013-01-29 08:43:33 $
24 * $Revision: 1.52 $
25 * $Name: not supported by cvs2svn $
26 */
27
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31
32/*-----------------------------------------------------------------------------
33 Includes
34 -----------------------------------------------------------------------------*/
35
36#include "irplib_distortion.h"
37
38#include "irplib_flat.h"
39#include "irplib_utils.h"
40#include "irplib_polynomial.h"
41
42#include <math.h>
43#include <float.h>
44
45/*-----------------------------------------------------------------------------
46 Define
47 -----------------------------------------------------------------------------*/
48
49#define IRPLIB_MAX(A,B) ((A) > (B) ? (A) : (B))
50#define IRPLIB_MIN(A,B) ((A) < (B) ? (A) : (B))
51
52#define ARC_MINGOODPIX 100
53#define ARC_MINARCLENFACT 2.0
54#define ARC_MINNBARCS 4
55#define ARC_RANGE_FACT 3.0
56#define ARC_WINDOWSIZE 32
57
58#define TRESH_MEDIAN_MIN 0.0
59#define TRESH_SIGMA_MAX 200.0
60
61/*----------------------------------------------------------------------------*/
65/*----------------------------------------------------------------------------*/
66
67/*-----------------------------------------------------------------------------
68 Functions prototypes
69 -----------------------------------------------------------------------------*/
70
71static cpl_apertures * irplib_distortion_detect_arcs(cpl_image *,
72 cpl_image **, int, int, double, int, int, int, int);
73static cpl_error_code irplib_distortion_fill_border(cpl_image *, int, int,
74 int, int, double);
75static int irplib_distortion_threshold1d(cpl_image *, double, cpl_image *,
76 double);
77static cpl_error_code irplib_distortion_purge_arcs(cpl_apertures **, cpl_image *,
78 const cpl_image *, int, int,
79 double);
80static cpl_error_code irplib_distortion_fill_arc_positions(cpl_bivector *,
81 cpl_vector *,
82 const cpl_image *,
83 const cpl_image *,
84 const cpl_apertures *);
85
86static double irplib_distortion_get_row_centroid(const cpl_image *,
87 const cpl_image *, int, int);
88
89static int irplib_distortion_sub_hor_lowpass(cpl_image *, int);
90static cpl_image * irplib_distortion_remove_ramp(const cpl_image *);
91
92static cpl_error_code irplib_image_filter_background_line(cpl_image *,
93 const cpl_image *, int, cpl_boolean) ;
94
95static cpl_error_code irplib_polynomial_fit_2d(cpl_polynomial *,
96 const cpl_bivector *,
97 const cpl_vector *, int,
98 double, double *);
99
100static cpl_matrix * irplib_matrix_product_normal_create(const cpl_matrix *);
101
102/*-----------------------------------------------------------------------------
103 Functions code
104 -----------------------------------------------------------------------------*/
105
108/*----------------------------------------------------------------------------*/
137/*----------------------------------------------------------------------------*/
138cpl_polynomial * irplib_distortion_estimate(
139 const cpl_image * org,
140 int xmin,
141 int ymin,
142 int xmax,
143 int ymax,
144 int auto_ramp_sub,
145 int arc_sat,
146 int max_arc_width,
147 double kappa,
148 int degree,
149 cpl_apertures ** arcs)
150{
151 cpl_image * local_im;
152 cpl_image * filtered;
153 cpl_image * label_image;
154 double rightmost, leftmost;
155 cpl_bivector * grid;
156 cpl_vector * values_to_fit;
157 int n_arcs;
158 cpl_polynomial * poly2d;
159 double mse = 0.0;
160 const int nx = cpl_image_get_size_x(org);
161 const int ny = cpl_image_get_size_y(org);
162 const int min_arc_range = (int)(nx / ARC_RANGE_FACT);
163 int i;
164
165 /* Check entries */
166 cpl_ensure(org != NULL, CPL_ERROR_NULL_INPUT, NULL);
167 cpl_ensure(kappa >= 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
168 cpl_ensure(max_arc_width > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
169
170 /* The background may vary strongly along the vertical line. */
171 /* Detect and rm background with a 1+2*max_arc_width x 1 median filter */
172
173 filtered = cpl_image_new(nx, ny, cpl_image_get_type(org));
174
175 irplib_image_filter_background_line(filtered, org, max_arc_width, CPL_TRUE);
176
177 if (auto_ramp_sub) {
178 local_im = irplib_distortion_remove_ramp(filtered);
179 cpl_image_delete(filtered);
180 } else {
181 local_im = filtered;
182 }
183
184 cpl_error_ensure(local_im != NULL, cpl_error_get_code(),
185 return(NULL), "Cannot clean the image");
186
187 /* Detect the arcs in the input image */
188 *arcs = irplib_distortion_detect_arcs(local_im, &label_image, arc_sat,
189 max_arc_width, kappa, xmin, ymin,
190 xmax, ymax);
191 if (*arcs == NULL) {
192 cpl_image_delete(local_im);
193 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
194 "Cannot detect the arcs");
195 return NULL;
196 }
197 n_arcs = cpl_apertures_get_size(*arcs);
198 cpl_msg_info(cpl_func, "%d detected arcs", n_arcs);
199
200 /* Check that the arcs are not concentrated in the same zone */
201 rightmost = leftmost = cpl_apertures_get_pos_x(*arcs, 1);
202 for (i=1; i<n_arcs; i++) {
203 if (cpl_apertures_get_pos_x(*arcs, i+1) < leftmost)
204 leftmost = cpl_apertures_get_pos_x(*arcs, i+1);
205 if (cpl_apertures_get_pos_x(*arcs, i+1) > rightmost)
206 rightmost = cpl_apertures_get_pos_x(*arcs, i+1);
207 }
208 if ((int)(rightmost-leftmost) < min_arc_range) {
209#if defined CPL_HAVE_VA_ARGS && CPL_HAVE_VA_ARGS != 0
210 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
211 "too narrow range (%g-%g)<%d",
212 rightmost, leftmost, min_arc_range);
213#else
214 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
215 "too narrow range");
216#endif
217 cpl_apertures_delete(*arcs);
218 cpl_image_delete(local_im);
219 cpl_image_delete(label_image);
220 *arcs = NULL;
221 return NULL;
222 }
223
224 /* Create a 2-D deformation grid with detected arcs */
225 cpl_msg_info(cpl_func, "Create deformation grid");
226 grid = cpl_bivector_new(n_arcs * ny);
227 values_to_fit = cpl_vector_new(n_arcs * ny);
228
229 if (irplib_distortion_fill_arc_positions(grid, values_to_fit, local_im,
230 label_image, *arcs)){
231 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
232 "cannot get arcs positions");
233 cpl_apertures_delete(*arcs);
234 cpl_image_delete(local_im);
235 cpl_image_delete(label_image);
236 *arcs = NULL;
237 return NULL;
238 }
239 cpl_image_delete(label_image);
240 cpl_image_delete(local_im);
241
242 /* Apply the fitting */
243 poly2d = cpl_polynomial_new(2);
244 if (irplib_polynomial_fit_2d(poly2d, grid, values_to_fit, degree,
245 0.5*(ny+1), &mse)) {
246 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
247 "cannot apply the 2d fit");
248 cpl_bivector_delete(grid);
249 cpl_vector_delete(values_to_fit);
250 cpl_apertures_delete(*arcs);
251 *arcs = NULL;
252 return NULL;
253 }
254
255 cpl_msg_info(cpl_func,
256 "Fitted a %d. degree 2D-polynomial to %"CPL_SIZE_FORMAT" points "
257 "with mean-square error: %g", degree,
258 cpl_vector_get_size(values_to_fit), mse);
259
260 /* Free and return */
261 cpl_bivector_delete(grid);
262 cpl_vector_delete(values_to_fit);
263 return poly2d;
264}
265
268/*----------------------------------------------------------------------------*/
284/*----------------------------------------------------------------------------*/
285static cpl_apertures * irplib_distortion_detect_arcs(
286 cpl_image * im,
287 cpl_image ** label_im,
288 int arc_sat,
289 int max_arc_width,
290 double kappa,
291 int xmin,
292 int ymin,
293 int xmax,
294 int ymax)
295{
296 const int ny = cpl_image_get_size_y(im);
297 /* Set min_arclen */
298 const int min_arclen = (int)(ny / ARC_MINARCLENFACT);
299 cpl_image * filt_im;
300 cpl_mask * filter;
301 cpl_image * collapsed;
302 cpl_mask * bin_im;
303 double threshold, fillval, median_val, sigma;
304 cpl_apertures * det;
305 cpl_size nobj;
306 int ngoodpix;
307
308 /* Default values for output parameters */
309 *label_im = NULL;
310
311 /* Clear zones to be ignored (to avoid false detections) */
312 median_val = cpl_image_get_median_dev(im, &sigma);
313 fillval = median_val-sigma/2.0;
314 if (irplib_distortion_fill_border(im, xmin, ymin, xmax, ymax, fillval)) {
315 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
316 "cannot fill bad zones");
317 return NULL;
318 }
319
320 /* Subtract a low-pass */
321 filt_im = cpl_image_duplicate(im);
322 if (irplib_distortion_sub_hor_lowpass(filt_im, ARC_WINDOWSIZE) == -1) {
323 cpl_image_delete(filt_im);
324 return NULL;
325 }
326
327 /* Get relevant stats for thresholding */
328 median_val = cpl_image_get_median_dev(filt_im, &sigma);
329
330 /* Correct median_val and sigma if necessary */
331 if (median_val < TRESH_MEDIAN_MIN) median_val = TRESH_MEDIAN_MIN;
332 if (sigma > TRESH_SIGMA_MAX) sigma = TRESH_SIGMA_MAX;
333
334 /* Set the threshold */
335 threshold = median_val + sigma * kappa;
336
337 /* Collapse the image */
338 collapsed = cpl_image_collapse_median_create(filt_im, 0, 0, 0);
339
340 /* Threshold to keep only the arcs - use of the collapsed image */
341 if (irplib_distortion_threshold1d(filt_im, median_val, collapsed, 0.0)==-1) {
342 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
343 "cannot threshold the filtered image");
344 cpl_image_delete(filt_im);
345 cpl_image_delete(collapsed);
346 return NULL;
347 }
348 cpl_image_delete(collapsed);
349
350 /* Binarize the image */
351 bin_im = cpl_mask_threshold_image_create(filt_im, threshold,
352 DBL_MAX);
353 cpl_image_delete(filt_im);
354 if (bin_im == NULL) {
355 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
356 "cannot binarise the image");
357 return NULL;
358 }
359
360 /* Test if there are enough good pixels */
361 ngoodpix = cpl_mask_count(bin_im);
362 if (ngoodpix < ARC_MINGOODPIX) {
363#if defined CPL_HAVE_VA_ARGS && CPL_HAVE_VA_ARGS != 0
364 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
365 "Too few (%d) white pixels", ngoodpix);
366#else
367 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
368 "Too few white pixels");
369#endif
370 cpl_mask_delete(bin_im);
371 return NULL;
372 }
373
374 /* Apply a morphological opening to clean the isolated pixels */
375 filter = cpl_mask_new(3, 3);
376 cpl_mask_not(filter);
377 cpl_mask_filter(bin_im, bin_im, filter, CPL_FILTER_OPENING,
378 CPL_BORDER_ZERO);
379 cpl_mask_delete(filter);
380
381 /* Labelize pixel map to a label image */
382 *label_im = cpl_image_labelise_mask_create(bin_im, &nobj);
383 cpl_mask_delete(bin_im);
384
385 /* Compute statistics on objects */
386 if ((det = cpl_apertures_new_from_image(im, *label_im)) == NULL) {
387 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
388 "Cannot compute arcs stats");
389 cpl_image_delete(*label_im);
390 *label_im = NULL;
391 return NULL;
392 }
393
394 /* Purge non-relevant arcs */
395 if (irplib_distortion_purge_arcs(&det, *label_im, im, min_arclen,
396 max_arc_width, arc_sat)) {
397 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
398 "Cannot purge the arcs");
399 cpl_image_delete(*label_im);
400 *label_im = NULL;
401 cpl_apertures_delete(det);
402 return NULL;
403 }
404 if (cpl_apertures_get_size(det) < ARC_MINNBARCS) {
405#if defined CPL_HAVE_VA_ARGS && CPL_HAVE_VA_ARGS != 0
406 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
407 "Not enough valid arcs (%"CPL_SIZE_FORMAT" < %d)",
408 cpl_apertures_get_size(det), ARC_MINNBARCS);
409#else
410 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
411 "Not enough valid arcs, min="
412 CPL_STRINGIFY(ARC_MINNBARCS));
413#endif
414 cpl_image_delete(*label_im);
415 *label_im = NULL;
416 cpl_apertures_delete(det);
417 return NULL;
418 }
419
420 /* Return */
421 return det;
422}
423
424/*----------------------------------------------------------------------------*/
434/*----------------------------------------------------------------------------*/
435static cpl_error_code irplib_distortion_fill_border(cpl_image * self,
436 int xmin,
437 int ymin,
438 int xmax,
439 int ymax,
440 double fillval)
441{
442 const int nx = cpl_image_get_size_x(self);
443 const int ny = cpl_image_get_size_y(self);
444 float * pfi = cpl_image_get_data_float(self);
445 const float fvalue = (float)fillval;
446 int i, j;
447
448
449 cpl_ensure_code(pfi != NULL, cpl_error_get_code());
450
451 /* Ensure validity of pixel buffer access */
452 xmin = IRPLIB_MIN(xmin, nx+1);
453 ymax = IRPLIB_MIN(ymax, ny);
454
455 /* - and avoid double access */
456 xmax = IRPLIB_MAX(xmax, xmin - 1);
457 ymin = IRPLIB_MIN(ymin, ymax + 1);
458
459 /* Fill the zone */
460
461 for (j = 0; j < ymin-1; j++) {
462 for (i = 0; i < nx; i++) {
463 pfi[i+j*nx] = fvalue;
464 }
465 }
466 /* assert( j == IRPLIB_MAX(0, ymin-1) ); */
467
468 for (; j < ymax; j++) {
469 for (i = 0; i < xmin-1; i++) {
470 pfi[i+j*nx] = fvalue;
471 }
472 for (i = xmax; i < nx; i++) {
473 pfi[i+j*nx] = fvalue;
474 }
475 }
476 /* assert( j == IRPLIB_MAX(0, ymax) ); */
477
478 for (; j < ny; j++) {
479 for (i = 0; i < nx; i++) {
480 pfi[i+j*nx] = fvalue;
481 }
482 }
483
484 return CPL_ERROR_NONE;
485}
486
487static int irplib_distortion_threshold1d(
488 cpl_image * im,
489 double threshold,
490 cpl_image * im1d,
491 double newval)
492{
493 float * pim;
494 float * pim1d;
495 int nx, ny;
496 int i, j;
497
498 /* Check entries */
499 if (im == NULL) return -1;
500 if (im1d == NULL) return -1;
501 if (cpl_image_get_type(im) != CPL_TYPE_FLOAT) return -1;
502 if (cpl_image_get_type(im1d) != CPL_TYPE_FLOAT) return -1;
503
504 /* Get access to the im / im1d data */
505 pim = cpl_image_get_data_float(im);
506 pim1d = cpl_image_get_data_float(im1d);
507 nx = cpl_image_get_size_x(im);
508 ny = cpl_image_get_size_y(im);
509
510 /* Apply the thresholding */
511 for (i=0; i<nx; i++)
512 if (pim1d[i] < threshold) {
513 for (j=0; j<ny; j++) pim[i+j*nx] = (float)newval;
514 }
515
516 /* Return */
517 return 0;
518}
519
520static int irplib_distortion_sub_hor_lowpass(
521 cpl_image * im,
522 int filt_size)
523{
524 cpl_vector * linehi;
525 cpl_vector * linelo;
526 cpl_vector * avglinehi;
527 cpl_vector * avglinelo;
528 double * pavglinehi;
529 float * pim;
530 int lopos, hipos, nx, ny;
531 int i, j;
532
533 /* Test entries */
534 if (im == NULL) return -1;
535 if (filt_size <= 0) return -1;
536
537 /* Initialise */
538 nx = cpl_image_get_size_x(im);
539 ny = cpl_image_get_size_y(im);
540 lopos = (int)(ny/4);
541 hipos = (int)(3*ny/4);
542
543 /* Get the vectors out of the image */
544 if ((linehi = cpl_vector_new_from_image_row(im, hipos)) == NULL) {
545 return -1;
546 }
547 if ((linelo = cpl_vector_new_from_image_row(im, lopos)) == NULL) {
548 cpl_vector_delete(linehi);
549 return -1;
550 }
551
552 /* Filter the vectors */
553 if ((avglinehi = cpl_vector_filter_median_create(linehi,
554 filt_size)) == NULL) {
555 cpl_vector_delete(linehi);
556 cpl_vector_delete(linelo);
557 return -1;
558 }
559 cpl_vector_delete(linehi);
560
561 if ((avglinelo = cpl_vector_filter_median_create(linelo,
562 filt_size)) == NULL) {
563 cpl_vector_delete(linelo);
564 cpl_vector_delete(avglinehi);
565 return -1;
566 }
567 cpl_vector_delete(linelo);
568
569 /* Average the filtered vectors to get the low freq signal */
570 cpl_vector_add(avglinehi, avglinelo);
571 cpl_vector_delete(avglinelo);
572 cpl_vector_divide_scalar(avglinehi, 2.0);
573
574 /* Subtract the low frequency signal */
575 pavglinehi = cpl_vector_get_data(avglinehi);
576 pim = cpl_image_get_data_float(im);
577 for (i=0; i<nx; i++) {
578 for (j=0; j<ny; j++) {
579 pim[i+j*nx] -= pavglinehi[i];
580 }
581 }
582 cpl_vector_delete(avglinehi);
583
584 return 0;
585}
586
587/*----------------------------------------------------------------------------*/
598/*----------------------------------------------------------------------------*/
599static
600cpl_error_code irplib_distortion_purge_arcs(cpl_apertures ** self,
601 cpl_image * lab_im,
602 const cpl_image * arc_im,
603 int min_arclen,
604 int max_arcwidth,
605 double arc_sat)
606{
607 const double ycenter = 0.5 * (1 + cpl_image_get_size_y(arc_im));
608 int narcs;
609 int nkeep = 0;
610 int ifirst = 1;
611 int * relabel;
612 int i;
613
614 /* Check entries */
615 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
616
617 /* Get number of arcs */
618 narcs = cpl_apertures_get_size(*self);
619
620 cpl_ensure_code(narcs > 0, CPL_ERROR_DATA_NOT_FOUND);
621 cpl_ensure_code(cpl_image_get_type(lab_im) == CPL_TYPE_INT,
622 CPL_ERROR_ILLEGAL_INPUT);
623
624 /* Allocate relabel array with default relabelling to zero */
625 relabel = cpl_calloc(narcs, sizeof(int));
626
627 /* Loop on the different arcs candidates */
628 for (i = 0; i < narcs; i++) {
629 /* Test if the current object is a valid arc */
630 const int arclen = 1
631 + cpl_apertures_get_top(*self, i+1)
632 - cpl_apertures_get_bottom(*self, i+1);
633
634 if (cpl_apertures_get_top(*self, i+1) < ycenter) continue;
635 if (cpl_apertures_get_bottom(*self, i+1) > ycenter) continue;
636
637 if (arclen > min_arclen) {
638 const int arcwidth = 1
639 + cpl_apertures_get_right(*self, i+1)
640 - cpl_apertures_get_left(*self, i+1);
641 if (arcwidth < max_arcwidth) {
642 const int edge = cpl_apertures_get_left_y(*self, i+1);
643 if (edge > 0) {
644 const double mean = cpl_apertures_get_mean(*self, i+1);
645 if (mean < arc_sat) {
646 relabel[i] = ++nkeep;
647 /* Relabeling, if any, starts with ifirst */
648 if (nkeep == i+1) ifirst = nkeep;
649 }
650 }
651 }
652 }
653 }
654
655 if (nkeep < narcs) {
656 /* Update the labelised image by erasing non valid arcs */
657 int * plabim = cpl_image_get_data_int(lab_im);
658 const int npix = cpl_image_get_size_x(lab_im)
659 * cpl_image_get_size_y(lab_im);
660
661 if (nkeep == 0) {
662 cpl_free(relabel);
663#if defined CPL_HAVE_VA_ARGS && CPL_HAVE_VA_ARGS != 0
664 return cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
665 "All %d arc(s) are invalid", narcs);
666#else
667 return cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
668 "All arcs are invalid");
669#endif
670 }
671
672 for (i = 0; i < npix; i++) {
673 const int label = plabim[i];
674
675 if (label < 0 || label > narcs) break;
676 if (label >= ifirst) plabim[i] = relabel[label-1];
677 }
678
679 if (i < npix) {
680 /* lab_im is not a valid label image */
681 cpl_free(relabel);
682 return cpl_error_set(cpl_func, plabim[i] < 0
683 ? CPL_ERROR_ILLEGAL_INPUT
684 : CPL_ERROR_INCOMPATIBLE_INPUT);
685 }
686
687 /* Purge the bad arcs */
688 cpl_apertures_delete(*self);
689 *self = cpl_apertures_new_from_image(arc_im, lab_im);
690
691 }
692
693 cpl_free(relabel);
694
695 cpl_msg_info(cpl_func, "Purged %d of %d arcs (1st purged=%d)", narcs - nkeep,
696 narcs, ifirst);
697
698 /* arc_im may be invalid */
699 cpl_ensure_code(*self != NULL, cpl_error_get_code());
700
701 return CPL_ERROR_NONE;
702}
703
704
705/*----------------------------------------------------------------------------*/
719/*----------------------------------------------------------------------------*/
720static cpl_error_code
721irplib_distortion_fill_arc_positions(cpl_bivector * grid,
722 cpl_vector * fitvalues,
723 const cpl_image * in,
724 const cpl_image * label_im,
725 const cpl_apertures * det)
726{
727 const int narcs = cpl_apertures_get_size(det);
728 int nfitvals = cpl_vector_get_size(fitvalues);
729 const int nx = cpl_image_get_size_x(label_im);
730 const int ny = cpl_image_get_size_y(label_im);
731 cpl_image * filt_img;
732 cpl_mask * kernel;
733 cpl_vector * gridx = cpl_bivector_get_x(grid);
734 cpl_vector * gridy = cpl_bivector_get_y(grid);
735 cpl_polynomial* dist1d;
736 cpl_matrix * dist1dx = NULL;
737 cpl_vector * dist1dy = NULL;
738 double * dgridx;
739 double * dgridy;
740 double * dfitv;
741 int ndone = 0;
742 int i, obj;
743
744 cpl_ensure_code(nfitvals > 0, CPL_ERROR_DATA_NOT_FOUND);
745 cpl_ensure_code(narcs > 0, CPL_ERROR_DATA_NOT_FOUND);
746 cpl_ensure_code(cpl_image_get_type(label_im) == CPL_TYPE_INT,
747 CPL_ERROR_TYPE_MISMATCH);
748
749 /* Ensure space for output */
750 if (nfitvals < narcs * ny) {
751 nfitvals = narcs * ny;
752 cpl_vector_set_size(fitvalues, nfitvals);
753 }
754 if (cpl_vector_get_size(gridx) < nfitvals ||
755 cpl_vector_get_size(gridy) < nfitvals) {
756 cpl_vector_set_size(gridx, nfitvals);
757 cpl_vector_set_size(gridy, nfitvals);
758 }
759
760 /* Get data after resizing */
761 dgridx = cpl_vector_get_data(gridx);
762 dgridy = cpl_vector_get_data(gridy);
763 dfitv = cpl_vector_get_data(fitvalues);
764
765 /* Median filter on input image */
766 kernel = cpl_mask_new(3, 3);
767 cpl_mask_not(kernel);
768 filt_img = cpl_image_new(nx, ny, cpl_image_get_type(in));
769 cpl_image_filter_mask(filt_img, in, kernel, CPL_FILTER_MEDIAN,
770 CPL_BORDER_FILTER);
771 cpl_mask_delete(kernel);
772
773 dist1d = cpl_polynomial_new(1);
774
775 for (obj = 0; obj < narcs; obj++) {
776 /* Find the reference X-coordinate for the arc */
777 const int * plabel_im = cpl_image_get_data_int_const(label_im);
778 const int ndist1d = cpl_apertures_get_top(det, obj+1)
779 - cpl_apertures_get_bottom(det, obj+1) + 1;
780 cpl_boolean sampsym = CPL_TRUE;
781 int j, prevj = 0;
782 int k = 0;
783
784 (void)cpl_matrix_unwrap(dist1dx);
785 (void)cpl_vector_unwrap(dist1dy);
786 dist1dx = cpl_matrix_wrap(1, ndist1d, dgridy + ndone);
787 dist1dy = cpl_vector_wrap(ndist1d, dfitv + ndone);
788
789 /* Find out the X coord. at all Y positions on the arc */
790
791 for (j = cpl_apertures_get_bottom(det, obj+1)-1;
792 j < cpl_apertures_get_top(det, obj+1); j++) {
793
794 for (i = 0; i < nx; i++) {
795 if (plabel_im[i + j * nx] == obj + 1) break;
796 }
797 if (i < nx) {
798 /* Found 1st pixel of aperture obj+1 in row j+1 */
799 cpl_errorstate prestate = cpl_errorstate_get();
800
801 const double x_finepos
802 = irplib_distortion_get_row_centroid(filt_img, label_im,
803 i, j);
804 if (!cpl_errorstate_is_equal(prestate)) {
805 irplib_error_recover(prestate, "Could not find X-position "
806 "for line %d at y=%d (x=%d)",
807 obj+1, j+1, i+1);
808 } else if (x_finepos >= 0.0) {
809 cpl_matrix_set(dist1dx, 0, k, 1.0 + j);
810 cpl_vector_set(dist1dy, k, 1.0 + x_finepos);
811 if (k > 0 && j != 1 + prevj) sampsym = CPL_FALSE;
812 prevj = j;
813 k++;
814 }
815 }
816 }
817 if (k > 0) {
818 double ref_xpos, grad;
819 cpl_error_code error;
820 const cpl_boolean did_drop = k != ndist1d;
821 const cpl_size mindeg = 0;
822 const cpl_size maxdeg = 2;
823
824 if (did_drop) {
825 /* Set correct size */
826 dist1dx = cpl_matrix_wrap(1, k, cpl_matrix_unwrap(dist1dx));
827 dist1dy = cpl_vector_wrap(k, cpl_vector_unwrap(dist1dy));
828 }
829
830 error = cpl_polynomial_fit(dist1d, dist1dx, &sampsym, dist1dy, NULL,
831 CPL_FALSE, &mindeg, &maxdeg);
832 if (error) {
833 cpl_msg_error(cpl_func, "1D-fit failed");
834 break;
835 }
836
837 ref_xpos = cpl_polynomial_eval_1d(dist1d, 0.5 * (ny + 1), &grad);
838
839 for (j = cpl_apertures_get_bottom(det, obj+1)-1;
840 j < cpl_apertures_get_top(det, obj+1); j++) {
841 const double xpos = cpl_polynomial_eval_1d(dist1d, j+1.0, NULL);
842
843 dfitv [ndone] = xpos;
844 dgridx[ndone] = ref_xpos;
845 /* Wrapping dist1dx does _not_ take care of dgridy,
846 in case of "Could not find X-position " */
847 if (did_drop)
848 dgridy[ndone] = 1.0 + j;
849 ndone++;
850 }
851 cpl_msg_info(cpl_func, "Line %d has center gradient %g", obj+1,
852 grad);
853 }
854 }
855
856 cpl_image_delete(filt_img);
857 cpl_polynomial_delete(dist1d);
858 (void)cpl_matrix_unwrap(dist1dx);
859 (void)cpl_vector_unwrap(dist1dy);
860
861 cpl_msg_info(cpl_func, "Found %d fitting points ("
862 "expected up to %d points)", ndone, nfitvals);
863
864 cpl_ensure_code(obj == narcs, cpl_error_get_code());
865
866 cpl_ensure_code(ndone > 0, CPL_ERROR_DATA_NOT_FOUND);
867
868 cpl_vector_set_size(fitvalues, ndone);
869 cpl_vector_set_size(gridx, ndone);
870 cpl_vector_set_size(gridy, ndone);
871
872 return CPL_ERROR_NONE;
873}
874
875/*----------------------------------------------------------------------------*/
885/*----------------------------------------------------------------------------*/
886static double irplib_distortion_get_row_centroid(const cpl_image * im,
887 const cpl_image * label_im,
888 int x,
889 int y)
890{
891 const int nx = cpl_image_get_size_x(im);
892 const int ny = cpl_image_get_size_y(im);
893 const int ynx = y * nx;
894 const float * pim = cpl_image_get_data_float_const(im);
895 const int * plabel_im = cpl_image_get_data_int_const(label_im);
896 int firstpos = -1;
897 int lastpos = -1;
898 int maxpos = x;
899 int objnum;
900 double wsum = 0.0;
901 double sum = 0.0;
902 double max = 0.0;
903
904 cpl_ensure(pim != NULL, cpl_error_get_code(), -1.0);
905 cpl_ensure(plabel_im != NULL, cpl_error_get_code(), -2.0);
906 cpl_ensure(x >= 0, CPL_ERROR_ILLEGAL_INPUT, -3.0);
907 cpl_ensure(y >= 0, CPL_ERROR_ILLEGAL_INPUT, -4.0);
908 cpl_ensure(x < nx, CPL_ERROR_ILLEGAL_INPUT, -5.0);
909 cpl_ensure(y < ny, CPL_ERROR_ILLEGAL_INPUT, -6.0);
910
911 max = (double)pim[x + ynx];
912 objnum = plabel_im[x + ynx];
913
914 /* While we stay in the same object... */
915 do {
916 const double val = (double)pim[x + ynx];
917
918 if (val > 0.0) { /* FIXME: Handle this exception better */
919 wsum += x * val;
920 sum += val;
921
922 if (firstpos < 0) firstpos = x;
923 lastpos = x;
924
925 if (val > max) {
926 max = val;
927 maxpos = x;
928 }
929 }
930
931
932 /* Next point */
933 x++;
934
935 } while (x < nx && objnum == plabel_im[x + ynx]);
936
937 cpl_ensure(sum > 0.0, CPL_ERROR_DATA_NOT_FOUND, -7.0);
938
939 /*
940 assert( 0 <= maxpos && maxpos < nx );
941 assert( objnum == plabel_im[maxpos + ynx] );
942 assert( wsum >= 0.0 );
943 */
944
945 return (wsum < sum * firstpos || wsum > sum * lastpos)
946 ? maxpos : wsum / sum;
947}
948
949/*----------------------------------------------------------------------------*/
955/*----------------------------------------------------------------------------*/
956#define IS_NB_TESTPOINTS 8
957#define IS_MIN_SLOPE 0.01
958#define IS_MAX_SLOPE_DIF 0.075
959#define IS_MAX_FIT_EDGE_DIF 0.05
960#define IS_MIN_RAMP 10.0
961#define IS_MAX_MNERR 13.0
962#define IS_MAX_MNERR_DIF 8.0
963#define IS_MAX_INTER_DIF 20.0
964#define IS_SKIPZONE 2.5
965#define SQR(x) ((x)*(x))
966static cpl_image * irplib_distortion_remove_ramp(const cpl_image * in)
967{
968 int ramp_present;
969 const int nx = cpl_image_get_size_x(in);
970 const int ny = cpl_image_get_size_y(in);
971 const int yhi = (int)(ny/2);
972 const int ylo = yhi - 1;
973 cpl_bivector * testpointlo;
974 double * testpointlo_x;
975 double * testpointlo_y;
976 cpl_bivector * testpointhi;
977 double * testpointhi_x;
978 double * testpointhi_y;
979 const int spacing = ny / (IS_SKIPZONE*IS_NB_TESTPOINTS);
980 double rampdif, fitslope;
981 double * pol_coefhi,
982 * pol_coeflo;
983 cpl_vector * median;
984 double * median_data;
985 double medianerrlo, medianerrhi;
986 double slope;
987 cpl_image * out;
988 float * pout;
989 int i;
990
991 cpl_ensure(cpl_image_get_type(in) == CPL_TYPE_FLOAT,
992 CPL_ERROR_UNSUPPORTED_MODE, NULL);
993
994 if (ny < IS_SKIPZONE * IS_NB_TESTPOINTS){
995#if defined CPL_HAVE_VA_ARGS && CPL_HAVE_VA_ARGS != 0
996 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
997 "image has %d lines, min="
998 CPL_STRINGIFY(IS_SKIPZONE) "*"
999 CPL_STRINGIFY(IS_NB_TESTPOINTS), ny);
1000#else
1001 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
1002 "image has too few lines, min="
1003 CPL_STRINGIFY(IS_SKIPZONE) "*"
1004 CPL_STRINGIFY(IS_NB_TESTPOINTS));
1005#endif
1006 return NULL;
1007 }
1008
1009 /* Fill the vectors */
1010 testpointhi = cpl_bivector_new(IS_NB_TESTPOINTS);
1011 testpointhi_x = cpl_bivector_get_x_data(testpointhi);
1012 testpointhi_y = cpl_bivector_get_y_data(testpointhi);
1013 testpointlo = cpl_bivector_new(IS_NB_TESTPOINTS);
1014 testpointlo_x = cpl_bivector_get_x_data(testpointlo);
1015 testpointlo_y = cpl_bivector_get_y_data(testpointlo);
1016 for (i=0; i<IS_NB_TESTPOINTS; i++) {
1017 int y;
1018 cpl_vector * tmp_vector;
1019 y = yhi + i * spacing;
1020 tmp_vector = cpl_vector_new_from_image_row(in, y+1);
1021 testpointhi_x[i] = y - ny / 2;
1022 testpointhi_y[i] = cpl_vector_get_median_const(tmp_vector);
1023 cpl_vector_delete(tmp_vector);
1024 y = ylo - i * spacing;
1025 tmp_vector = cpl_vector_new_from_image_row(in, y+1);
1026 testpointlo_x[IS_NB_TESTPOINTS-i-1] = y;
1027 testpointlo_y[IS_NB_TESTPOINTS-i-1]=cpl_vector_get_median_const(tmp_vector);
1028 cpl_vector_delete(tmp_vector);
1029 }
1030
1031 /* Apply the fit */
1032 pol_coefhi = irplib_flat_fit_slope_robust(testpointhi_x,
1033 testpointhi_y, IS_NB_TESTPOINTS);
1034 pol_coeflo = irplib_flat_fit_slope_robust(testpointlo_x,
1035 testpointlo_y, IS_NB_TESTPOINTS);
1036
1037 /* Compute the errors */
1038 median = cpl_vector_new(IS_NB_TESTPOINTS);
1039 median_data = cpl_vector_get_data(median);
1040 for (i=0; i<IS_NB_TESTPOINTS; i++) {
1041 median_data[i]=SQR(testpointhi_y[i]
1042 - pol_coefhi[0] - pol_coefhi[1] * testpointhi_x[i]);
1043 }
1044 medianerrhi = cpl_vector_get_median(median);
1045 for (i=0; i<IS_NB_TESTPOINTS; i++) {
1046 median_data[i]=SQR(testpointlo_y[i]
1047 - pol_coeflo[0] - pol_coeflo[1] * testpointlo_x[i]);
1048 }
1049 medianerrlo = cpl_vector_get_median(median);
1050 cpl_vector_delete(median);
1051 rampdif = testpointlo_y[IS_NB_TESTPOINTS-1] - testpointhi_y[0];
1052 slope = rampdif / (ny/2.0);
1053 fitslope = (pol_coefhi[1] + pol_coeflo[1]) / 2.0;
1054
1055 cpl_bivector_delete(testpointlo);
1056 cpl_bivector_delete(testpointhi);
1057
1058 /* Decide if there is a ramp or not */
1059 if (fabs(rampdif)<IS_MIN_RAMP ||
1060 fabs(pol_coefhi[1]) < IS_MIN_SLOPE ||
1061 fabs(pol_coeflo[1]) < IS_MIN_SLOPE ||
1062 pol_coefhi[1]/pol_coeflo[1]<0.5 ||
1063 pol_coefhi[1]/pol_coeflo[1]>2.0 ||
1064 fabs(pol_coefhi[1]-pol_coeflo[1])>IS_MAX_SLOPE_DIF ||
1065 fabs(pol_coefhi[0]-pol_coeflo[0]) > IS_MAX_INTER_DIF ||
1066 medianerrlo> IS_MAX_MNERR ||
1067 medianerrhi> IS_MAX_MNERR ||
1068 fabs(medianerrlo-medianerrhi) >IS_MAX_MNERR_DIF ||
1069 fabs(slope-fitslope) > IS_MAX_FIT_EDGE_DIF ||
1070 slope/fitslope<0.5 ||
1071 slope/fitslope>2.0) ramp_present = 0;
1072 else ramp_present = 1;
1073
1074 cpl_free(pol_coeflo);
1075 cpl_free(pol_coefhi);
1076
1077 /* Correct the ramp if it is there */
1078 out = cpl_image_duplicate(in);
1079 pout = cpl_image_get_data_float(out);
1080 if (ramp_present == 1) {
1081 float val;
1082 int j;
1083 for (j=0; j<ny/2; j++) {
1084 val = slope * (j-ny/2);
1085 for (i=0; i<nx; i++)
1086 pout[i+j*nx] -= val;
1087 }
1088 for (j=ny/2; j<ny; j++) {
1089 val = slope * (j-ny);
1090 for (i=0; i<nx; i++)
1091 pout[i+j*nx] -= val;
1092 }
1093
1094 }
1095
1096 return out;
1097}
1098
1099/*----------------------------------------------------------------------------*/
1113/*----------------------------------------------------------------------------*/
1114static cpl_error_code irplib_image_filter_background_line(cpl_image * self,
1115 const cpl_image * other,
1116 int hsize,
1117 cpl_boolean vertical)
1118{
1119 const int nx = cpl_image_get_size_x(self);
1120 const int ny = cpl_image_get_size_y(self);
1121 const int msize = 1 + 2 * hsize;
1122 cpl_mask * mask;
1123 cpl_image * background;
1124 cpl_error_code error = CPL_ERROR_NONE;
1125
1126 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
1127 cpl_ensure_code(hsize >= 0, CPL_ERROR_ILLEGAL_INPUT);
1128
1129 if (other == NULL) other = self;
1130
1131 mask = vertical ? cpl_mask_new(msize, 1) : cpl_mask_new(1, msize);
1132
1133 error |= cpl_mask_not(mask);
1134
1135 background = cpl_image_new(nx, ny, cpl_image_get_type(other));
1136
1137 error |= cpl_image_filter_mask(background, other, mask, CPL_FILTER_MEDIAN,
1138 CPL_BORDER_FILTER);
1139 cpl_mask_delete(mask);
1140
1141 if (self != other) {
1142 error |= cpl_image_copy(self, other, 1, 1);
1143 }
1144
1145 error |= cpl_image_subtract(self, background);
1146 cpl_image_delete(background);
1147
1148 return error ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
1149}
1150
1151
1152
1178static cpl_matrix * irplib_matrix_product_normal_create(const cpl_matrix * self)
1179{
1180
1181 double sum;
1182 cpl_matrix * product;
1183 const double * ai = cpl_matrix_get_data_const(self);
1184 double * bwrite;
1185 const int m = cpl_matrix_get_nrow(self);
1186 const int n = cpl_matrix_get_ncol(self);
1187 int i, j, k;
1188
1189
1190 cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, NULL);
1191
1192#if 0
1193 /* Initialize all values to zero.
1194 This is done to avoid access of uninitilized memory, in case
1195 someone passes the matrix to for example cpl_matrix_dump(). */
1196 product = cpl_matrix_new(m, m);
1197 bwrite = cpl_matrix_get_data(product);
1198#else
1199 bwrite = (double *) cpl_malloc(m * m * sizeof(double));
1200 product = cpl_matrix_wrap(m, m, bwrite);
1201#endif
1202
1203 /* The result at (i,j) is the dot-product of i'th and j'th row */
1204 for (i = 0; i < m; i++, bwrite += m, ai += n) {
1205 const double * aj;
1206 aj = ai; /* aj points to first entry in j'th row */
1207 for (j = i; j < m; j++, aj += n) {
1208 sum = 0.0;
1209 for (k = 0; k < n; k++) {
1210 sum += ai[k] * aj[k];
1211 }
1212 bwrite[j] = sum;
1213 }
1214 }
1215
1216 return product;
1217
1218}
1219
1220/*----------------------------------------------------------------------------*/
1234/*----------------------------------------------------------------------------*/
1235static cpl_error_code irplib_polynomial_fit_2d(cpl_polynomial * self,
1236 const cpl_bivector * xy_pos,
1237 const cpl_vector * values,
1238 int degree, double fixy,
1239 double * mse)
1240{
1241
1242 const int np = cpl_bivector_get_size(xy_pos);
1243 /* Number of unknowns to determine in one dimension */
1244 const int nc1 = 1+degree;
1245 /* Number of unknowns to determine */
1246 /* P_{i,0} = 0, except P_{1,0} = 1 */
1247 const int nc = nc1 * (1 + nc1) / 2 - nc1;
1248 cpl_matrix * mv; /* The transpose of the Vandermonde matrix */
1249 cpl_matrix * mh; /* Block-Hankel matrix, V'*V */
1250 cpl_matrix * mb;
1251 cpl_matrix * mx;
1252#ifdef IRPLIB_DISTORTION_ASSERT
1253 /*const double * coeffs1d;*/
1254#endif
1255 double * dmv;
1256 cpl_vector * xhat;
1257 cpl_vector * yhat;
1258 cpl_vector * zhat;
1259 cpl_size powers[2];
1260 int degx, degy;
1261 int i, j;
1262 cpl_error_code error;
1263
1264
1265 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
1266 cpl_ensure_code(cpl_polynomial_get_dimension(self) == 2,
1267 CPL_ERROR_INVALID_TYPE);
1268 cpl_ensure_code(np > 0, cpl_error_get_code());
1269 cpl_ensure_code(values != NULL, CPL_ERROR_NULL_INPUT);
1270
1271 cpl_ensure_code(cpl_vector_get_size(values) == np,
1272 CPL_ERROR_INCOMPATIBLE_INPUT);
1273
1274 cpl_ensure_code(degree > 0, CPL_ERROR_ILLEGAL_INPUT);
1275 cpl_ensure_code(np >= nc, CPL_ERROR_DATA_NOT_FOUND);
1276
1277 /* transform zero-point to fixy */
1278 yhat = cpl_vector_duplicate(cpl_bivector_get_y_const(xy_pos));
1279 cpl_vector_subtract_scalar(yhat, fixy);
1280
1281 /* - and ensure P(y) = y on center line */
1282 xhat = cpl_vector_duplicate(cpl_bivector_get_x_const(xy_pos));
1283 zhat = cpl_vector_duplicate(values);
1284 cpl_vector_subtract(zhat, xhat);
1285
1286 /* Initialize matrices */
1287 /* mv contains the polynomial terms in the order described */
1288 /* above in each row, for each input point. */
1289 dmv = (double*)cpl_malloc(nc*np*sizeof(double));
1290 mv = cpl_matrix_wrap(nc, np, dmv);
1291
1292 /* Has redundant FLOPs, appears to improve accuracy */
1293 for (i=0; i < np; i++) {
1294 const double x = cpl_vector_get(xhat, i);
1295 const double y = cpl_vector_get(yhat, i);
1296 double yvalue = y;
1297 j = 0;
1298 for (degy = 1; degy <= degree; degy++) {
1299 double xvalue = 1;
1300 for (degx = 0; degx <= degree-degy; degx++, j++) {
1301 dmv[np * j + i] = xvalue * yvalue;
1302 xvalue *= x;
1303 }
1304 yvalue *= y;
1305 }
1306 /* cx_assert( j == nc ); */
1307 }
1308 cpl_vector_delete(xhat);
1309 cpl_vector_delete(yhat);
1310
1311 /* mb contains the values, it is not modified */
1312 mb = cpl_matrix_wrap(np, 1, cpl_vector_get_data(zhat));
1313
1314 /* Form the right hand side of the normal equations */
1315 mx = cpl_matrix_product_create(mv, mb);
1316
1317 cpl_matrix_unwrap(mb);
1318 cpl_vector_delete(zhat);
1319
1320 /* Form the matrix of the normal equations */
1321 mh = irplib_matrix_product_normal_create(mv);
1322 cpl_matrix_delete(mv);
1323
1324 /* Solve XA=B by a least-square solution (aka pseudo-inverse). */
1325 error = cpl_matrix_decomp_chol(mh) || cpl_matrix_solve_chol(mh, mx);
1326
1327 cpl_matrix_delete(mh);
1328
1329 if (error) {
1330 cpl_matrix_delete(mx);
1331 cpl_ensure_code(0, error);
1332 }
1333
1334 /* Store coefficients for output */
1335
1336#ifdef IRPLIB_DISTORTION_ASSERT
1337 /*coeffs1d = cpl_matrix_get_data(mx);*/
1338#endif
1339
1340 j = 0;
1341 for (degy = 1; degy <= degree; degy++) {
1342 powers[1] = degy;
1343 for (degx = 0; degx <= degree-degy; degx++, j++) {
1344 powers[0] = degx;
1345 /* cx_assert( coeffs1d[j] == cpl_matrix_get(mx, j, 0) ); */
1346 cpl_polynomial_set_coeff(self, powers, cpl_matrix_get(mx, j, 0));
1347 }
1348 }
1349 /* cx_assert( j == nc ); */
1350
1351 cpl_matrix_delete(mx);
1352
1353 /* P_{1,0} = 1 */
1354 powers[0] = 1;
1355 powers[1] = 0;
1356 cpl_polynomial_set_coeff(self, powers, 1.0);
1357
1358 /* Transform the polynomial back in Y */
1359 cpl_polynomial_shift_1d(self, 1, -fixy);
1360
1361 /* If requested, compute mean squared error */
1362 if (mse != NULL) {
1363 const cpl_vector * x_pos = cpl_bivector_get_x_const(xy_pos);
1364 const cpl_vector * y_pos = cpl_bivector_get_y_const(xy_pos);
1365 cpl_vector * x_val = cpl_vector_new(2);
1366
1367 *mse = 0;
1368 for (i=0; i<np; i++) {
1369 double residue;
1370 cpl_vector_set(x_val, 0, cpl_vector_get(x_pos, i));
1371 cpl_vector_set(x_val, 1, cpl_vector_get(y_pos, i));
1372 /* Subtract from the true value, square, accumulate */
1373 residue = cpl_vector_get(values, i)
1374 - cpl_polynomial_eval(self, x_val);
1375 *mse += residue * residue;
1376 }
1377 cpl_vector_delete(x_val);
1378 /* Average the error term */
1379 *mse /= np;
1380 }
1381
1382 return CPL_ERROR_NONE;
1383}
1384
double * irplib_flat_fit_slope_robust(double *x, double *y, int np)
Fit a slope to a list of points (robust fit).
Definition: irplib_flat.c:189