FORS Pipeline Reference Manual 4.9.20
|
00001 /* $Id: fors_identify.c,v 1.56 2012/01/27 18:53:56 cgarcia Exp $ 00002 * 00003 * This file is part of the FORS Library 00004 * Copyright (C) 2002-2010 European Southern Observatory 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00019 */ 00020 00021 /* 00022 * $Author: cgarcia $ 00023 * $Date: 2012/01/27 18:53:56 $ 00024 * $Revision: 1.56 $ 00025 * $Name: fors-4_9_20 $ 00026 */ 00027 00028 #ifdef HAVE_CONFIG_H 00029 #include <config.h> 00030 #endif 00031 00032 #include <fors_identify.h> 00033 00034 #include <fors_image.h> 00035 #include <fors_pattern.h> 00036 #include <fors_point.h> 00037 #include <fors_dfs.h> 00038 #include <fors_utils.h> 00039 #include <fors_double.h> 00040 00041 #include <cpl.h> 00042 00043 #include <math.h> 00044 #include <assert.h> 00045 00052 struct _identify_method 00053 { 00054 int ncat; 00055 double nsource; 00056 double kappa; 00057 double search; 00058 double max_search; 00059 double max_offset; 00060 }; 00061 00062 static bool 00063 inside_region(const fors_std_star *std, 00064 void *reg); 00065 00066 static void 00067 match_patterns(const fors_star_list *stars, 00068 const fors_std_star_list *std, 00069 double kappa, 00070 double *sx_00, 00071 double *sy_00, 00072 double *med_scale, 00073 double *med_angle, 00074 int *status); 00075 00081 void 00082 fors_identify_define_parameters(cpl_parameterlist *parameters, 00083 const char *context) 00084 { 00085 cpl_parameter *p; 00086 const char *full_name = NULL; 00087 const char *name; 00088 00089 /* Restore if pattern matching needs to be restored 00090 00091 name = "ncat"; 00092 full_name = cpl_sprintf("%s.%s", context, name); 00093 p = cpl_parameter_new_value(full_name, 00094 CPL_TYPE_INT, 00095 "Number of catalog stars to use in " 00096 "pattern matching", 00097 context, 00098 10); 00099 00100 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name); 00101 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00102 cpl_parameterlist_append(parameters, p); 00103 cpl_free((void *)full_name); 00104 00105 00106 name = "nsource"; 00107 full_name = cpl_sprintf("%s.%s", context, name); 00108 p = cpl_parameter_new_value(full_name, 00109 CPL_TYPE_DOUBLE, 00110 "Number of sources to use in " 00111 "pattern matching, pr. catalog star", 00112 context, 00113 15.0); 00114 00115 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name); 00116 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00117 cpl_parameterlist_append(parameters, p); 00118 cpl_free((void *)full_name); 00119 00120 name = "kappa"; 00121 full_name = cpl_sprintf("%s.%s", context, name); 00122 p = cpl_parameter_new_value(full_name, 00123 CPL_TYPE_DOUBLE, 00124 "Rejection parameter (scale, angle) used " 00125 "in pattern matching ", 00126 context, 00127 5.0); 00128 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name); 00129 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00130 cpl_parameterlist_append(parameters, p); 00131 cpl_free((void *)full_name); 00132 00133 End of restoring if pattern matching has to be restored */ 00134 00135 /* 00136 00137 name = "search"; 00138 full_name = cpl_sprintf("%s.%s", context, name); 00139 p = cpl_parameter_new_value(full_name, 00140 CPL_TYPE_DOUBLE, 00141 "Search radius (pixels)", 00142 context, 00143 5.0); 00144 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name); 00145 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00146 cpl_parameterlist_append(parameters, p); 00147 cpl_free((void *)full_name); 00148 00149 name = "maxsearch"; 00150 full_name = cpl_sprintf("%s.%s", context, name); 00151 p = cpl_parameter_new_value(full_name, 00152 CPL_TYPE_DOUBLE, 00153 "Maximum search radius (pixels)", 00154 context, 00155 20.0); 00156 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name); 00157 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00158 cpl_parameterlist_append(parameters, p); 00159 cpl_free((void *)full_name); 00160 00161 */ 00162 00163 name = "maxoffset"; 00164 full_name = cpl_sprintf("%s.%s", context, name); 00165 p = cpl_parameter_new_value(full_name, 00166 CPL_TYPE_DOUBLE, 00167 "Maximum acceptable offset between the image and catalogue WCS (pixels)", 00168 context, 00169 150.0); 00170 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name); 00171 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00172 cpl_parameterlist_append(parameters, p); 00173 cpl_free((void *)full_name); 00174 00175 return; 00176 } 00177 00178 #undef cleanup 00179 #define cleanup \ 00180 do { \ 00181 cpl_free((void *)name); \ 00182 } while (0) 00183 00192 identify_method * 00193 fors_identify_method_new(const cpl_parameterlist *parameters, const char *context) 00194 { 00195 identify_method *im = cpl_malloc(sizeof(*im)); 00196 const char *name = NULL; 00197 00198 cpl_msg_info(cpl_func, "Identification parameters:"); 00199 00200 /* Restore if pattern matching to be restored 00201 00202 cpl_msg_indent_more(); 00203 name = cpl_sprintf("%s.%s", context, "ncat"); 00204 im->ncat = dfs_get_parameter_int_const(parameters, name); 00205 cpl_free((void *)name); name = NULL; 00206 cpl_msg_indent_less(); 00207 00208 00209 cpl_msg_indent_more(); 00210 name = cpl_sprintf("%s.%s", context, "nsource"); 00211 im->nsource = dfs_get_parameter_double_const(parameters, name); 00212 cpl_free((void *)name); name = NULL; 00213 cpl_msg_indent_less(); 00214 00215 00216 cpl_msg_indent_more(); 00217 name = cpl_sprintf("%s.%s", context, "kappa"); 00218 im->kappa = dfs_get_parameter_double_const(parameters, name); 00219 cpl_free((void *)name); name = NULL; 00220 cpl_msg_indent_less(); 00221 00222 End of restore if pattern matching to be restored */ 00223 00224 00225 // cpl_msg_indent_more(); 00226 // name = cpl_sprintf("%s.%s", context, "search"); 00227 im->search = 5.0; // dfs_get_parameter_double_const(parameters, name); 00228 // cpl_free((void *)name); name = NULL; 00229 // cpl_msg_indent_less(); 00230 00231 00232 // cpl_msg_indent_more(); 00233 // name = cpl_sprintf("%s.%s", context, "maxsearch"); 00234 // name = cpl_sprintf("%s.%s", context, "search"); // Same value for max search 00235 im->max_search = 5.0; // dfs_get_parameter_double_const(parameters, name); 00236 // cpl_free((void *)name); name = NULL; 00237 // cpl_msg_indent_less(); 00238 00239 00240 cpl_msg_indent_more(); 00241 name = cpl_sprintf("%s.%s", context, "maxoffset"); 00242 im->max_offset = dfs_get_parameter_double_const(parameters, name); 00243 cpl_free((void *)name); name = NULL; 00244 cpl_msg_indent_less(); 00245 00246 00247 assure( !cpl_error_get_code(), return NULL, NULL ); 00248 00249 return im; 00250 } 00251 00255 void 00256 fors_identify_method_delete(identify_method **em) 00257 { 00258 if (em && *em) { 00259 cpl_free(*em); *em = NULL; 00260 } 00261 return; 00262 } 00263 00273 static bool 00274 std_brighter_than(const fors_std_star *s1, 00275 void *s2) 00276 { 00277 return fors_std_star_brighter_than(s1, s2, NULL); 00278 } 00279 00289 static bool 00290 star_brighter_than(const fors_star *s1, 00291 void *s2) 00292 { 00293 return fors_star_brighter_than(s1, s2, NULL); 00294 } 00295 00304 static double 00305 distsq_shift(const fors_star *s, 00306 const fors_std_star *std, 00307 double shiftx, 00308 double shifty) 00309 { 00310 fors_point *shifted_pos = fors_point_new(std->pixel->x + shiftx, 00311 std->pixel->y + shifty); 00312 00313 double result = fors_point_distsq(s->pixel, shifted_pos); 00314 00315 fors_point_delete(&shifted_pos); 00316 00317 return result; 00318 } 00319 00328 static bool 00329 star_nearer(const fors_star *s1, 00330 const fors_star *s2, 00331 void *data) 00332 { 00333 struct { 00334 double shift_x, shift_y; 00335 const fors_std_star *ref; 00336 } *d = data; 00337 /* Cast is safe, see caller */ 00338 00339 return 00340 distsq_shift(s1, d->ref, d->shift_x, d->shift_y) < 00341 distsq_shift(s2, d->ref, d->shift_x, d->shift_y); 00342 } 00343 00344 00345 #undef cleanup 00346 #define cleanup \ 00347 do { \ 00348 fors_std_star_list_delete(&std_ccd , fors_std_star_delete); \ 00349 fors_std_star_list_delete(&std_ccd_bright, fors_std_star_delete); \ 00350 fors_star_list_delete(&source_bright, fors_star_delete); \ 00351 } while (0) 00352 00371 void 00372 fors_identify(fors_star_list *stars, 00373 fors_std_star_list *cat, 00374 const identify_method *im, 00375 cpl_image **histogram) 00376 { 00377 fors_std_star_list *std_ccd = NULL; /* Subset of catalog stars 00378 which are inside the CCD */ 00379 00380 fors_std_star_list *std_ccd_bright = NULL; /* Brightest std stars */ 00381 00382 fors_star_list *source_bright = NULL; /* Subset of brightest stars */ 00383 00384 double offset = 0.0; 00385 double sx_00, sy_00; 00386 int status; 00387 00388 if (histogram) 00389 *histogram = NULL; 00390 00391 assure( stars != NULL, return, NULL ); 00392 00393 cpl_msg_info(cpl_func, "Identifying sources"); 00394 cpl_msg_indent_more(); 00395 00396 00397 /* 00398 fors_star_print_list(CPL_MSG_ERROR, stars); 00399 fors_std_star_print_list(CPL_MSG_ERROR, cat); 00400 */ 00401 00402 cpl_msg_info(cpl_func, "Pattern matching"); 00403 cpl_msg_indent_more(); 00404 00405 /* Select standards inside CCD */ 00406 { 00407 double tolerance = 100; /* pixels */ 00408 struct { 00409 int xlo, ylo; 00410 int xhi, yhi; 00411 } region; 00412 if (fors_star_list_size(stars) > 0) { 00413 region.xlo = fors_star_get_x(fors_star_list_min_val(stars, 00414 fors_star_get_x, 00415 NULL), NULL) - tolerance; 00416 region.ylo = fors_star_get_y(fors_star_list_min_val(stars, 00417 fors_star_get_y, 00418 NULL), NULL) - tolerance; 00419 region.xhi = fors_star_get_x(fors_star_list_max_val(stars, 00420 fors_star_get_x, 00421 NULL), NULL) + tolerance; 00422 region.yhi = fors_star_get_y(fors_star_list_max_val(stars, 00423 fors_star_get_y, 00424 NULL), NULL) + tolerance; 00425 00426 } else { 00427 region.xlo = 1; 00428 region.ylo = 1; 00429 region.xhi = 1000; /* Anything can go here, not used */ 00430 region.yhi = 1000; 00431 } 00432 cpl_msg_debug(cpl_func, "Search region = (%d, %d) - (%d, %d)", 00433 region.xlo, region.ylo, 00434 region.xhi, region.yhi); 00435 00436 std_ccd = fors_std_star_list_extract( 00437 cat, fors_std_star_duplicate, inside_region, ®ion); 00438 00439 int multiple_entries = 0; 00440 00441 if (fors_std_star_list_size(std_ccd) > 1) { 00442 bool found_double; 00443 do { 00444 found_double = false; 00445 00446 /* Searching for the nearest neighbour will permute the list, 00447 do not do that while iterating the same list */ 00448 fors_std_star_list *tmp = 00449 fors_std_star_list_duplicate(std_ccd, 00450 fors_std_star_duplicate); 00451 00452 cpl_msg_debug(cpl_func, "%d stars left", fors_std_star_list_size(tmp)); 00453 00454 fors_std_star *std; 00455 00456 for (std = fors_std_star_list_first(tmp); 00457 std != NULL && !found_double; 00458 std = fors_std_star_list_next(tmp)) { 00459 00460 fors_std_star *self = fors_std_star_list_kth_val( 00461 std_ccd, 1, 00462 (fors_std_star_list_func_eval) 00463 fors_std_star_dist_arcsec, 00464 std); 00465 00466 fors_std_star *nn = fors_std_star_list_kth_val( 00467 std_ccd, 2, 00468 (fors_std_star_list_func_eval) 00469 fors_std_star_dist_arcsec, 00470 std); 00471 00472 double min_dist = fors_std_star_dist_arcsec(std, nn); 00473 00474 cpl_msg_debug(cpl_func, "dist = %f arcseconds", min_dist); 00475 00476 /* If very close, remove the one with the largest magnitude 00477 error. 00478 00479 Do not try to combine the two magnitudes because 00480 those estimates may or may not be correlated 00481 */ 00482 if (min_dist < 5) { 00483 multiple_entries += 1; 00484 00485 if (std->dmagnitude > nn->dmagnitude) { 00486 fors_std_star_list_remove(std_ccd, self); 00487 fors_std_star_delete(&self); 00488 } else { 00489 fors_std_star_list_remove(std_ccd, nn); 00490 fors_std_star_delete(&nn); 00491 } 00492 found_double = true; 00493 } 00494 } 00495 00496 fors_std_star_list_delete(&tmp, 00497 fors_std_star_delete); 00498 00499 } while (found_double); 00500 } 00501 00502 cpl_msg_info(cpl_func, 00503 "%d catalog star%s are expected inside detector, " 00504 "ignored %d repeated source%s", 00505 fors_std_star_list_size(std_ccd), 00506 fors_std_star_list_size(std_ccd) == 1 ? "" : "s", 00507 multiple_entries, 00508 multiple_entries == 1 ? "" : "s"); 00509 } 00510 00511 00512 if (0) { 00513 /* Select brightest std */ 00514 if (fors_std_star_list_size(std_ccd) <= im->ncat) { 00515 std_ccd_bright = fors_std_star_list_duplicate(std_ccd, 00516 fors_std_star_duplicate); 00517 } 00518 else { 00519 fors_std_star *kth = 00520 fors_std_star_list_kth(std_ccd, 00521 im->ncat + 1, 00522 fors_std_star_brighter_than, NULL); 00523 00524 //fors_std_star_print_list(std_ccd); 00525 00526 std_ccd_bright = fors_std_star_list_extract( 00527 std_ccd, fors_std_star_duplicate, 00528 std_brighter_than, kth); 00529 } 00530 00531 if (fors_std_star_list_size(std_ccd_bright) < 3) { 00532 00533 cpl_msg_warning(cpl_func, 00534 "Too few catalog stars (%d) available for pattern " 00535 "matching, assuming FITS header WCS solution", 00536 fors_std_star_list_size(std_ccd_bright)); 00537 00538 sx_00 = 0; 00539 sy_00 = 0; 00540 } 00541 else { 00542 00543 double med_scale, med_angle; 00544 00545 cpl_msg_info(cpl_func, "Using %d brightest standards", 00546 fors_std_star_list_size(std_ccd_bright)); 00547 00548 fors_std_star_print_list(CPL_MSG_DEBUG, std_ccd_bright); 00549 00550 /* Select sources */ 00551 int n_sources = 00552 (int) (fors_std_star_list_size(std_ccd_bright)*im->nsource + 0.5); 00553 00554 if (fors_star_list_size(stars) <= n_sources) { 00555 source_bright = fors_star_list_duplicate(stars, 00556 fors_star_duplicate); 00557 } 00558 else { 00559 fors_star *kth = 00560 fors_star_list_kth(stars, 00561 n_sources + 1, 00562 fors_star_brighter_than, NULL); 00563 00564 source_bright = fors_star_list_extract( 00565 stars, fors_star_duplicate, 00566 star_brighter_than, kth); 00567 } 00568 00569 cpl_msg_info(cpl_func, "Using %d brightest sources", 00570 fors_star_list_size(source_bright)); 00571 fors_star_print_list(CPL_MSG_DEBUG, source_bright); 00572 00573 00574 status = 0; 00575 match_patterns(source_bright, std_ccd_bright, 00576 im->kappa, 00577 &sx_00, &sy_00, &med_scale, &med_angle, &status); 00578 00579 assure( !cpl_error_get_code(), return, "Pattern matching failed" ); 00580 00581 00582 if (status) { 00583 cpl_msg_warning(cpl_func, 00584 "BAD pattern matching solution rejected."); 00585 00586 if (med_scale > 1.1 || med_scale < 0.9) { 00587 cpl_msg_warning(cpl_func, "Unexpected scale from pattern " 00588 "matching (expected 1.0): assuming FITS header WCS solution"); 00589 } 00590 00591 offset = sqrt(sx_00 * sx_00 + sy_00 * sy_00); 00592 00593 if (offset > im->max_offset) { 00594 cpl_msg_warning(cpl_func, "Pattern matching identifications " 00595 "are more than %.2f pixel off their expected positions (max " 00596 "allowed was: %.2f). Pattern matching solution is rejected, " 00597 "FITS header WCS solution is used instead!", offset, 00598 im->max_offset); 00599 } 00600 00601 sx_00 = 0; 00602 sy_00 = 0; 00603 } 00604 } 00605 cpl_msg_indent_less(); 00606 00607 cpl_msg_info(cpl_func, 00608 "Average shift from pattern matching = (%.2f, %.2f) pixels", 00609 sx_00, sy_00); 00610 } 00611 00612 /* 00613 * Begin here alternative algorithm to pattern matching 00614 */ 00615 00616 double search_radius = im->max_offset; 00617 const fors_std_star *catalog_star; 00618 const fors_star *ccd_star; 00619 float dx, dy; 00620 int npix = (2 * search_radius + 1); 00621 cpl_image *histo = cpl_image_new(npix, npix, CPL_TYPE_INT); 00622 int *dhisto = cpl_image_get_data(histo); 00623 cpl_size xpos, ypos; 00624 int i, rebin, max; 00625 int found = 0; 00626 00627 for (catalog_star = fors_std_star_list_first_const(std_ccd); 00628 catalog_star != NULL; 00629 catalog_star = fors_std_star_list_next_const(std_ccd)) { 00630 00631 for (ccd_star = fors_star_list_first_const(stars); 00632 ccd_star != NULL; 00633 ccd_star = fors_star_list_next_const(stars)) { 00634 00635 dx = catalog_star->pixel->x - ccd_star->pixel->x; 00636 dy = catalog_star->pixel->y - ccd_star->pixel->y; 00637 00638 if (fabs(dx) < search_radius) { 00639 if (fabs(dy) < search_radius) { 00640 xpos = search_radius + floor(dx + 0.5); 00641 ypos = search_radius + floor(dy + 0.5); 00642 ++dhisto[xpos + ypos * npix]; 00643 } 00644 } 00645 } 00646 } 00647 00648 max = 0; 00649 for (i = 0; i < npix * npix; i++) { 00650 if (max < dhisto[i]) { 00651 max = dhisto[i]; 00652 } 00653 } 00654 00655 if (max == 0) { 00656 cpl_msg_warning(cpl_func, 00657 "WCS offset determination failed."); 00658 sx_00 = 0.0; 00659 sy_00 = 0.0; 00660 } 00661 else { 00662 rebin = 0; 00663 for (i = 0; i < npix * npix; i++) { 00664 if (max == dhisto[i]) { 00665 if (found) { 00666 cpl_image *rebinned; 00667 found = 2; 00668 rebin = 1; 00669 cpl_msg_warning(cpl_func, 00670 "More than one WCS offset found, try rebinning..."); 00671 rebinned = cpl_image_rebin(histo, 1, 1, 3, 3); 00672 cpl_image_delete(histo); 00673 histo = rebinned; 00674 dhisto = cpl_image_get_data(histo); 00675 npix = cpl_image_get_size_x(histo); 00676 search_radius /= 3; 00677 break; 00678 } 00679 else { 00680 found = 1; 00681 } 00682 } 00683 } 00684 00685 if (found > 1) { 00686 found = max = 0; 00687 for (i = 0; i < npix * npix; i++) { 00688 if (max < dhisto[i]) { 00689 max = dhisto[i]; 00690 } 00691 } 00692 00693 for (i = 0; i < npix * npix; i++) { 00694 if (max == dhisto[i]) { 00695 if (found) { 00696 00697 /* 00698 * Check if connected to previously found maxima 00699 */ 00700 00701 int connected = 0; 00702 00703 if (i > 0) { 00704 if (max == dhisto[i-1]) { 00705 connected = 1; 00706 } 00707 } 00708 if (i > npix) { 00709 if (max == dhisto[i-npix-1] || 00710 max == dhisto[i-npix] || 00711 max == dhisto[i-npix+1]) { 00712 connected = 1; 00713 } 00714 } 00715 00716 if (!connected) { 00717 found = 2; 00718 cpl_msg_warning(cpl_func, 00719 "WCS offset determination failed."); 00720 sx_00 = 0.0; 00721 sy_00 = 0.0; 00722 break; 00723 } 00724 } 00725 else { 00726 found = 1; 00727 } 00728 } 00729 } 00730 } 00731 } 00732 00733 if (found == 1) { 00734 cpl_image_get_maxpos(histo, &xpos, &ypos); 00735 xpos--; 00736 ypos--; 00737 00738 /* 00739 * Refine peak position 00740 */ 00741 00742 float xmean = 0.0; 00743 float ymean = 0.0; 00744 int i, j, count = 0; 00745 00746 for (i = xpos - 1; i <= xpos; i++) { 00747 for (j = ypos - 1; j <= ypos; j++) { 00748 if (i >= 0 && i < npix) { 00749 if (j >= 0 && j < npix) { 00750 xmean += i * dhisto[i + j*npix]; 00751 ymean += j * dhisto[i + j*npix]; 00752 count += dhisto[i + j*npix]; 00753 } 00754 } 00755 } 00756 } 00757 00758 sx_00 = search_radius - xmean / count - 0.5; 00759 sy_00 = search_radius - ymean / count - 0.5; 00760 00761 if (rebin) { 00762 sx_00 *= 3; 00763 sy_00 *= 3; 00764 } 00765 00766 /* 00767 cpl_msg_info(cpl_func, 00768 "Average shift from histogram method = (%.2f, %.2f) pixels", 00769 sx_00, sy_00); 00770 */ 00771 00772 // cpl_image_save(histo, "histogram.fits", CPL_BPP_32_SIGNED, NULL, 00773 // CPL_IO_DEFAULT); 00774 // 00775 // cpl_image_delete(histo); 00776 } 00777 00778 if (histogram) 00779 *histogram = histo; 00780 else 00781 cpl_image_delete(histo); 00782 00783 offset = sqrt(sx_00 * sx_00 + sy_00 * sy_00); 00784 00785 if (offset > im->max_offset) { 00786 cpl_msg_warning(cpl_func, "Offset with respect to WCS is %.2f pixel " 00787 "(max allowed was: %.2f). This offset is rejected.", 00788 offset, im->max_offset); 00789 00790 sx_00 = 0; 00791 sy_00 = 0; 00792 } 00793 00794 /* 00795 * End here alternative algorithm to pattern matching 00796 */ 00797 00798 /* Finally, make the identification if a unique source is found 00799 within the corrected position search radius. 00800 00801 Use all catalog stars (inside CCD). 00802 */ 00803 00804 if (sx_00 == 0.0 && sy_00 == 0.0) { 00805 cpl_msg_warning(cpl_func, "No standard star could be identified."); 00806 cpl_msg_indent_less(); 00807 cleanup; 00808 return; 00809 } 00810 00811 int number_of_ids = 0; 00812 bool require_unique = true; 00813 search_radius = im->search; 00814 00815 while (number_of_ids == 0 && search_radius <= im->max_search) { 00816 00817 cpl_msg_info(cpl_func, "Identification"); 00818 00819 cpl_msg_indent_more(); 00820 cpl_msg_info(cpl_func, "Average shift with WCS = (%.2f, %.2f) pixels", 00821 sx_00, 00822 sy_00); 00823 00824 if (fabs(sx_00) > 10.0) { 00825 cpl_msg_warning(cpl_func, "Large x-shift"); 00826 } 00827 if (fabs(sy_00) > 10.0) { 00828 cpl_msg_warning(cpl_func, "Large y-shift"); 00829 } 00830 00831 cpl_msg_info(cpl_func, "search_radius = %.2f pixels", search_radius); 00832 00833 struct { 00834 double shift_x, shift_y; 00835 const fors_std_star *ref; 00836 } data; 00837 00838 data.shift_x = sx_00; 00839 data.shift_y = sy_00; 00840 00841 if (fors_star_list_size(stars) > 0) { 00842 for (data.ref = fors_std_star_list_first_const(std_ccd); 00843 data.ref != NULL; 00844 data.ref = fors_std_star_list_next_const(std_ccd)) { 00845 00846 fors_star *nearest_1 = fors_star_list_kth(stars, 00847 1, 00848 star_nearer, 00849 &data); 00850 00851 if (fors_star_list_size(stars) > 1) { 00852 00853 fors_star *nearest_2 = fors_star_list_kth(stars, 00854 2, 00855 star_nearer, 00856 &data); 00857 00858 cpl_msg_debug(cpl_func, "Nearest candidates at " 00859 "distance %f and %f pixels", 00860 sqrt(distsq_shift(nearest_1, data.ref, 00861 data.shift_x, 00862 data.shift_y)), 00863 sqrt(distsq_shift(nearest_2, data.ref, 00864 data.shift_x, 00865 data.shift_y))); 00866 00867 if (distsq_shift(nearest_1, data.ref, 00868 data.shift_x, data.shift_y) <= 00869 search_radius * search_radius) { 00870 00871 if (!require_unique || 00872 distsq_shift(nearest_2, data.ref, 00873 data.shift_x, data.shift_y) > 00874 search_radius * search_radius) { 00875 00876 cpl_msg_debug(cpl_func, 00877 "unique source inside %f pixels", 00878 search_radius); 00879 00880 nearest_1->id = fors_std_star_duplicate(data.ref); 00881 number_of_ids += 1; 00882 } 00883 } 00884 } 00885 else { 00886 cpl_msg_debug(cpl_func, "Nearest candidate at " 00887 "distance %f pixels", 00888 sqrt(distsq_shift(nearest_1, data.ref, 00889 data.shift_x, 00890 data.shift_y))); 00891 if (distsq_shift(nearest_1, data.ref, 00892 data.shift_x, data.shift_y) <= 00893 search_radius * search_radius) { 00894 00895 cpl_msg_debug(cpl_func, 00896 "unique source inside %f pixels", 00897 search_radius); 00898 00899 nearest_1->id = fors_std_star_duplicate(data.ref); 00900 number_of_ids += 1; 00901 } 00902 } 00903 } 00904 } 00905 00906 cpl_msg_info(cpl_func, "Identified %d star%s", 00907 number_of_ids, (number_of_ids == 1) ? "" : "s"); 00908 00909 if (number_of_ids == 0) { 00910 00911 if (fabs(sx_00) > 0.1 && 00912 fabs(sy_00) > 0.1) { 00913 00914 cpl_msg_debug(cpl_func, 00915 "No identifications made, " 00916 "set shift to zero and try again"); 00917 search_radius = 20; 00918 require_unique = false; 00919 00920 sx_00 = 0; 00921 sy_00 = 0; 00922 } 00923 else { 00924 require_unique = false; 00925 00926 search_radius *= 2; 00927 00928 cpl_msg_debug(cpl_func, 00929 "No identifications made, " 00930 "double search radius and try again"); 00931 } 00932 } 00933 00934 cpl_msg_indent_less(); 00935 00936 } /* while no identifications made */ 00937 00938 if (number_of_ids == 0) { 00939 cpl_msg_warning(cpl_func, 00940 "No identifications made, " 00941 "within search radius %f pixels", 00942 im->max_search); 00943 /* When maxsearchradius was a parameter 00944 00945 cpl_msg_warning(cpl_func, 00946 "No identifications made, " 00947 "max search radius reached: %f pixels", 00948 im->max_search); 00949 */ 00950 } 00951 else { 00952 /* Sketch to recompute shift: 00953 * Get median shifts (in x and y) of identified stars. 00954 * Then do the equivalent of a single tour through the while() loop above 00955 * 00956 * This could perhaps be unified with the code above to avoid duplication 00957 */ 00958 } 00959 00960 00961 cpl_msg_indent_less(); 00962 00963 cleanup; 00964 return; 00965 } 00966 00975 static bool 00976 inside_region(const fors_std_star *std, 00977 void *reg) 00978 { 00979 const struct { 00980 int xlo, ylo; 00981 int xhi, yhi; 00982 } *region = reg; 00983 00984 return 00985 region->xlo <= std->pixel->x && std->pixel->x <= region->xhi && 00986 region->ylo <= std->pixel->y && std->pixel->y <= region->yhi; 00987 } 00988 00989 00990 00991 #undef cleanup 00992 #define cleanup \ 00993 do { \ 00994 fors_point_list_delete(&std_points, fors_point_delete); \ 00995 fors_point_list_delete(&source_points, fors_point_delete); \ 00996 fors_pattern_list_delete(&std_patterns, fors_pattern_delete); \ 00997 fors_pattern_list_delete(&source_patterns, fors_pattern_delete); \ 00998 double_list_delete(&scales, double_delete); \ 00999 double_list_delete(&angles, double_delete); \ 01000 double_list_delete(&angle_cos, double_delete); \ 01001 double_list_delete(&angle_sin, double_delete); \ 01002 double_list_delete(&match_dist, double_delete); \ 01003 double_list_delete(&shiftx, double_delete); \ 01004 double_list_delete(&shifty, double_delete); \ 01005 } while (0) 01006 01022 static void 01023 match_patterns(const fors_star_list *stars, 01024 const fors_std_star_list *std, 01025 double kappa, 01026 double *sx_00, 01027 double *sy_00, 01028 double *med_scale, 01029 double *med_angle, 01030 int *status) 01031 { 01032 fors_point_list *std_points = NULL; 01033 fors_point_list *source_points = NULL; 01034 01035 fors_pattern_list *std_patterns = NULL; 01036 fors_pattern_list *source_patterns = NULL; 01037 01038 double_list *scales = NULL; 01039 double_list *angles = NULL; 01040 double_list *angle_cos = NULL; 01041 double_list *angle_sin = NULL; 01042 double_list *match_dist = NULL; 01043 double_list *shiftx = NULL; 01044 double_list *shifty = NULL; 01045 01046 *status = 0; 01047 01048 assure( sx_00 != NULL, return, NULL ); 01049 assure( sy_00 != NULL, return, NULL ); 01050 01051 /* Build patterns */ 01052 std_points = fors_point_list_new(); 01053 { 01054 const fors_std_star *s; 01055 01056 for (s = fors_std_star_list_first_const(std); 01057 s != NULL; 01058 s = fors_std_star_list_next_const(std)) { 01059 01060 fors_point_list_insert(std_points, 01061 fors_point_new(s->pixel->x, 01062 s->pixel->y)); 01063 } 01064 } 01065 01066 source_points = fors_point_list_new(); 01067 { 01068 const fors_star *s; 01069 01070 for (s = fors_star_list_first_const(stars); 01071 s != NULL; 01072 s = fors_star_list_next_const(stars)) { 01073 01074 fors_point_list_insert(source_points, 01075 fors_point_new(s->pixel->x, 01076 s->pixel->y)); 01077 } 01078 } 01079 01080 const double min_dist = 1.0; /* minimum distance between two 01081 points in a pattern */ 01082 double sigma_std = 0.0; /* No uncertainty of catalog patterns */ 01083 std_patterns = 01084 fors_pattern_new_from_points(std_points, min_dist, sigma_std); 01085 cpl_msg_info(cpl_func, "Created %d catalog patterns", 01086 fors_pattern_list_size(std_patterns)); 01087 01088 double sigma_source; 01089 if (fors_star_list_size(stars) > 0) { 01090 sigma_source = fors_star_list_median(stars, 01091 fors_star_extension, NULL); 01092 cpl_msg_info(cpl_func, "Average source extension = %.2f pixels", sigma_source); 01093 } else { 01094 sigma_source = -1; /* not used in this case */ 01095 } 01096 source_patterns = 01097 fors_pattern_new_from_points(source_points, min_dist, sigma_source); 01098 01099 cpl_msg_info(cpl_func, "Created %d source patterns", 01100 fors_pattern_list_size(source_patterns)); 01101 01102 scales = double_list_new(); 01103 angles = double_list_new(); 01104 angle_cos = double_list_new(); 01105 angle_sin = double_list_new(); 01106 match_dist = double_list_new(); 01107 01108 if ( fors_pattern_list_size(source_patterns) > 0) { 01109 /* Identify, 01110 get average scale, orientation */ 01111 fors_pattern *p; 01112 01113 for (p = fors_pattern_list_first(std_patterns); 01114 p != NULL; 01115 p = fors_pattern_list_next(std_patterns)) { 01116 01117 fors_pattern *nearest_source = 01118 fors_pattern_list_min_val(source_patterns, 01119 (fors_pattern_list_func_eval) fors_pattern_distsq, 01120 p); 01121 01122 double scale = fors_pattern_get_scale(p, nearest_source); 01123 double angle = fors_pattern_get_angle(p, nearest_source); 01124 double angle_c = cos(angle); 01125 double angle_s = sin(angle); 01126 double dist = sqrt(fors_pattern_distsq(p, nearest_source)); 01127 double dist_per_error = fors_pattern_dist_per_error(p, nearest_source); 01128 01129 cpl_msg_debug(cpl_func, "dist, ndist, scale, orientation = %f, %f, %f, %f", 01130 dist, dist_per_error, scale, angle * 360/(2*M_PI)); 01131 01132 /* Make identification if patterns match within error bars 01133 (defined above as sigma_source) 01134 */ 01135 if (dist_per_error < 1.0) { 01136 double_list_insert(scales , double_duplicate(&scale)); 01137 double_list_insert(angles , double_duplicate(&angle)); 01138 double_list_insert(angle_cos, double_duplicate(&angle_c)); 01139 double_list_insert(angle_sin, double_duplicate(&angle_s)); 01140 double_list_insert(match_dist, double_duplicate(&dist)); 01141 } 01142 } 01143 } 01144 else { 01145 /* no source patterns to match */ 01146 } 01147 01148 if ( double_list_size(scales) >= 2 ) { 01149 double scale_avg = double_list_median(scales, double_eval, NULL); 01150 double scale_stdev = double_list_mad(scales, double_eval, NULL) * STDEV_PR_MAD; 01151 01152 cpl_msg_info(cpl_func, "Median scale = %.4f +- %.4f", 01153 scale_avg, scale_stdev); 01154 01155 *med_scale = scale_avg; 01156 01157 if (scale_stdev > 0.2) { 01158 cpl_msg_warning(cpl_func, "Uncertain scale determination"); 01159 *status = 1; 01160 } 01161 01162 /* 01163 * Represent each angle as a unit vector, compute average 01164 * unit vector orientation. 01165 * Compute median absolute deviation with respect to this average 01166 */ 01167 double angle_avg = atan2(double_list_mean(angle_sin, double_eval, NULL), 01168 double_list_mean(angle_cos, double_eval, NULL)); 01169 double angle_stdev = STDEV_PR_MAD * 01170 double_list_median(angles, (double_list_func_eval) fors_angle_diff, &angle_avg); 01171 01172 cpl_msg_info(cpl_func, "Average orientation = %.1f +- %.1f degrees", 01173 angle_avg * 360 / (2*M_PI), 01174 angle_stdev * 360 / (2*M_PI)); 01175 01176 *med_angle = angle_avg; 01177 01178 if (angle_avg > M_PI/4 || angle_avg < -M_PI/4) { 01179 cpl_msg_warning(cpl_func, "Expected orientation = 0 degrees"); 01180 *status = 1; 01181 /* To model any orientation, we should use higher order (than zero) polynomials 01182 for the shifts */ 01183 } 01184 01185 double avg_dist = double_list_mean(match_dist, double_eval, NULL); 01186 double false_dist = 1.0/sqrt(M_PI * fors_pattern_list_size(source_patterns)); 01187 /* Average distance to nearest false match */ 01188 01189 cpl_msg_info(cpl_func, "Average match distance = %f pixel", avg_dist); 01190 cpl_msg_info(cpl_func, "False match distance = %f pixel", false_dist); 01191 cpl_msg_info(cpl_func, "Safety index = %.3f (should be >~ 5)", 01192 false_dist / avg_dist); 01193 if (false_dist / avg_dist < 1.5) { 01194 cpl_msg_warning(cpl_func, "Uncertain pattern matching"); 01195 *status = 1; 01196 } 01197 01198 /* Get shift from matches */ 01199 shiftx = double_list_new(); 01200 shifty = double_list_new(); 01201 { 01202 fors_pattern *p; 01203 01204 for (p = fors_pattern_list_first(std_patterns); 01205 p != NULL; 01206 p = fors_pattern_list_next(std_patterns)) { 01207 01208 fors_pattern *nearest_source = 01209 fors_pattern_list_min_val( 01210 source_patterns, 01211 (fors_pattern_list_func_eval) fors_pattern_distsq, 01212 p); 01213 01214 double dist = sqrt(fors_pattern_distsq(p, nearest_source)); 01215 double dist_per_error = fors_pattern_dist_per_error(p, nearest_source); 01216 double scale = fors_pattern_get_scale(p, nearest_source); 01217 double angle = fors_pattern_get_angle(p, nearest_source); 01218 01219 cpl_msg_debug(cpl_func, "scale, orientation, distance, norm.distance " 01220 "= %f, %f, %f, %f", 01221 scale, angle * 360/(2*M_PI), dist, dist_per_error); 01222 01223 if (dist_per_error < 1.0 && 01224 fabs(scale - scale_avg) <= kappa * scale_stdev && 01225 fors_angle_diff(&angle, &angle_avg) <= kappa * angle_stdev) { 01226 01227 /* Compute shift of the two patterns' reference stars */ 01228 double shift_x = fors_pattern_get_ref(nearest_source)->x - fors_pattern_get_ref(p)->x; 01229 double shift_y = fors_pattern_get_ref(nearest_source)->y - fors_pattern_get_ref(p)->y; 01230 01231 cpl_msg_debug(cpl_func, "Accepted, shift = (%f, %f) pixels", 01232 shift_x, shift_y); 01233 01234 double_list_insert(shiftx, double_duplicate(&shift_x)); 01235 double_list_insert(shifty, double_duplicate(&shift_y)); 01236 } 01237 } 01238 } 01239 01240 if (double_list_size(shiftx) > 0) { 01241 *sx_00 = double_list_median(shiftx, double_eval, NULL); 01242 } 01243 else { 01244 /* If this happens it is likely a bug, because 01245 we already checked that 'scales' is non-empty. 01246 01247 But do not try to get the median of an empty list in any case, 01248 which would cause a crash 01249 */ 01250 cpl_msg_warning(cpl_func, "No standardstar identifications!"); 01251 *status = 1; 01252 } 01253 01254 if (double_list_size(shiftx) > 0) { 01255 *sy_00 = double_list_median(shifty, double_eval, NULL); 01256 } 01257 else { 01258 cpl_msg_warning(cpl_func, "No standardstar identifications!"); 01259 *status = 1; 01260 } 01261 } 01262 else { 01263 cpl_msg_warning(cpl_func, 01264 "Too few (%d) matching patterns: assuming zero shift", 01265 double_list_size(scales)); 01266 *sx_00 = 0; 01267 *sy_00 = 0; 01268 *med_scale = 1.0; 01269 *med_angle = 0.0; 01270 } 01271 01272 cleanup; 01273 return; 01274 } 01275 01276 01277 01278