33#include "cr2res_slit_curv.h"
34#include "cr2res_dfs.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"
60 double *result) CPL_ATTR_NONNULL;
64 double *result) CPL_ATTR_NONNULL;
65static int cr2res_slit_curv_remove_peaks_at_edge(
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
74static int cr2res_slit_curv_remove_outliers(
79 const int fit_second_order,
80 const double divergence
82static int cr2res_slit_curv_single_peak(
83 const cpl_image * img_peak,
84 const cpl_vector * ycen,
93static int cr2res_slit_curv_all_peaks(
94 const cpl_vector * peaks,
95 const cpl_image * img_rect,
96 const cpl_vector * ycen,
98 const int fit_second_order,
101 cpl_vector ** vec_c) ;
134 const hdrl_image * img,
135 const cpl_table * trace_wave,
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)
146 const cpl_image * img_in;
149 cpl_bivector * spec_bi;
152 cpl_image * img_rect;
157 cpl_matrix * samppos;
158 hdrl_image * hdrl_other;
159 int fit_second_order;
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;
168 cpl_msg_error(__func__,
"Only degree 1 or 2 are valid") ;
174 const int ncols = 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){
187 height, &sfunc, &spec_bi, &model) != 0){
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);
200 trace, ncols)) == NULL){
201 cpl_vector_delete(peaks);
205 cpl_vector_delete(peaks);
206 cpl_vector_delete(ycen);
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);
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);
222 cpl_image_delete(img_rect);
223 cpl_vector_delete(ycen);
230 cr2res_slit_curv_remove_outliers(peaks, vec_a, vec_b,
231 vec_c, fit_second_order, 5);
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);
245 samppos = cpl_matrix_wrap(1, cpl_vector_get_size(peaks),
246 cpl_vector_get_data(peaks));
248 *slit_poly_a = cpl_polynomial_new(1);
249 *slit_poly_b = cpl_polynomial_new(1);
250 *slit_poly_c = cpl_polynomial_new(1);
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);
263 cpl_polynomial_set_coeff(*slit_poly_a, &power,
264 1 + cpl_polynomial_get_coeff(*slit_poly_a, &power));
267 cpl_vector_delete(vec_a);
268 cpl_vector_delete(vec_b);
269 cpl_vector_delete(vec_c);
270 cpl_vector_delete(peaks);
272 if (cpl_error_get_code() != CPL_ERROR_NONE){
273 cpl_errorstate_dump(NULL, CPL_FALSE,
274 cpl_errorstate_dump_one);
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
298 cpl_image * img_other;
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);
308 if (cpl_error_get_code() != CPL_ERROR_NONE){
337static int cr2res_slit_curv_remove_outliers(
342 const int fit_second_order,
343 const double divergence
346 cpl_vector * remove_peaks;
347 cpl_image * img_other;
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);
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++)
366 if (fabs(cpl_vector_get(vec_a, i) - median) > mad * divergence)
367 cpl_vector_set(remove_peaks, i, 1);
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++)
374 if (fabs(cpl_vector_get(vec_b, i) - median) > mad * divergence)
375 cpl_vector_set(remove_peaks, i, 1);
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++)
384 if (fabs(cpl_vector_get(vec_c, i) - median) > mad * divergence)
385 cpl_vector_set(remove_peaks, i, 1);
392 for (i = 0; i < npeaks; i++){
393 if (cpl_vector_get(remove_peaks, i) != 0){
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));
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);
408 cpl_vector_delete(remove_peaks);
410 if (cpl_error_get_code() != CPL_ERROR_NONE){
430 const cpl_table * trace_wave,
437 cpl_image * out_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 ;
450 if (trace_wave == NULL)
return NULL ;
453 nrows = cpl_table_get_nrow(trace_wave) ;
457 out =
hdrl_image_new(CR2RES_DETECTOR_SIZE, CR2RES_DETECTOR_SIZE) ;
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) ;
464 for (k = 0; k < nrows; k++) {
465 int cur_order, cur_trace_id;
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 ;
473 tmp_array = cpl_table_get_array(trace_wave, CR2RES_COL_SLIT_CURV_A, k) ;
475 tmp_array = cpl_table_get_array(trace_wave, CR2RES_COL_SLIT_CURV_B, k) ;
477 tmp_array = cpl_table_get_array(trace_wave, CR2RES_COL_SLIT_CURV_C, k) ;
480 if (slit_poly_a != NULL && slit_poly_b != NULL && slit_poly_c != NULL) {
482 tmp_array = cpl_table_get_array(trace_wave, CR2RES_COL_UPPER, k) ;
486 tmp_array = cpl_table_get_array(trace_wave, CR2RES_COL_LOWER, k) ;
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) ;
501 for (i=0 ; i<nx ; i++) {
503 if ((ref_x % spacing_pixels) != 0) continue ;
505 cpl_msg_debug(__func__,
506 "Process Order/Trace: %d/%d - ref_x=%g",
507 cur_order, cur_trace_id, (
double)ref_x) ;
511 slit_poly_b, slit_poly_c, ref_x) ;
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,
519 x1 = (cpl_size)x_slit_pos ;
521 val1 = value * (x2-x_slit_pos) ;
522 val2 = value - val1 ;
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 ;
530 cpl_polynomial_delete(slit_curv_poly) ;
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) ;
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) ;
558 cpl_polynomial * slit_poly_a,
559 cpl_polynomial * slit_poly_b,
560 cpl_polynomial * slit_poly_c,
563 cpl_polynomial * out ;
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)) ;
646static int fmodel(
const double x[],
const double a[],
double *result){
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];
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]];
662 const double pos_x = x[0];
663 const double pos_y = x[1];
664 const double pos_y_rel = x[1] - yc;
670 const double curvature =
671 tilt * pos_y_rel + shear * (pos_y * pos_y - yc * yc);
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)];
691static int dmodel_da(
const double x[],
const double a[],
double *result){
692 const double height = a[0];
694 const double sigma = a[2];
695 const double center = a[3];
696 const double tilt = a[4];
697 const double shear = a[5];
699 const double * ycen = (
double*)(intptr_t)a[8];
700 const double yc = ycen[(size_t)x[0]];
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;
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));
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;
736static int cr2res_slit_curv_remove_peaks_at_edge(
741 cpl_size i, j, npeaks;
746 npeaks = cpl_vector_get_size(*peaks);
747 for (i = 0; i < npeaks; i++) {
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));
754 cpl_vector_set_size(*peaks, j);
790static int cr2res_slit_curv_single_peak(
791 const cpl_image * img_peak,
792 const cpl_vector * ycen,
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;
814 const int width = cpl_image_get_size_x(img_peak);
815 const int height = cpl_image_get_size_y(img_peak);
820 for (j = 0; j < width; j++){
821 for (k = 0; k < height; k++){
824 pix_value = cpl_image_get(img_peak, j+1, k+1, &badpix);
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);
839 img_slitfunc = cpl_image_collapse_median_create(img_peak, 1, 0, 0);
840 maximum = cpl_image_get_max(img_slitfunc);
844 cpl_image_delete(img_slitfunc);
845 *value_a = *value_b = *value_c = 0.;
849 cpl_image_divide_scalar(img_slitfunc, maximum);
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,
855 minimum = cpl_image_get(img_spec, 1, 1, &badpix);
856 cpl_image_delete(img_spec);
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));
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));
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,
883 cpl_matrix_unwrap(x_extract);
884 cpl_vector_unwrap(y_extract);
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])
894 fmodel(pos, cpl_vector_get_data(a), &result);
895 cpl_image_set(img_model, j+1, k+1, result);
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);
905 cpl_image_delete(img_slitfunc);
908 if (error != CPL_ERROR_NONE){
909 cpl_msg_debug(__func__,
"%s", cpl_error_get_message());
912 *value_a = *value_b = *value_c = 0.;
916 *value_b = cpl_vector_get(a, 4);
917 *value_c = cpl_vector_get(a, 5);
921 yc = cpl_vector_get(ycen, (cpl_size)peak);
922 *value_a = -(*value_b * yc + *value_c * yc * yc);
948static int cr2res_slit_curv_all_peaks(
949 const cpl_vector * peaks,
950 const cpl_image * img_rect,
951 const cpl_vector * ycen,
953 const int fit_second_order,
958 double value_a, value_b, value_c;
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);
980 const int N = width * height;
984 const int ia[] = {1, 1, 1, 1, 1, fit_second_order, 0, 0, 0};
986 if (peaks == NULL || img_rect == NULL || ycen == NULL ||
987 vec_a == NULL || vec_b == NULL || vec_c == NULL)
return -1;
989 x = cpl_matrix_new(N, D);
990 y = cpl_vector_new(N);
991 a = cpl_vector_new(M);
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));
997 for (i = 0; i < npeaks; i++){
998 cpl_image * img_peak;
1002 peak = cpl_vector_get(peaks, i);
1003 img_peak = cpl_image_extract(img_rect, peak - window, 1,
1004 peak + window, height);
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);
1013 cpl_matrix_delete(x);
1014 cpl_vector_delete(y);
1015 cpl_vector_delete(a);
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_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.
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.
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
const cpl_image * hdrl_image_get_image_const(const hdrl_image *himg)
get data as cpl image
hdrl_image * hdrl_image_new(cpl_size nx, cpl_size ny)
create new zero filled hdrl image
void hdrl_image_delete(hdrl_image *himg)
delete hdrl_image