CR2RE Pipeline Reference Manual 1.6.7
cr2res_slit_curv.c
1/*
2 * This file is part of the CR2RES Pipeline
3 * Copyright (C) 2002,2003 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24/*-----------------------------------------------------------------------------
25 Includes
26 -----------------------------------------------------------------------------*/
27
28#include <math.h>
29#include <string.h>
30
31#include <cpl.h>
32
33#include "cr2res_slit_curv.h"
34#include "cr2res_dfs.h"
35#include "cr2res_io.h"
36#include "cr2res_pfits.h"
37#include "cr2res_utils.h"
38#include "cr2res_trace.h"
39#include "cr2res_extract.h"
40#include "cr2res_etalon.h"
41
42/*-----------------------------------------------------------------------------
43 Defines
44 -----------------------------------------------------------------------------*/
45
46/*-----------------------------------------------------------------------------
47 Functions prototypes
48 -----------------------------------------------------------------------------*/
49
50/*static int cr2res_slit_curv_get_position(
51 cpl_polynomial * trace,
52 cpl_polynomial * wave,
53 double ref_wl,
54 double * xpos,
55 double * ypos) ;*/
56
57static int fmodel(
58 const double x[],
59 const double a[],
60 double *result) CPL_ATTR_NONNULL;
61static int dmodel_da(
62 const double x[],
63 const double a[],
64 double *result) CPL_ATTR_NONNULL;
65static int cr2res_slit_curv_remove_peaks_at_edge(
66 cpl_vector ** peaks,
67 const int width,
68 const int ncols) ;
69static int cr2res_slit_curv_smooth_image_median(
70 hdrl_image * hdrl_out,
71 const cpl_image * img_in,
72 const cpl_size kernel_size
73);
74static int cr2res_slit_curv_remove_outliers(
75 cpl_vector * peaks,
76 cpl_vector * vec_a,
77 cpl_vector * vec_b,
78 cpl_vector * vec_c,
79 const int fit_second_order,
80 const double divergence
81);
82static int cr2res_slit_curv_single_peak(
83 const cpl_image * img_peak,
84 const cpl_vector * ycen,
85 const double peak,
86 cpl_matrix * x,
87 cpl_vector * y,
88 cpl_vector * a,
89 const int * ia,
90 double * value_a,
91 double * value_b,
92 double * value_c) ;
93static int cr2res_slit_curv_all_peaks(
94 const cpl_vector * peaks,
95 const cpl_image * img_rect,
96 const cpl_vector * ycen,
97 const int window,
98 const int fit_second_order,
99 cpl_vector ** vec_a,
100 cpl_vector ** vec_b,
101 cpl_vector ** vec_c) ;
102
103/*----------------------------------------------------------------------------*/
107/*----------------------------------------------------------------------------*/
108
111/*----------------------------------------------------------------------------*/
132/*----------------------------------------------------------------------------*/
134 const hdrl_image * img,
135 const cpl_table * trace_wave,
136 const int order,
137 const int trace,
138 const int height,
139 const int window,
140 const cpl_size change_degree,
141 const int slit_degree,
142 cpl_polynomial ** slit_poly_a,
143 cpl_polynomial ** slit_poly_b,
144 cpl_polynomial ** slit_poly_c)
145{
146 const cpl_image * img_in;
147 cpl_vector * sfunc;
148 cpl_vector * ycen;
149 cpl_bivector * spec_bi;
150 hdrl_image * model;
151 cpl_vector * peaks;
152 cpl_image * img_rect;
153 cpl_vector * vec_a;
154 cpl_vector * vec_b;
155 cpl_vector * vec_c;
156 cpl_size power;
157 cpl_matrix * samppos;
158 hdrl_image * hdrl_other;
159 int fit_second_order;
160
161 if (img == NULL || trace_wave == NULL || slit_poly_a == NULL ||
162 slit_poly_b == NULL || slit_poly_c == NULL) return -1;
163 if (slit_degree == 1)
164 fit_second_order = 0;
165 else if (slit_degree == 2)
166 fit_second_order = 1;
167 else {
168 cpl_msg_error(__func__, "Only degree 1 or 2 are valid") ;
169 return -1 ;
170 }
171
172
173 img_in = hdrl_image_get_image_const(img);
174 const int ncols = cpl_image_get_size_x(img_in);
175
176 // Median filter the image
177 // to remove outliers, which would mess with the peak detection
178 hdrl_other = hdrl_image_new(cpl_image_get_size_x(img_in),
179 cpl_image_get_size_y(img_in));
180 if (cr2res_slit_curv_smooth_image_median(hdrl_other, img_in, 3) != 0){
181 return -1;
182 }
183 img_in = hdrl_image_get_image_const(hdrl_other);
184
185 // Determine the peaks and remove peaks at the edges of the order
186 if (cr2res_extract_sum_vert(hdrl_other, trace_wave, order, trace,
187 height, &sfunc, &spec_bi, &model) != 0){
188 return -1;
189 }
190 peaks = cr2res_etalon_find_peaks(cpl_bivector_get_x(spec_bi),
191 cpl_vector_get_mean(cpl_bivector_get_x(spec_bi)), 3);
192 cr2res_slit_curv_remove_peaks_at_edge(&peaks, window, ncols);
193 cpl_bivector_delete(spec_bi);
194 cpl_vector_delete(sfunc);
195 hdrl_image_delete(model);
196
197
198 // Rectify the image
199 if ((ycen = cr2res_trace_get_ycen(trace_wave, order,
200 trace, ncols)) == NULL){
201 cpl_vector_delete(peaks);
202 return -1 ;
203 }
204 if ((img_rect = cr2res_image_cut_rectify(img_in, ycen, height)) == NULL){
205 cpl_vector_delete(peaks);
206 cpl_vector_delete(ycen);
207 return -1;
208 }
209 if (cpl_msg_get_level() == CPL_MSG_DEBUG){
210 cpl_image_save(img_rect, "debug_image_rect.fits",
211 CPL_TYPE_DOUBLE, NULL, CPL_IO_CREATE);
212 }
213
214 // Determine the curvature of all peaks
215 if (cr2res_slit_curv_all_peaks(peaks, img_rect, ycen, window,
216 fit_second_order, &vec_a, &vec_b, &vec_c) != 0){
217 cpl_vector_delete(peaks);
218 cpl_vector_delete(ycen);
219 cpl_image_delete(img_rect);
220 return -1;
221 }
222 cpl_image_delete(img_rect);
223 cpl_vector_delete(ycen);
224 hdrl_image_delete(hdrl_other);
225
226 // Discard outliers
227 // It would be better to make the fit itself more robust,
228 // however this is a lot easier and works as long as there is no
229 // strong variation within the order
230 cr2res_slit_curv_remove_outliers(peaks, vec_a, vec_b,
231 vec_c, fit_second_order, 5);
232
233 if (cpl_msg_get_level() == CPL_MSG_DEBUG){
234 cpl_vector_save(peaks, "debug_peaks.fits",
235 CPL_TYPE_DOUBLE, NULL, CPL_IO_CREATE);
236 cpl_vector_save(vec_a, "debug_vector_a.fits",
237 CPL_TYPE_DOUBLE, NULL, CPL_IO_CREATE);
238 cpl_vector_save(vec_b, "debug_vector_b.fits",
239 CPL_TYPE_DOUBLE, NULL, CPL_IO_CREATE);
240 cpl_vector_save(vec_c, "debug_vector_c.fits",
241 CPL_TYPE_DOUBLE, NULL, CPL_IO_CREATE);
242 }
243
244 // Fit the curvature to the whole order
245 samppos = cpl_matrix_wrap(1, cpl_vector_get_size(peaks),
246 cpl_vector_get_data(peaks));
247
248 *slit_poly_a = cpl_polynomial_new(1);
249 *slit_poly_b = cpl_polynomial_new(1);
250 *slit_poly_c = cpl_polynomial_new(1);
251
252 cpl_polynomial_fit(*slit_poly_a, samppos, NULL, vec_a, NULL,
253 CPL_TRUE, NULL, &change_degree);
254 cpl_polynomial_fit(*slit_poly_b, samppos, NULL, vec_b, NULL,
255 CPL_TRUE, NULL, &change_degree);
256 cpl_polynomial_fit(*slit_poly_c, samppos, NULL, vec_c, NULL,
257 CPL_TRUE, NULL, &change_degree);
258 cpl_matrix_unwrap(samppos);
259
260 // Add 1 to the linear coefficient of the a polynomial
261 // So we can subtract it later in the extraction
262 power = 1;
263 cpl_polynomial_set_coeff(*slit_poly_a, &power,
264 1 + cpl_polynomial_get_coeff(*slit_poly_a, &power));
265
266 // Clean up memory
267 cpl_vector_delete(vec_a);
268 cpl_vector_delete(vec_b);
269 cpl_vector_delete(vec_c);
270 cpl_vector_delete(peaks);
271
272 if (cpl_error_get_code() != CPL_ERROR_NONE){
273 cpl_errorstate_dump(NULL, CPL_FALSE,
274 cpl_errorstate_dump_one);
275 // Probably the fit failed
276 // Maybe not enough peaks?
277 return -1;
278 }
279
280 return 0;
281}
282
283/*----------------------------------------------------------------------------*/
292/*----------------------------------------------------------------------------*/
293static int cr2res_slit_curv_smooth_image_median(
294 hdrl_image * hdrl_out,
295 const cpl_image * img_in,
296 const cpl_size kernel_size
297){
298 cpl_image * img_other;
299 cpl_mask * kernel;
300
301 img_other = hdrl_image_get_image(hdrl_out);
302 kernel = cpl_mask_new(kernel_size, kernel_size);
303 cpl_mask_not(kernel);
304 cpl_image_filter_mask(img_other, img_in, kernel,
305 CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
306 cpl_mask_delete(kernel);
307
308 if (cpl_error_get_code() != CPL_ERROR_NONE){
309 return -1;
310 }
311
312 return 0;
313}
314
315/*----------------------------------------------------------------------------*/
336/*----------------------------------------------------------------------------*/
337static int cr2res_slit_curv_remove_outliers(
338 cpl_vector * peaks,
339 cpl_vector * vec_a,
340 cpl_vector * vec_b,
341 cpl_vector * vec_c,
342 const int fit_second_order,
343 const double divergence
344)
345{
346 cpl_vector * remove_peaks;
347 cpl_image * img_other;
348 cpl_size npeaks;
349 cpl_size i, j;
350 double median, mad;
351
352 // First step is to find the peaks to remove
353 // We use the median and the median absolute deviation (mad)
354
355 npeaks = cpl_vector_get_size(peaks);
356 remove_peaks = cpl_vector_new(npeaks);
357 for (i=0; i < npeaks; i++) cpl_vector_set(remove_peaks, i, 0);
358
359 // Unfortunately we have to wrap the vector data in an image
360 // since the relevant function is only available for images
361 img_other = cpl_image_wrap_double(1, npeaks, cpl_vector_get_data(vec_a));
362 median = cpl_image_get_mad(img_other, &mad);
363 cpl_image_unwrap(img_other);
364 for (i = 0; i < npeaks; i++)
365 {
366 if (fabs(cpl_vector_get(vec_a, i) - median) > mad * divergence)
367 cpl_vector_set(remove_peaks, i, 1);
368 }
369 img_other = cpl_image_wrap_double(1, npeaks, cpl_vector_get_data(vec_b));
370 median = cpl_image_get_mad(img_other, &mad);
371 cpl_image_unwrap(img_other);
372 for (i = 0; i < npeaks; i++)
373 {
374 if (fabs(cpl_vector_get(vec_b, i) - median) > mad * divergence)
375 cpl_vector_set(remove_peaks, i, 1);
376 }
377 if (fit_second_order){
378 img_other = cpl_image_wrap_double(1, npeaks,
379 cpl_vector_get_data(vec_c));
380 median = cpl_image_get_mad(img_other, &mad);
381 cpl_image_unwrap(img_other);
382 for (i = 0; i < npeaks; i++)
383 {
384 if (fabs(cpl_vector_get(vec_c, i) - median) > mad * divergence)
385 cpl_vector_set(remove_peaks, i, 1);
386 }
387 }
388
389 // Remove the outlier peaks from the vectors
390 // Overwrite the removed peaks, with data from the next entries
391 j = 0;
392 for (i = 0; i < npeaks; i++){
393 if (cpl_vector_get(remove_peaks, i) != 0){
394 j++;
395 } else {
396 cpl_vector_set(peaks, i-j, cpl_vector_get(peaks, i));
397 cpl_vector_set(vec_a, i-j, cpl_vector_get(vec_a, i));
398 cpl_vector_set(vec_b, i-j, cpl_vector_get(vec_b, i));
399 cpl_vector_set(vec_c, i-j, cpl_vector_get(vec_c, i));
400 }
401 }
402 npeaks -= j;
403 cpl_vector_set_size(peaks, npeaks);
404 cpl_vector_set_size(vec_a, npeaks);
405 cpl_vector_set_size(vec_b, npeaks);
406 cpl_vector_set_size(vec_c, npeaks);
407
408 cpl_vector_delete(remove_peaks);
409
410 if (cpl_error_get_code() != CPL_ERROR_NONE){
411 return -1;
412 }
413 return 0;
414}
415
416/*----------------------------------------------------------------------------*/
428/*----------------------------------------------------------------------------*/
430 const cpl_table * trace_wave,
431 int order,
432 int trace_id,
433 int spacing_pixels,
434 int full_trace)
435{
436 hdrl_image * out ;
437 cpl_image * out_ima ;
438 double * pout_ima ;
439 const cpl_array * tmp_array ;
440 cpl_polynomial * slit_poly_a ;
441 cpl_polynomial * slit_poly_b ;
442 cpl_polynomial * slit_poly_c ;
443 cpl_polynomial * upper_poly ;
444 cpl_polynomial * lower_poly ;
445 cpl_polynomial * slit_curv_poly ;
446 double upper_pos, lower_pos, x_slit_pos, value, val1, val2 ;
447 cpl_size i, j, k, nrows, nx, ny, x1, x2, ref_x ;
448
449 /* Check Entries */
450 if (trace_wave == NULL) return NULL ;
451
452 /* Initialise */
453 nrows = cpl_table_get_nrow(trace_wave) ;
454 value = 10000. ;
455
456 /* Create the image */
457 out = hdrl_image_new(CR2RES_DETECTOR_SIZE, CR2RES_DETECTOR_SIZE) ;
458 out_ima = hdrl_image_get_image(out) ;
459 nx = cpl_image_get_size_x(out_ima) ;
460 ny = cpl_image_get_size_y(out_ima) ;
461 pout_ima = cpl_image_get_data_double(out_ima) ;
462
463 /* Loop on the traces */
464 for (k = 0; k < nrows; k++) {
465 int cur_order, cur_trace_id;
466 /* Only specified order / trace */
467 cur_order = cpl_table_get(trace_wave, CR2RES_COL_ORDER, k, NULL) ;
468 cur_trace_id = cpl_table_get(trace_wave, CR2RES_COL_TRACENB,k,NULL);
469 if (order > -1 && order != cur_order) continue ;
470 if (trace_id > -1 && trace_id != cur_trace_id) continue ;
471
472 /* Get the Slit curvature for the trace */
473 tmp_array = cpl_table_get_array(trace_wave, CR2RES_COL_SLIT_CURV_A, k) ;
474 slit_poly_a = cr2res_convert_array_to_poly(tmp_array) ;
475 tmp_array = cpl_table_get_array(trace_wave, CR2RES_COL_SLIT_CURV_B, k) ;
476 slit_poly_b = cr2res_convert_array_to_poly(tmp_array) ;
477 tmp_array = cpl_table_get_array(trace_wave, CR2RES_COL_SLIT_CURV_C, k) ;
478 slit_poly_c = cr2res_convert_array_to_poly(tmp_array) ;
479
480 if (slit_poly_a != NULL && slit_poly_b != NULL && slit_poly_c != NULL) {
481 /* Get the Upper Polynomial */
482 tmp_array = cpl_table_get_array(trace_wave, CR2RES_COL_UPPER, k) ;
483 upper_poly = cr2res_convert_array_to_poly(tmp_array) ;
484
485 /* Get the Lower Polynomial */
486 tmp_array = cpl_table_get_array(trace_wave, CR2RES_COL_LOWER, k) ;
487 lower_poly = cr2res_convert_array_to_poly(tmp_array) ;
488
489 /* Check if all Polynomials are available */
490 if (upper_poly == NULL || lower_poly == NULL) {
491 if (upper_poly != NULL) cpl_polynomial_delete(upper_poly) ;
492 if (lower_poly != NULL) cpl_polynomial_delete(lower_poly) ;
493 cpl_msg_warning(__func__, "Cannot get UPPER/LOWER information");
494 cpl_polynomial_delete(slit_poly_a) ;
495 cpl_polynomial_delete(slit_poly_b) ;
496 cpl_polynomial_delete(slit_poly_c) ;
497 continue ;
498 }
499
500 /* Set the Pixels in the trace */
501 for (i=0 ; i<nx ; i++) {
502 ref_x = i+1 ;
503 if ((ref_x % spacing_pixels) != 0) continue ;
504
505 cpl_msg_debug(__func__,
506 "Process Order/Trace: %d/%d - ref_x=%g",
507 cur_order, cur_trace_id, (double)ref_x) ;
508
509 /* Create the slit curvature polynomial at position i+1 */
510 slit_curv_poly = cr2res_slit_curv_build_poly(slit_poly_a,
511 slit_poly_b, slit_poly_c, ref_x) ;
512
513 upper_pos = cpl_polynomial_eval_1d(upper_poly, ref_x, NULL) ;
514 lower_pos = cpl_polynomial_eval_1d(lower_poly, ref_x, NULL) ;
515 for (j=0 ; j<ny ; j++) {
516 if ((j+1 >= lower_pos && j+1 <= upper_pos) || full_trace) {
517 x_slit_pos = cpl_polynomial_eval_1d(slit_curv_poly,
518 j+1, NULL) ;
519 x1 = (cpl_size)x_slit_pos ;
520 x2 = x1 + 1 ;
521 val1 = value * (x2-x_slit_pos) ;
522 val2 = value - val1 ;
523 /*cpl_msg_debug(__func__,
524 "%"CPL_SIZE_FORMAT" %"CPL_SIZE_FORMAT" (%g) / %"CPL_SIZE_FORMAT" %"CPL_SIZE_FORMAT" (%g)",
525 x1-1, j, val1, x2-1, j, val2) ; */
526 if (x1>=1 && x1<=nx) pout_ima[(x1-1)+j*nx] = val1 ;
527 if (x2>=1 && x2<=nx) pout_ima[(x2-1)+j*nx] = val2 ;
528 }
529 }
530 cpl_polynomial_delete(slit_curv_poly) ;
531 }
532 cpl_polynomial_delete(slit_poly_a) ;
533 cpl_polynomial_delete(slit_poly_b) ;
534 cpl_polynomial_delete(slit_poly_c) ;
535 cpl_polynomial_delete(upper_poly) ;
536 cpl_polynomial_delete(lower_poly) ;
537 } else {
538 if (slit_poly_a != NULL) cpl_polynomial_delete(slit_poly_a) ;
539 if (slit_poly_b != NULL) cpl_polynomial_delete(slit_poly_b) ;
540 if (slit_poly_c != NULL) cpl_polynomial_delete(slit_poly_c) ;
541 }
542 }
543 return out ;
544}
545
546
547/*----------------------------------------------------------------------------*/
556/*----------------------------------------------------------------------------*/
558 cpl_polynomial * slit_poly_a,
559 cpl_polynomial * slit_poly_b,
560 cpl_polynomial * slit_poly_c,
561 cpl_size x)
562{
563 cpl_polynomial * out ;
564 cpl_size power ;
565
566 /* Generate the slit curvature polynomial */
567 out = cpl_polynomial_new(1) ;
568 power = 0 ; cpl_polynomial_set_coeff(out, &power,
569 cpl_polynomial_eval_1d(slit_poly_a, x, NULL)) ;
570 power = 1 ; cpl_polynomial_set_coeff(out, &power,
571 cpl_polynomial_eval_1d(slit_poly_b, x, NULL)) ;
572 power = 2 ; cpl_polynomial_set_coeff(out, &power,
573 cpl_polynomial_eval_1d(slit_poly_c, x, NULL)) ;
574 return out ;
575}
576
579/*----------------------------------------------------------------------------*/
592/*----------------------------------------------------------------------------*/
593/*
594static int cr2res_slit_curv_get_position(
595 cpl_polynomial * trace,
596 cpl_polynomial * wave,
597 double ref_wl,
598 double * xpos,
599 double * ypos)
600{
601 double cur_wl, tmp_wl ;
602 cpl_size x ;
603*/
604 /* Check entries */
605/* if (trace == NULL || wave == NULL || xpos == NULL || ypos == NULL)
606 return -1 ;
607*/
608 /* Loop on the x positions */
609/* for (x=0 ; x<=CR2RES_DETECTOR_SIZE+1 ; x++) {
610 cur_wl = cpl_polynomial_eval_1d(wave, (double)x, NULL) ;
611*/
612 /* As soon as the WL is bigger than ref_wl, we keep X */
613/* if (cur_wl > ref_wl) break ;
614 }
615
616 tmp_wl = cpl_polynomial_eval_1d(wave, (double)(x-1), NULL) ;
617*/
618 /* Linear interpolation */
619/* if (fabs(cur_wl-tmp_wl) < 1e-5)
620 *xpos = (double)x ;
621 else
622 *xpos = (double)(x -((cur_wl-ref_wl) / (cur_wl-tmp_wl))) ;
623
624 *ypos = cpl_polynomial_eval_1d(trace, *xpos, NULL) ;
625*/ /* Check results */
626/* if (*xpos < 1 || *xpos > CR2RES_DETECTOR_SIZE ||
627 *ypos < 1 || *ypos > CR2RES_DETECTOR_SIZE) {
628 *xpos = *ypos = -1 ;
629 return -1 ;
630 }
631 return 0 ;
632}
633*/
634/*----------------------------------------------------------------------------*/
645/*----------------------------------------------------------------------------*/
646static int fmodel(const double x[], const double a[], double *result){
647 // result = B + A * exp(-(x-mu)**2 / (2 * sigma**2))
648 // with mu = center + tilt * y + shear * y**2
649 const double height = a[0];
650 const double bottom = a[1];
651 const double sigma = a[2];
652 const double center = a[3];
653 const double tilt = a[4];
654 const double shear = a[5];
655 const double nrows = a[6];
656 // We pass the slitfunc and ycen arrays by reference
657 // but since a is a double, we have to get tricky with the casting
658 const double * slitfunc = (const double*)(intptr_t)a[7];
659 const double * ycen = (const double*)(intptr_t)a[8];
660 const double yc = ycen[(size_t)x[0]];
661
662 const double pos_x = x[0];
663 const double pos_y = x[1];
664 const double pos_y_rel = x[1] - yc;
665
666 // Shift the center of the gaussian
667 // The curvature should be 0, when pos_y = ycen (in the global frame)
668 // i.e. we fix the offset a = - tilt * yc - shear * yc * yc
669 // Which means we are using x**2 - yc**2 and NOT (x-yc)**2
670 const double curvature =
671 tilt * pos_y_rel + shear * (pos_y * pos_y - yc * yc);
672 // Calculate the Gaussian
673 const double b1 = pos_x - center - curvature;
674 *result = bottom + height * exp(-(b1 * b1) / (2 * sigma * sigma));
675 *result *= slitfunc[(size_t)(pos_y_rel + nrows/2)];
676 return 0;
677}
678
679/*----------------------------------------------------------------------------*/
690/*----------------------------------------------------------------------------*/
691static int dmodel_da(const double x[], const double a[], double *result){
692 const double height = a[0];
693 //const double bottom = a[1];
694 const double sigma = a[2];
695 const double center = a[3];
696 const double tilt = a[4];
697 const double shear = a[5];
698 //const double nrows = a[6];
699 const double * ycen = (double*)(intptr_t)a[8];
700 const double yc = ycen[(size_t)x[0]];
701
702 const double pos_x = x[0];
703 const double pos_y = x[1];
704 const double pos_y_rel = x[1] - yc;
705 const double pos_y_squared = pos_y * pos_y - yc * yc;
706
707 const double curvature = tilt * pos_y_rel + shear * pos_y_squared;
708 const double b1 = pos_x - center - curvature;
709 const double b2 = sigma * sigma;
710 const double factor = exp(-(b1 * b1) / (2 * sigma * sigma));
711
712 result[0] = factor;
713 result[1] = 1.0;
714 result[2] = height * factor * (b1 * b1 / b2 - 1) / sigma;
715 result[3] = height * factor * b1 / b2;
716 result[4] = result[3] * pos_y_rel;
717 result[5] = result[3] * pos_y_squared;
718 // The others dont matter?
719 return 0;
720}
721
722/*----------------------------------------------------------------------------*/
735/*----------------------------------------------------------------------------*/
736static int cr2res_slit_curv_remove_peaks_at_edge(
737 cpl_vector ** peaks,
738 const int width,
739 const int ncols)
740{
741 cpl_size i, j, npeaks;
742
743 // Loop through the vector and shift peaks to the beginning of the vector,
744 // overwriting existing values, of peaks at the edge
745 j = 0;
746 npeaks = cpl_vector_get_size(*peaks);
747 for (i = 0; i < npeaks; i++) {
748 double peak;
749 peak = cpl_vector_get(*peaks, i);
750 if (peak <= width || peak >= ncols - width) continue;
751 cpl_vector_set(*peaks, j, cpl_vector_get(*peaks, i));
752 j++;
753 }
754 cpl_vector_set_size(*peaks, j);
755
756 return 0;
757}
758
759/*----------------------------------------------------------------------------*/
789/*----------------------------------------------------------------------------*/
790static int cr2res_slit_curv_single_peak(
791 const cpl_image * img_peak,
792 const cpl_vector * ycen,
793 const double peak,
794 cpl_matrix * x,
795 cpl_vector * y,
796 cpl_vector * a,
797 const int * ia,
798 double * value_a,
799 double * value_b,
800 double * value_c)
801{
802 cpl_size n, j, k;
803 int window, badpix;
804 cpl_error_code error;
805 cpl_image * img_slitfunc;
806 cpl_image * img_spec;
807 cpl_matrix * x_extract;
808 cpl_vector * y_extract;
809 double minimum, maximum;
810 double result;
811 double pos[2];
812 double pix_value;
813
814 const int width = cpl_image_get_size_x(img_peak);
815 const int height = cpl_image_get_size_y(img_peak);
816 window = width / 2;
817
818 // Set x and y matrix/vector
819 n = 0;
820 for (j = 0; j < width; j++){
821 for (k = 0; k < height; k++){
822 // We want to use the absolute reference frame
823 // as thats what is desired in the output
824 pix_value = cpl_image_get(img_peak, j+1, k+1, &badpix);
825 // Filter out bad pixels
826 if (!badpix && !isnan(pix_value)){
827 pos[0] = peak - window + j;
828 pos[1] = cpl_vector_get(ycen, (cpl_size)pos[0]) - height / 2 + k;
829 cpl_matrix_set(x, n, 0, pos[0]);
830 cpl_matrix_set(x, n, 1, pos[1]);
831 cpl_vector_set(y, n, pix_value);
832 n++;
833 }
834 }
835 }
836
837 // Collapse image along y axis, to get slit illumination
838 // normalized to the peak maximum
839 img_slitfunc = cpl_image_collapse_median_create(img_peak, 1, 0, 0);
840 maximum = cpl_image_get_max(img_slitfunc);
841 // minimum = cpl_image_get_min(img_slitfunc);
842 if (maximum <= 0){
843 // Abort before division by 0, and weird peaks
844 cpl_image_delete(img_slitfunc);
845 *value_a = *value_b = *value_c = 0.;
846 return -1;
847 }
848
849 cpl_image_divide_scalar(img_slitfunc, maximum);
850
851 // Collapse image along x axis, to get spectrum
852 img_spec = cpl_image_collapse_median_create(img_peak, 0, 0, 0);
853 maximum = cpl_image_get(img_spec, cpl_image_get_size_x(img_spec) / 2, 1,
854 &badpix);
855 minimum = cpl_image_get(img_spec, 1, 1, &badpix);
856 cpl_image_delete(img_spec);
857
858 // Set initial guesses
859 // We pass the slitfunction and the ycen arrays as pointers disguised as
860 // doubles in the vector. Its not very pretty, but it beats copying them
861 // into the vector
862 cpl_vector_set(a, 0, maximum - minimum);
863 cpl_vector_set(a, 1, minimum);
864 cpl_vector_set(a, 2, (double)window / 4.);
865 cpl_vector_set(a, 3, peak);
866 cpl_vector_set(a, 4, 0);
867 cpl_vector_set(a, 5, 0);
868 cpl_vector_set(a, 6, height);
869 cpl_vector_set(a, 7, (double)(intptr_t)cpl_image_get_data(img_slitfunc));
870 cpl_vector_set(a, 8, (double)(intptr_t)cpl_vector_get_data_const(ycen));
871
872 // We want to reuse the memory in every peak, but we only need a fraction of
873 // it in every iteration. To adjust the size we wrap the larger memory
874 // in a smaller matrix/vector, that only has the good pixels in it
875 x_extract = cpl_matrix_wrap(n, 2, cpl_matrix_get_data(x));
876 y_extract = cpl_vector_wrap(n, cpl_vector_get_data(y));
877
878 // Fit the model
879 error = cpl_fit_lvmq(x_extract, NULL, y_extract, NULL, a, ia, fmodel, dmodel_da,
880 CPL_FIT_LVMQ_TOLERANCE, CPL_FIT_LVMQ_COUNT, CPL_FIT_LVMQ_MAXITER,
881 NULL, NULL, NULL);
882
883 cpl_matrix_unwrap(x_extract);
884 cpl_vector_unwrap(y_extract);
885
886 if (cpl_msg_get_level() == CPL_MSG_DEBUG){
887 cpl_image * img_model;
888 img_model = cpl_image_new(width, height, CPL_TYPE_DOUBLE);
889 for (j = 0; j < width; j++){
890 for (k = 0; k < height; k++){
891 pos[0] = peak - window + j;
892 pos[1] = cpl_vector_get(ycen, (cpl_size)pos[0])
893 - height / 2 + k;
894 fmodel(pos, cpl_vector_get_data(a), &result);
895 cpl_image_set(img_model, j+1, k+1, result);
896 }
897 }
898 cpl_image_save(img_peak, "debug_image_curv.fits",
899 CPL_TYPE_DOUBLE, NULL, CPL_IO_CREATE);
900 cpl_image_save(img_model, "debug_model_curv.fits",
901 CPL_TYPE_DOUBLE, NULL, CPL_IO_CREATE);
902 cpl_image_delete(img_model);
903 }
904
905 cpl_image_delete(img_slitfunc);
906
907 // Check results
908 if (error != CPL_ERROR_NONE){
909 cpl_msg_debug(__func__, "%s", cpl_error_get_message());
910 cpl_error_reset();
911
912 *value_a = *value_b = *value_c = 0.;
913 return -1;
914 } else {
915 double yc;
916 *value_b = cpl_vector_get(a, 4);
917 *value_c = cpl_vector_get(a, 5);
918 // The offset a was fixed so that is 0 in the local frame
919 // i.e. so that the curvature at the central line is 0
920 // a = - tilt * ycen - shear * ycen * ycen
921 yc = cpl_vector_get(ycen, (cpl_size)peak);
922 *value_a = -(*value_b * yc + *value_c * yc * yc);
923 return 0;
924 }
925}
926
927/*----------------------------------------------------------------------------*/
947/*----------------------------------------------------------------------------*/
948static int cr2res_slit_curv_all_peaks(
949 const cpl_vector * peaks,
950 const cpl_image * img_rect,
951 const cpl_vector * ycen,
952 const int window,
953 const int fit_second_order,
954 cpl_vector ** vec_a,
955 cpl_vector ** vec_b,
956 cpl_vector ** vec_c)
957{
958 double value_a, value_b, value_c;
959 cpl_size i;
960
961 const int width = 2 * window + 1;
962 const int height = cpl_image_get_size_y(img_rect);
963 const int npeaks = cpl_vector_get_size(peaks);
964
965 cpl_matrix * x;
966 cpl_vector * y;
967 cpl_vector * a;
968
969 // Fit parameters are:
970 // The peak height of the Gaussian
971 // The bottom level of the Gaussian
972 // The width (sigma) of the Gaussian
973 // The center position (as offset from the peak pixel)
974 // The tilt (first order curvature)
975 // The shear (second order curvature), only if fit_second_order is not 0
976 // Fixed parameters are:
977 // Number of rows in the image
978 // The slit illumination function array (as a pointer)
979 // The central row (ycen) array (as a pointer)
980 const int N = width * height;
981 const int D = 2;
982 const int M = 9;
983
984 const int ia[] = {1, 1, 1, 1, 1, fit_second_order, 0, 0, 0};
985
986 if (peaks == NULL || img_rect == NULL || ycen == NULL ||
987 vec_a == NULL || vec_b == NULL || vec_c == NULL) return -1;
988
989 x = cpl_matrix_new(N, D);
990 y = cpl_vector_new(N);
991 a = cpl_vector_new(M);
992
993 *vec_a = cpl_vector_new(cpl_vector_get_size(peaks));
994 *vec_b = cpl_vector_new(cpl_vector_get_size(peaks));
995 *vec_c = cpl_vector_new(cpl_vector_get_size(peaks));
996
997 for (i = 0; i < npeaks; i++){
998 cpl_image * img_peak;
999 double peak;
1000 // Loop over the individual lines
1001 // and fit each of them separately
1002 peak = cpl_vector_get(peaks, i);
1003 img_peak = cpl_image_extract(img_rect, peak - window, 1,
1004 peak + window, height);
1005
1006 cr2res_slit_curv_single_peak(img_peak, ycen, peak, x, y, a, ia,
1007 &value_a, &value_b, &value_c);
1008 cpl_vector_set(*vec_a, i, value_a);
1009 cpl_vector_set(*vec_b, i, value_b);
1010 cpl_vector_set(*vec_c, i, value_c);
1011 cpl_image_delete(img_peak);
1012 }
1013 cpl_matrix_delete(x);
1014 cpl_vector_delete(y);
1015 cpl_vector_delete(a);
1016
1017 return 0;
1018}
1019
cpl_vector * cr2res_etalon_find_peaks(const cpl_vector *in, double height, double distance)
Detect peaks from a 1d periodic signal and store their positions.
int cr2res_extract_sum_vert(const hdrl_image *hdrl_in, const cpl_table *trace_tab, int order, int trace_id, int height, cpl_vector **slit_func, cpl_bivector **spec, hdrl_image **model)
Simple extraction function.
int cr2res_slit_curv_compute_order_trace(const hdrl_image *img, const cpl_table *trace_wave, const int order, const int trace, const int height, const int window, const cpl_size change_degree, const int slit_degree, cpl_polynomial **slit_poly_a, cpl_polynomial **slit_poly_b, cpl_polynomial **slit_poly_c)
Get the slit curvature directly from the image.
hdrl_image * cr2res_slit_curv_gen_map(const cpl_table *trace_wave, int order, int trace_id, int spacing_pixels, int full_trace)
Compute the slit_curv map from the trace_wave table.
cpl_polynomial * cr2res_slit_curv_build_poly(cpl_polynomial *slit_poly_a, cpl_polynomial *slit_poly_b, cpl_polynomial *slit_poly_c, cpl_size x)
Create the slit curvature polynomial for x position.
cpl_vector * cr2res_trace_get_ycen(const cpl_table *trace, int order_idx, int trace_nb, int size)
Retrieves the middle (All) polynomial from trace table and evaluates.
Definition: cr2res_trace.c:769
cpl_image * cr2res_image_cut_rectify(const cpl_image *img_in, const cpl_vector *ycen, int height)
Cut a bent order into a rectangle, shifting columns.
Definition: cr2res_utils.c:907
cpl_polynomial * cr2res_convert_array_to_poly(const cpl_array *arr)
Convert an array to polynomial.
cpl_image * hdrl_image_get_image(hdrl_image *himg)
get data as cpl image
Definition: hdrl_image.c:105
const cpl_image * hdrl_image_get_image_const(const hdrl_image *himg)
get data as cpl image
Definition: hdrl_image.c:118
hdrl_image * hdrl_image_new(cpl_size nx, cpl_size ny)
create new zero filled hdrl image
Definition: hdrl_image.c:311
void hdrl_image_delete(hdrl_image *himg)
delete hdrl_image
Definition: hdrl_image.c:379