SINFONI Pipeline Reference Manual  2.6.0
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 
71 static cpl_apertures * irplib_distortion_detect_arcs(cpl_image *,
72  cpl_image **, int, int, double, int, int, int, int);
73 static cpl_error_code irplib_distortion_fill_border(cpl_image *, int, int,
74  int, int, double);
75 static int irplib_distortion_threshold1d(cpl_image *, double, cpl_image *,
76  double);
77 static cpl_error_code irplib_distortion_purge_arcs(cpl_apertures **, cpl_image *,
78  const cpl_image *, int, int,
79  double);
80 static 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 
86 static double irplib_distortion_get_row_centroid(const cpl_image *,
87  const cpl_image *, int, int);
88 
89 static int irplib_distortion_sub_hor_lowpass(cpl_image *, int);
90 static cpl_image * irplib_distortion_remove_ramp(const cpl_image *);
91 
92 static cpl_error_code irplib_image_filter_background_line(cpl_image *,
93  const cpl_image *, int, cpl_boolean) ;
94 
95 static cpl_error_code irplib_polynomial_fit_2d(cpl_polynomial *,
96  const cpl_bivector *,
97  const cpl_vector *, int,
98  double, double *);
99 
100 static cpl_matrix * irplib_matrix_product_normal_create(const cpl_matrix *);
101 
102 /*-----------------------------------------------------------------------------
103  Functions code
104  -----------------------------------------------------------------------------*/
105 
108 /*----------------------------------------------------------------------------*/
137 /*----------------------------------------------------------------------------*/
138 cpl_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 /*----------------------------------------------------------------------------*/
285 static 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  IRPLIB_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 /*----------------------------------------------------------------------------*/
435 static 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 
487 static 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 
520 static 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 /*----------------------------------------------------------------------------*/
599 static
600 cpl_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 /*----------------------------------------------------------------------------*/
720 static cpl_error_code
721 irplib_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 /*----------------------------------------------------------------------------*/
886 static 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))
966 static 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  IRPLIB_STRINGIFY(IS_SKIPZONE) "*"
999  IRPLIB_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  IRPLIB_STRINGIFY(IS_SKIPZONE) "*"
1004  IRPLIB_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 /*----------------------------------------------------------------------------*/
1114 static 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 
1178 static 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 /*----------------------------------------------------------------------------*/
1235 static 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:191