ERIS Pipeline Reference Manual 1.8.15
eris_nix_match.c
1/* $Id$
2 *
3 * This file is part of the ERIS/NIX Pipeline
4 * Copyright (C) 2017 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 02110-1301 USA
19 */
20
21 /*
22 * $Author$
23 * $Date$
24 * $Rev$
25 */
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#include "casu_stats.h"
30#endif
31
32/*-----------------------------------------------------------------------------
33 Includes
34 -----------------------------------------------------------------------------*/
35
36#include <casu_mods.h>
37#include <hdrl.h>
38#include <libgen.h>
39#include <math.h>
40#include <string.h>
41
42#include "eris_nix_defs.h"
43#include "eris_nix_match.h"
44
45/*----------------------------------------------------------------------------*/
49/*----------------------------------------------------------------------------*/
50
54/*----------------------------------------------------------------------------*/
70/*----------------------------------------------------------------------------*/
71
72cpl_error_code enm_associate_std(cpl_table * objtab,
73 cpl_table * stdtab,
74 const float assoc,
75 const int strict_classification,
76 cpl_table ** associated_std) {
77
78 if (cpl_error_get_code() != CPL_ERROR_NONE) return cpl_error_get_code();
79 if (!objtab) return CPL_ERROR_NONE;
80 if (!stdtab) return CPL_ERROR_NONE;
81
82 //cpl_msg_info(cpl_func, "matching");
83 //cpl_table_dump(objtab, 0, 100, NULL);
84 //cpl_msg_info(cpl_func, "..table of standards");
85 //cpl_table_dump(stdtab, 0, 100, NULL);
86
87 /* Check the size of the tables */
88
89 cpl_size nstd = cpl_table_get_nrow(stdtab);
90 cpl_size nobj = cpl_table_get_nrow(objtab);
91 enu_check_error_code("Error accessing standard or object tables");
92 if (nstd == 0) {
93 cpl_msg_warning(cpl_func, "No associated sources: standards "
94 "catalogue is empty");
95 *associated_std = NULL;
96 return CPL_ERROR_NONE;
97 } else if (nobj == 0) {
98 cpl_msg_warning(cpl_func, "No associated sources: image "
99 "catalogue is empty");
100 *associated_std = NULL;
101 return CPL_ERROR_NONE;
102 }
103
104 /* tables look sensible - look for associations */
105
106 cpl_table_cast_column(stdtab, "xpredict", NULL, CPL_TYPE_DOUBLE);
107 double * xstd = cpl_table_get_data_double(stdtab, "xpredict");
108 cpl_table_cast_column(stdtab, "ypredict", NULL, CPL_TYPE_DOUBLE);
109 double * ystd = cpl_table_get_data_double(stdtab, "ypredict");
110 enu_check_error_code("Error mapping table columns 1");
111 cpl_table_cast_column(objtab, "X_coordinate", NULL, CPL_TYPE_DOUBLE);
112 double * xobj = cpl_table_get_data_double(objtab, "X_coordinate");
113 cpl_table_cast_column(objtab, "Y_coordinate", NULL, CPL_TYPE_DOUBLE);
114 double * yobj = cpl_table_get_data_double(objtab, "Y_coordinate");
115 cpl_table_cast_column(objtab, "Classification", NULL, CPL_TYPE_DOUBLE);
116 double * classification = cpl_table_get_data_double(objtab,
117 "Classification");
118 cpl_table_cast_column(objtab, "Ellipticity", NULL, CPL_TYPE_DOUBLE);
119 double * ellipticity = cpl_table_get_data_double(objtab,
120 "Ellipticity");
121
122 /* loop through table of standards, selecting rows that associate
123 with an image object */
124
125 float assoc2 = assoc * assoc;
126 cpl_table_unselect_all(stdtab);
127
128 for (cpl_size istd = 0; istd < nstd; istd++) {
129
130 /* is there an image object within assoc pixels? */
131
132 cpl_size iobj_match = -1;
133 int unique = CPL_TRUE;
134
135 for (cpl_size iobj= 0; iobj < nobj; iobj++) {
136
137 /* Ignore if object is weird and we're being strict.
138 CASU classifications < 0 are good, >= 0 complicated or junk */
139 if (strict_classification &&
140 (classification[iobj] >= 0.0 || ellipticity[iobj] < 0.5)) {
141 continue;
142 }
143
144 float distance2 = pow(xobj[iobj] - xstd[istd], 2.0) +
145 pow(yobj[iobj] - ystd[istd], 2.0);
146 if (distance2 < assoc2) {
147 if (iobj_match != -1) {
148 unique = CPL_FALSE;
149 break;
150 } else {
151 iobj_match = iobj;
152 }
153 }
154 }
155
156 if (iobj_match != -1 && unique) {
157 cpl_table_select_row(stdtab, istd);
158 }
159 }
160
161 if (cpl_table_count_selected(stdtab) > 0) {
162
163 /* Make an associated_std table of the rows selected by association */
164
165 *associated_std = cpl_table_extract_selected(stdtab);
166
167 /* Go through the associated standards table, find the associated
168 object and add the object columns to it. Ignore the RA and DEC
169 columns in the catalogue as the standards table will already
170 have these */
171
172 cpl_array * colnames = cpl_table_get_column_names(objtab);
173 cpl_size ncol = cpl_array_get_size(colnames);
174
175 for (cpl_size i = 0; i < ncol; i++) {
176 const char * colname = cpl_array_get_string(colnames, i);
177 if (strcmp(colname,"RA") && strcmp(colname,"DEC")) {
178 cpl_table_new_column(*associated_std, colname,
179 cpl_table_get_column_type(objtab,
180 colname));
181 }
182 }
183
184 nstd = cpl_table_get_nrow(*associated_std);
185
186 cpl_table_cast_column(*associated_std, "xpredict", NULL,
187 CPL_TYPE_DOUBLE);
188 xstd = cpl_table_get_data_double(*associated_std, "xpredict");
189 cpl_table_cast_column(*associated_std, "ypredict", NULL,
190 CPL_TYPE_DOUBLE);
191 ystd = cpl_table_get_data_double(*associated_std, "ypredict");
192 enu_check_error_code("Error mapping matched table columns 1");
193
194 for (cpl_size istd = 0; istd < nstd; istd++) {
195
196 /* find the associated standard */
197
198 cpl_size iobj_match = -1;
199 for (cpl_size iobj= 0; iobj < nobj; iobj++) {
200 if (strict_classification &&
201 (classification[iobj] >= 0.0 || ellipticity[iobj] < 0.5)) {
202 continue;
203 }
204 float distance2 = pow(xobj[iobj] - xstd[istd], 2.0) +
205 pow(yobj[iobj] - ystd[istd], 2.0);
206 if (distance2 < assoc2) {
207 iobj_match = iobj;
208 break;
209 }
210 }
211
212 for (cpl_size i = 0; i < ncol; i++) {
213 const char * colname = cpl_array_get_string(colnames, i);
214 if (strcmp(colname,"RA") && strcmp(colname,"DEC")) {
215 int ignore = 0;
216
217 switch (cpl_table_get_column_type(objtab, colname)) {
218 case CPL_TYPE_INT:
219 cpl_table_set_int(*associated_std, colname, istd,
220 cpl_table_get_int(objtab, colname,
221 iobj_match, &ignore));
222 break;
223 case CPL_TYPE_FLOAT:
224 cpl_table_set_float(*associated_std, colname, istd,
225 cpl_table_get_float(objtab,
226 colname, iobj_match, &ignore));
227 break;
228 case CPL_TYPE_DOUBLE:
229 cpl_table_set_double(*associated_std, colname, istd,
230 cpl_table_get_double(objtab,
231 colname, iobj_match, &ignore));
232 break;
233 default:
234 cpl_table_set_float(*associated_std, colname, istd,
235 cpl_table_get_float(objtab,
236 colname, iobj_match, &ignore));
237 break;
238 }
239 }
240 }
241 }
242 cpl_array_delete(colnames);
243
244 } else {
245 cpl_msg_warning(cpl_func, "No sources associated");
246 *associated_std = NULL;
247 return CPL_ERROR_NONE;
248 }
249
250 cleanup:
251
252 if (cpl_error_get_code() != CPL_ERROR_NONE) {
253 cpl_table_delete(*associated_std);
254 *associated_std = NULL;
255 }
256 return cpl_error_get_code();
257}
258
259
260/*----------------------------------------------------------------------------*/
276/*----------------------------------------------------------------------------*/
277
278cpl_error_code enm_correct_crpix(const char * wcs_method,
279 const char * catalogue,
280 cpl_table * matched_stds,
281 located_image * limage,
282 cpl_matrix ** xy_shift) {
283
284 if (cpl_error_get_code() != CPL_ERROR_NONE) return cpl_error_get_code();
285 cpl_ensure_code(wcs_method, CPL_ERROR_NULL_INPUT);
286 cpl_ensure_code(catalogue, CPL_ERROR_NULL_INPUT);
287 cpl_ensure_code(limage, CPL_ERROR_NULL_INPUT);
288 cpl_ensure_code(xy_shift, CPL_ERROR_NULL_INPUT);
289
290 /* Get the RA,DECs and x,y coords of the matched objects and
291 use them to Calculate the median wcs offset between expected
292 and measured positions of matched catalogue objects */
293
294 cpl_size nmatch = 0;
295 if (matched_stds) {
296 nmatch = cpl_table_get_nrow(matched_stds);
297 //cpl_table_dump(matched_stds, 0, 100, NULL);
298 }
299 //cpl_msg_info(cpl_func, "nmatch %d", (int)nmatch);
300
301 if (nmatch > 0) {
302 /* cast columns to double if required */
303 if (cpl_table_get_column_type(matched_stds, "X_coordinate") ==
304 CPL_TYPE_FLOAT) {
305 cpl_table_cast_column(matched_stds,
306 "X_coordinate",
307 NULL,
308 CPL_TYPE_DOUBLE);
309 cpl_table_cast_column(matched_stds,
310 "Y_coordinate",
311 NULL,
312 CPL_TYPE_DOUBLE);
313 cpl_table_cast_column(matched_stds,
314 "xpredict",
315 NULL,
316 CPL_TYPE_DOUBLE);
317 cpl_table_cast_column(matched_stds,
318 "ypredict",
319 NULL,
320 CPL_TYPE_DOUBLE);
321 }
322 /* map the columns */
323 const double * x_coord = cpl_table_get_data_double_const(matched_stds,
324 "X_coordinate");
325 const double * y_coord = cpl_table_get_data_double_const(matched_stds,
326 "Y_coordinate");
327 const double * xpredict = cpl_table_get_data_double_const(matched_stds,
328 "xpredict");
329 const double * ypredict = cpl_table_get_data_double_const(matched_stds,
330 "ypredict");
331
332 enu_check_error_code("failed to access matched table: %s",
333 cpl_error_get_message());
334
335 double diff[nmatch];
336 for (cpl_size i = 0; i < nmatch; i++) {
337 diff[i] = x_coord[i] - xpredict[i];
338 }
339 double xoffset = casu_dmed(diff, NULL, nmatch);
340 for (cpl_size i = 0; i < nmatch; i++) {
341 diff[i] = y_coord[i] - ypredict[i];
342 }
343 double yoffset = casu_dmed(diff, NULL, nmatch);
344
345 /* update WCS keywords with offset */
346
347 cpl_msg_info(cpl_func, "correcting %4.2e %4.2e", xoffset, yoffset);
348
349 double crpix1 = cpl_propertylist_get_double(limage->plist, "CRPIX1");
350 cpl_propertylist_update_double(limage->plist, "CRPIX1", crpix1 + xoffset);
351 double crpix2 = cpl_propertylist_get_double(limage->plist, "CRPIX2");
352 cpl_propertylist_update_double(limage->plist, "CRPIX2", crpix2 + yoffset);
353
354 /* store correction method */
355
356 cpl_propertylist_update_string(limage->plist, "ESO WCS_METHOD",
357 wcs_method);
358 cpl_propertylist_update_string(limage->plist, "WCS_CAT",
359 catalogue);
360
361 /* store the shift being used */
362
363 *xy_shift = cpl_matrix_new(1, 2);
364 cpl_matrix_set(*xy_shift, 0, 0, xoffset);
365 cpl_matrix_set(*xy_shift, 0, 1, yoffset);
366 } else {
367 cpl_msg_info(cpl_func, "no entries in matched standards table, no "
368 "correction applied");
369 *xy_shift = NULL;
370 }
371
372 cleanup:
373
374 return cpl_error_get_code();
375}
376
377
378/*----------------------------------------------------------------------------*/
398/*----------------------------------------------------------------------------*/
399
400cpl_error_code enm_try_all_associations(cpl_table * objtab,
401 cpl_table * stdtab,
402 const double match_rad,
403 cpl_table ** matchtab) {
404
405 cpl_array * colnames = NULL;
406 const char * funcid = "enm_match_brute_force";
407 cpl_size * matches = NULL;
408 cpl_array * match_is = NULL;
409 cpl_array * match_io = NULL;
410 cpl_table * mstds = NULL;
411
412 if (cpl_error_get_code() != CPL_ERROR_NONE) return cpl_error_get_code();
413 if (!objtab) return CPL_ERROR_NONE;
414 if (!stdtab) return CPL_ERROR_NONE;
415
416 //cpl_table_dump(stdtab, 0, 100, NULL);
417 //cpl_table_dump(objtab, 0, 100, NULL);
418
419 /* Check the size of the tables */
420
421 cpl_size nstd = cpl_table_get_nrow(stdtab);
422 cpl_size nobj = cpl_table_get_nrow(objtab);
423 enu_check(nstd > 0, CPL_ERROR_ILLEGAL_INPUT, "Standards catalogue"
424 "contains no entries");
425 enu_check(nobj > 0, CPL_ERROR_ILLEGAL_INPUT, "Image catalogue"
426 "contains no entries");
427
428 /* sort the tables in order of source flux. One aim of this that
429 if the tables have few entries with several possible matches
430 then a match with the brightest source will be found first
431 and win - in such a case this may not always be the right
432 outcome but, hopefully, more often than not*/
433
434 if (cpl_table_has_column(stdtab, "Aper_flux_3")) {
435 cpl_msg_info(cpl_func, " ..sorting standards table");
436 cpl_propertylist * reflist = cpl_propertylist_new();
437 cpl_propertylist_update_bool(reflist, "Aper_flux_3", CPL_TRUE);
438 cpl_table_sort(stdtab, reflist);
439 cpl_propertylist_delete(reflist);
440 }
441 if (cpl_table_has_column(objtab, "Aper_flux_3")) {
442 cpl_msg_info(cpl_func, " ..sorting catalogue table");
443 cpl_propertylist * reflist = cpl_propertylist_new();
444 cpl_propertylist_update_bool(reflist, "Aper_flux_3", CPL_TRUE);
445 cpl_table_sort(objtab, reflist);
446 cpl_propertylist_delete(reflist);
447 }
448
449 /* the algorithm is to trial associating each standard with each
450 object then see how many other sources 'match'.
451 The time taken goes as nsts*nobj (# trials) * nstd*nobj (test
452 metric) so can quickly get large. This is unnecessary if
453 we just to find the best shift so limit the sizes -
454 but try to do this in a way that doesn't limit the spread of
455 objects over the images */
456
457 if (nstd > 100) {
458 cpl_msg_info(cpl_func, "truncating standards table to 100 entries");
459 nstd = 100;
460 }
461 if (nobj > 100) {
462 cpl_msg_info(cpl_func, "truncating catalogue table to 100 entries");
463 nobj = 100;
464 }
465
466 *matchtab = NULL;
467
468 /* tables look sensible - map positional columns */
469
470 double * xstd = cpl_table_get_data_double(stdtab, "xpredict");
471 double * ystd = cpl_table_get_data_double(stdtab, "ypredict");
472 double * xobj = cpl_table_get_data_double(objtab, "X_coordinate");
473 double * yobj = cpl_table_get_data_double(objtab, "Y_coordinate");
474
475 cpl_size max_match = 0;
476 cpl_size match_std = -1;
477 cpl_size match_obj = -1;
478
479 for (cpl_size is = 0; is < nstd; is++) {
480 for (cpl_size io = 0; io < nobj; io++) {
481
482 cpl_matrix * distances = NULL;
483 enm_try_association(xstd, ystd, nstd,
484 xobj, yobj, nobj,
485 is, io, &distances);
486
487 /* match objects */
488
489 cpl_array * match_distances = cpl_array_new(0, CPL_TYPE_DOUBLE);
490
491 while (cpl_matrix_get_min(distances) < match_rad) {
492 cpl_size istd_min = 0;
493 cpl_size iobj_min = 0;
494 double dist = cpl_matrix_get_min(distances);
495 cpl_matrix_get_minpos(distances,
496 &istd_min,
497 &iobj_min);
498 cpl_matrix_fill_row(distances, 10000.0, istd_min);
499 cpl_matrix_fill_column(distances, 10000.0, iobj_min);
500 //cpl_msg_info(cpl_func, "std %d matches obj %d dist %f",
501 // (int)istd_min, (int)iobj_min, dist);
502 cpl_size next = cpl_array_get_size(match_distances);
503 cpl_array_set_size(match_distances, next+1);
504 cpl_array_set_double(match_distances, next, dist);
505 }
506
507 cpl_size nmatch = cpl_array_get_size(match_distances);
508 if (nmatch > max_match) {
509 max_match = nmatch;
510 match_std = is;
511 match_obj = io;
512 }
513
514 cpl_array_delete(match_distances);
515 cpl_matrix_delete(distances);
516 }
517 }
518 cpl_msg_info(cpl_func, " ..best match s=%d o=%d nmatch=%d match_rad=%e",
519 (int)match_std, (int)match_obj, (int)max_match, match_rad);
520 if (max_match == 1) {
521 cpl_msg_warning(cpl_func, "\033[0;31m"
522 "WCS calibration is unreliable because the pattern "
523 "match is based on a single source"
524 "\033[0m");
525 }
526
527 /* recalculate the catalogue association that gave the best match.
528 For this we want all the matched objects we can get so use the
529 full tables */
530
531 nstd = cpl_table_get_nrow(stdtab);
532 nobj = cpl_table_get_nrow(objtab);
533
534 cpl_matrix * distances = NULL;
535 enm_try_association(xstd, ystd, nstd,
536 xobj, yobj, nobj,
537 match_std, match_obj, &distances);
538
539 match_is = cpl_array_new(0, CPL_TYPE_INT);
540 match_io = cpl_array_new(0, CPL_TYPE_INT);
541
542 //cpl_matrix_dump(distances, NULL);
543
544 while (cpl_matrix_get_min(distances) < match_rad) {
545 cpl_size istd_min = 0;
546 cpl_size iobj_min = 0;
547 cpl_matrix_get_minpos(distances,
548 &istd_min,
549 &iobj_min);
550 cpl_matrix_fill_row(distances, 10000.0, istd_min);
551 cpl_matrix_fill_column(distances, 10000.0, iobj_min);
552 cpl_size nmatch = cpl_array_get_size(match_is);
553 cpl_array_set_size(match_is, nmatch+1);
554 cpl_array_set_int(match_is, nmatch, istd_min);
555 cpl_array_set_size(match_io, nmatch+1);
556 cpl_array_set_int(match_io, nmatch, iobj_min);
557 }
558 cpl_matrix_delete(distances);
559
560 /* Make a copy of the standards table and add all the columns
561 from the object catalogue that it doesn't already have */
562
563 mstds = cpl_table_duplicate(stdtab);
564 colnames = cpl_table_get_column_names(objtab);
565 cpl_size ncol = cpl_array_get_size(colnames);
566 for (cpl_size k = 0; k < ncol; k++) {
567 const char * colname = cpl_array_get_string(colnames, k);
568 if (!cpl_table_has_column(mstds, colname)) {
569 cpl_table_new_column(mstds, colname,
570 cpl_table_get_column_type(objtab, colname));
571 }
572 }
573
574 /* Now go through mstds (the matched standards catalogue) and set the
575 matches. NB: the columns in objtab
576 are created by casu_imcore which only produces integer,
577 float and double columns, so we don't need to worry about
578 any other type when copying the columns to the matched
579 standards catalogue. */
580
581 cpl_table_unselect_all(mstds);
582
583 for (cpl_size im = 0; im < cpl_array_get_size(match_is); im++) {
584 int ignore = 0;
585 cpl_size is = cpl_array_get_int(match_is, im, &ignore);
586
587 /* select the row in mstds and copy the column values except for
588 RA and DEC */
589
590 cpl_table_select_row(mstds, is);
591
592 cpl_size io = cpl_array_get_int(match_io, im, &ignore);
593
594 for (cpl_size k = 0; k < ncol; k++) {
595 const char * colname = cpl_array_get_string(colnames, k);
596 if (!strcmp(colname,"RA") || !strcmp(colname,"DEC")) {
597 continue;
598 }
599
600 cpl_type column_type = cpl_table_get_column_type(objtab,
601 colname);
602 switch (column_type) {
603 case CPL_TYPE_INT:
604 cpl_table_set_int(mstds, colname, is,
605 cpl_table_get_int(objtab,
606 colname, io, &ignore));
607 break;
608
609 case CPL_TYPE_FLOAT:
610 cpl_table_set_float(mstds, colname, is,
611 cpl_table_get_float(objtab,
612 colname, io, &ignore));
613 break;
614 case CPL_TYPE_DOUBLE:
615 cpl_table_set_double(mstds, colname, is,
616 cpl_table_get_double(objtab,
617 colname, io, &ignore));
618 break;
619 default:
620 cpl_error_set_message(funcid,
621 CPL_ERROR_UNSUPPORTED_MODE,
622 "column type not handled: %s",
623 cpl_type_get_name(column_type));
624 break;
625 }
626 }
627 }
628
629 /* Now extract the selected rows into the output table */
630
631 *matchtab = cpl_table_extract_selected(mstds);
632
633 //cpl_table_dump(*matchtab, 0, 100, NULL);
634
635 cleanup:
636
637 cpl_array_delete(colnames);
638 cpl_free(matches);
639 cpl_array_delete(match_is);
640 cpl_array_delete(match_io);
641 cpl_table_delete(mstds);
642
643 if (cpl_error_get_code() != CPL_ERROR_NONE) {
644 cpl_table_delete(*matchtab);
645 *matchtab = NULL;
646 }
647
648 return cpl_error_get_code();
649}
650
651
652/*----------------------------------------------------------------------------*/
675/*----------------------------------------------------------------------------*/
676
677cpl_error_code enm_try_association(const double xstd[],
678 const double ystd[],
679 const cpl_size nstd,
680 const double xobj[],
681 const double yobj[],
682 const cpl_size nobj,
683 const cpl_size is,
684 const cpl_size io,
685 cpl_matrix ** distances) {
686
687 if (cpl_error_get_code() != CPL_ERROR_NONE) return cpl_error_get_code();
688 cpl_ensure_code(xstd, CPL_ERROR_NULL_INPUT);
689 cpl_ensure_code(ystd, CPL_ERROR_NULL_INPUT);
690 cpl_ensure_code(xobj, CPL_ERROR_NULL_INPUT);
691 cpl_ensure_code(yobj, CPL_ERROR_NULL_INPUT);
692 cpl_ensure_code(is>=0 && is<nstd, CPL_ERROR_ACCESS_OUT_OF_RANGE);
693 cpl_ensure_code(io>=0 && io<nobj, CPL_ERROR_ACCESS_OUT_OF_RANGE);
694
695 //cpl_msg_info(cpl_func, "associate std=%d with obj=%d", (int)is, (int)io);
696
697 /* the implied shift if this was true */
698
699 double xshift = xstd[is] - xobj[io];
700 double yshift = ystd[is] - yobj[io];
701
702 /* match up objects */
703
704 *distances = cpl_matrix_new(nstd, nobj);
705 cpl_matrix_fill(*distances, 10000.0);
706
707 for (cpl_size iis = 0; iis < nstd; iis++) {
708 for (cpl_size iio = 0; iio < nobj; iio++) {
709
710 /* position object would have in std image */
711 double xobj_shifted = xobj[iio] + xshift;
712 double yobj_shifted = yobj[iio] + yshift;
713
714 /* distance between them? */
715 double xdist = xstd[iis] - xobj_shifted;
716 double ydist = ystd[iis] - yobj_shifted;
717 double dist = sqrt(xdist*xdist + ydist * ydist);
718
719 cpl_matrix_set(*distances, iis, iio, dist);
720 }
721 }
722
723 return cpl_error_get_code();
724}
725
cpl_error_code enm_correct_crpix(const char *wcs_method, const char *catalogue, cpl_table *matched_stds, located_image *limage, cpl_matrix **xy_shift)
Apply median offsets from matched standards table to image.
cpl_error_code enm_try_association(const double xstd[], const double ystd[], const cpl_size nstd, const double xobj[], const double yobj[], const cpl_size nobj, const cpl_size is, const cpl_size io, cpl_matrix **distances)
Method to calculate 'distances' matrix given an object association.
cpl_error_code enm_try_all_associations(cpl_table *objtab, cpl_table *stdtab, const double match_rad, cpl_table **matchtab)
Match standards with objects by trying all possibilities and selecting best.
cpl_error_code enm_associate_std(cpl_table *objtab, cpl_table *stdtab, const float assoc, const int strict_classification, cpl_table **associated_std)
Associate image catalogue objects with standards.