FORS Pipeline Reference Manual 4.9.9
|
00001 /* $Id: fors_img_screen_flat_impl.c,v 1.43 2010/09/14 07:49:30 cizzo 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: cizzo $ 00023 * $Date: 2010/09/14 07:49:30 $ 00024 * $Revision: 1.43 $ 00025 * $Name: fors-4_9_9 $ 00026 */ 00027 00028 #ifdef HAVE_CONFIG_H 00029 #include <config.h> 00030 #endif 00031 00032 #include <fors_img_screen_flat_impl.h> 00033 00034 #include <fors_stack.h> 00035 #include <fors_tools.h> 00036 #include <fors_dfs.h> 00037 #include <fors_qc.h> 00038 #include <fors_utils.h> 00039 00040 #include <cpl.h> 00041 #include <math.h> 00042 00049 const char *const fors_img_screen_flat_name = "fors_img_screen_flat"; 00050 const char *const fors_img_screen_flat_description_short = 00051 "Compute master screen flat frame"; 00052 const char *const fors_img_screen_flat_author = "Jonas M. Larsen"; 00053 const char *const fors_img_screen_flat_email = PACKAGE_BUGREPORT; 00054 const char *const fors_img_screen_flat_description = 00055 "After bias subtraction, the input flat field frames are combined using\n" 00056 "the given stack method. The combined frame is finally normalised dividing\n" 00057 "it by its large scale illumination trend. The large scale trend is obtained\n" 00058 "by applying a median filter with a large kernel. To avoid boundary effects, \n" 00059 "the median filter is applied only to the specified region.\n" 00060 "The overscan regions, if present, are removed from the result.\n\n" 00061 "Input files:\n" 00062 "\n" 00063 " DO category: Type: Explanation: Required:\n" 00064 " SCREEN_FLAT_IMG Raw Screen flat field Y\n" 00065 " MASTER_BIAS Raw Master bias Y\n" 00066 "\n" 00067 "Output files:\n\n" 00068 " DO category: Data type: Explanation:\n" 00069 " MASTER_SCREEN_FLAT_IMG FITS image Master screen flat field\n"; 00070 00071 00072 static void 00073 remove_large_scale(fors_image *master_screen_flat, 00074 const cpl_parameterlist *parameters, 00075 const char *context, 00076 const fors_setting *setting); 00077 00078 static void 00079 remove_large_scale_fit(fors_image *master_screen_flat, 00080 const cpl_parameterlist *parameters, 00081 const char *context, 00082 const fors_setting *setting); 00083 00084 static void 00085 write_qc(cpl_propertylist *qc, 00086 const cpl_frame *first_raw, 00087 const fors_image_list *sflats, 00088 const fors_image *master_sflat, 00089 const fors_setting *setting, 00090 double saturation); 00096 void fors_img_screen_flat_define_parameters(cpl_parameterlist *parameters) 00097 { 00098 const char *context = cpl_sprintf("fors.%s", fors_img_screen_flat_name); 00099 const char *full_name = NULL; 00100 00101 fors_stack_define_parameters(parameters, context, "average"); 00102 00103 { 00104 cpl_parameter *p; 00105 const char *name; 00106 00107 /* Median filter, xradius, yradius */ 00108 name = "xradius"; 00109 full_name = cpl_sprintf("%s.%s", context, name); 00110 p = cpl_parameter_new_value(full_name, 00111 CPL_TYPE_INT, 00112 "Median filter x radius (unbinned pixels)", 00113 context, 00114 50); 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); full_name = NULL; 00119 00120 name = "yradius"; 00121 full_name = cpl_sprintf("%s.%s", context, name); 00122 p = cpl_parameter_new_value(full_name, 00123 CPL_TYPE_INT, 00124 "Median filter y radius (unbinned pixels)", 00125 context, 00126 50); 00127 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name); 00128 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00129 cpl_parameterlist_append(parameters, p); 00130 cpl_free((void *)full_name); full_name = NULL; 00131 00132 name = "degree"; 00133 full_name = cpl_sprintf("%s.%s", context, name); 00134 p = cpl_parameter_new_value(full_name, 00135 CPL_TYPE_INT, 00136 "Degree of fitting polynomial", 00137 context, 00138 -1); 00139 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name); 00140 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00141 cpl_parameterlist_append(parameters, p); 00142 cpl_free((void *)full_name); full_name = NULL; 00143 00144 name = "sampling"; 00145 full_name = cpl_sprintf("%s.%s", context, name); 00146 p = cpl_parameter_new_value(full_name, 00147 CPL_TYPE_INT, 00148 "Sampling interval for fitting", 00149 context, 00150 100); 00151 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name); 00152 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00153 cpl_parameterlist_append(parameters, p); 00154 cpl_free((void *)full_name); full_name = NULL; 00155 } 00156 00157 cpl_free((void *)context); 00158 cpl_free((void *)full_name); 00159 00160 return; 00161 } 00162 00163 #undef cleanup 00164 #define cleanup \ 00165 do { \ 00166 cpl_frameset_delete(sflat_frames); \ 00167 fors_setting_delete(&setting); \ 00168 cpl_frameset_delete(master_bias_frame); \ 00169 fors_image_delete_const(&master_bias); \ 00170 fors_stack_method_delete(&sm); \ 00171 cpl_free((void *)context); \ 00172 fors_image_delete(&master_screen_flat); \ 00173 fors_image_delete(&master_norm_flat); \ 00174 fors_image_list_delete_const(&sflats, fors_image_delete); \ 00175 cpl_propertylist_delete(qc); \ 00176 } while (0) 00177 00185 void 00186 fors_img_screen_flat(cpl_frameset *frames, const cpl_parameterlist *parameters) 00187 { 00188 /* Raw */ 00189 cpl_frameset *sflat_frames = NULL; 00190 const fors_image_list *sflats = NULL; 00191 fors_setting *setting = NULL; 00192 00193 /* Calibration */ 00194 cpl_frameset *master_bias_frame = NULL; 00195 const fors_image *master_bias = NULL; 00196 00197 /* Products */ 00198 fors_image *master_screen_flat = NULL; 00199 fors_image *master_norm_flat = NULL; 00200 00201 /* QC */ 00202 cpl_propertylist *qc = cpl_propertylist_new(); 00203 double saturation; 00204 00205 /* Parameters */ 00206 stack_method *sm = NULL; 00207 00208 /* Other */ 00209 const char *context = cpl_sprintf("fors.%s", fors_img_screen_flat_name); 00210 00211 /* Get parameters */ 00212 sm = fors_stack_method_new(parameters, context); 00213 assure( !cpl_error_get_code(), return, "Could not get stacking method"); 00214 00215 /* Find raw */ 00216 sflat_frames = fors_frameset_extract(frames, SCREEN_FLAT_IMG); 00217 assure( cpl_frameset_get_size(sflat_frames) > 0, return, 00218 "No %s provided", SCREEN_FLAT_IMG); 00219 00220 /* Find calibration */ 00221 master_bias_frame = fors_frameset_extract(frames, MASTER_BIAS); 00222 assure( cpl_frameset_get_size(master_bias_frame) == 1, return, 00223 "One %s required. %d found", 00224 MASTER_BIAS, cpl_frameset_get_size(master_bias_frame)); 00225 00226 /* Get instrument setting */ 00227 setting = fors_setting_new(cpl_frameset_get_first(sflat_frames)); 00228 assure( !cpl_error_get_code(), return, "Could not get instrument setting" ); 00229 00230 master_bias = fors_image_load(cpl_frameset_get_first(master_bias_frame), 00231 NULL, setting, NULL); 00232 assure( !cpl_error_get_code(), return, 00233 "Could not load master bias"); 00234 00235 /* Load raw frames, subtract bias */ 00236 sflats = fors_image_load_list_const(sflat_frames, master_bias, 00237 setting, &saturation); 00238 assure( !cpl_error_get_code(), return, "Could not load screen flat images"); 00239 00240 /* Stack */ 00241 master_screen_flat = fors_stack_const(sflats, sm); 00242 assure( !cpl_error_get_code(), return, "Screen flat stacking failed"); 00243 00244 /* Divide out large scale structure */ 00245 master_norm_flat = fors_image_duplicate(master_screen_flat); 00246 remove_large_scale(master_norm_flat, 00247 parameters, context, 00248 setting); 00249 00250 assure( !cpl_error_get_code(), return, 00251 "Flat field smoothing/normalization failed"); 00252 00253 /* QC */ 00254 write_qc(qc, cpl_frameset_get_first(sflat_frames), 00255 sflats, master_norm_flat, setting, saturation); 00256 assure( !cpl_error_get_code(), return, "Failed to compute QC"); 00257 00258 /* Save products */ 00259 fors_dfs_save_image(frames, master_screen_flat, MASTER_SCREEN_FLAT_IMG, 00260 NULL, parameters, fors_img_screen_flat_name, 00261 cpl_frameset_get_first(sflat_frames)); 00262 assure( !cpl_error_get_code(), return, "Saving %s failed", 00263 MASTER_SCREEN_FLAT_IMG); 00264 00265 fors_dfs_save_image(frames, master_norm_flat, MASTER_NORM_FLAT_IMG, 00266 qc, parameters, fors_img_screen_flat_name, 00267 cpl_frameset_get_first(sflat_frames)); 00268 assure( !cpl_error_get_code(), return, "Saving %s failed", 00269 MASTER_NORM_FLAT_IMG); 00270 00271 cleanup; 00272 return; 00273 } 00274 00275 #undef cleanup 00276 #define cleanup \ 00277 do { \ 00278 cpl_image_delete(smoothed); \ 00279 } while (0) 00280 00298 static void 00299 remove_large_scale(fors_image *master_screen_flat, 00300 const cpl_parameterlist *parameters, 00301 const char *context, 00302 const fors_setting *setting) 00303 00304 { 00305 cpl_image *smoothed = NULL; 00306 const char *name; 00307 int xradius, yradius; 00308 int xstart, ystart; 00309 int xend, yend; 00310 00311 name = cpl_sprintf("%s.%s", context, "degree"); 00312 const cpl_parameter *param = cpl_parameterlist_find_const(parameters, name); 00313 int degree = cpl_parameter_get_int(param); 00314 cpl_free((void *)name); name = NULL; 00315 if (degree >= 0) { 00316 remove_large_scale_fit(master_screen_flat, parameters, context, 00317 setting); 00318 return; 00319 } 00320 00321 cpl_msg_info(cpl_func, "Median filter parameters:"); 00322 00323 cpl_msg_indent_more(); 00324 cpl_msg_indent_more(); 00325 name = cpl_sprintf("%s.%s", context, "xradius"); 00326 xradius = dfs_get_parameter_int_const(parameters, 00327 name); 00328 cpl_free((void *)name); name = NULL; 00329 cpl_msg_indent_less(); 00330 cpl_msg_indent_less(); 00331 assure( !cpl_error_get_code(), return, NULL ); 00332 00333 00334 cpl_msg_indent_more(); 00335 cpl_msg_indent_more(); 00336 name = cpl_sprintf("%s.%s", context, "yradius"); 00337 yradius = dfs_get_parameter_int_const(parameters, 00338 name); 00339 cpl_free((void *)name); name = NULL; 00340 cpl_msg_indent_less(); 00341 cpl_msg_indent_less(); 00342 assure( !cpl_error_get_code(), return, NULL ); 00343 00344 /* Done reading parameters, apply filter */ 00345 bool use_data = true; /* Filter the data values, not error bars */ 00346 00347 /* Correct for CCD binning */ 00348 xradius = (xradius - 1)/setting->binx + 1; 00349 yradius = (yradius - 1)/setting->biny + 1; 00350 xstart = 1; 00351 ystart = 1; 00352 xend = fors_image_get_size_x(master_screen_flat); 00353 yend = fors_image_get_size_y(master_screen_flat); 00354 int xstep = 1; 00355 int ystep = 1; 00356 00357 smoothed = fors_image_filter_median_create(master_screen_flat, 00358 xradius, yradius, 00359 xstart, ystart, 00360 xend, yend, 00361 xstep, ystep, 00362 use_data); 00363 00364 assure( !cpl_error_get_code(), return, NULL ); 00365 00366 int DEBUG = 1; 00367 if (DEBUG) { 00368 const char *filename = "smooth.fits"; 00369 00370 cpl_msg_info(cpl_func, "Saving large scale structure image to %s", 00371 filename); 00372 cpl_image_save(smoothed, filename, CPL_BPP_IEEE_FLOAT, 00373 NULL, CPL_IO_DEFAULT); 00374 } 00375 00376 fors_image_divide_noerr(master_screen_flat, smoothed); 00377 /* smoothed image modified here */ 00378 00379 cleanup; 00380 return; 00381 } 00382 00383 00384 00385 #undef cleanup 00386 #define cleanup \ 00387 do { \ 00388 cpl_image_delete(smoothed); \ 00389 } while (0) 00390 00406 static void 00407 remove_large_scale_fit(fors_image *master_screen_flat, 00408 const cpl_parameterlist *parameters, 00409 const char *context, 00410 const fors_setting *setting) 00411 00412 { 00413 cpl_image *smoothed = NULL; 00414 const char *name; 00415 int step; 00416 int degree; 00417 00418 cpl_msg_info(cpl_func, "Large scale fitting parameters:"); 00419 00420 cpl_msg_indent_more(); 00421 cpl_msg_indent_more(); 00422 name = cpl_sprintf("%s.%s", context, "sampling"); 00423 step = dfs_get_parameter_int_const(parameters, 00424 name); 00425 cpl_free((void *)name); name = NULL; 00426 cpl_msg_indent_less(); 00427 cpl_msg_indent_less(); 00428 assure( !cpl_error_get_code(), return, NULL ); 00429 00430 cpl_msg_indent_more(); 00431 cpl_msg_indent_more(); 00432 name = cpl_sprintf("%s.%s", context, "degree"); 00433 degree = dfs_get_parameter_int_const(parameters, 00434 name); 00435 cpl_free((void *)name); name = NULL; 00436 cpl_msg_indent_less(); 00437 cpl_msg_indent_less(); 00438 assure( !cpl_error_get_code(), return, NULL ); 00439 00440 /* 00441 * Determine typical level of flat field, that will be used to 00442 * avoid fitting values from the vignetted region. 00443 */ 00444 00445 float level = fors_image_get_median(master_screen_flat, NULL) / 2; 00446 00447 smoothed = fors_image_flat_fit_create(master_screen_flat, 00448 step, degree, level); 00449 00450 assure( !cpl_error_get_code(), return, NULL ); 00451 00452 int DEBUG = 1; 00453 if (DEBUG) { 00454 const char *filename = "smooth.fits"; 00455 00456 cpl_msg_info(cpl_func, "Saving large scale structure image to %s", 00457 filename); 00458 cpl_image_save(smoothed, filename, CPL_BPP_IEEE_FLOAT, 00459 NULL, CPL_IO_DEFAULT); 00460 } 00461 00462 fors_image_divide_noerr(master_screen_flat, smoothed); 00463 /* smoothed image modified here */ 00464 00465 cleanup; 00466 return; 00467 } 00468 00469 00470 00471 #undef cleanup 00472 #define cleanup \ 00473 do { \ 00474 fors_image_delete(&image); \ 00475 } while (0) 00476 00484 static void 00485 write_qc(cpl_propertylist *qc, 00486 const cpl_frame *first_raw_frame, 00487 const fors_image_list *sflats, 00488 const fors_image *master_sflat, 00489 const fors_setting *setting, 00490 double saturation) 00491 { 00492 fors_image *image = NULL; 00493 00494 fors_qc_start_group(qc, fors_qc_dic_version, setting->instrument); 00495 00496 fors_qc_write_group_heading(first_raw_frame, 00497 MASTER_SCREEN_FLAT_IMG, 00498 setting->instrument); 00499 assure( !cpl_error_get_code(), return, "Could not write %s QC parameters", 00500 MASTER_SCREEN_FLAT_IMG); 00501 00502 00503 00504 fors_qc_write_qc_double(qc, 00505 saturation, 00506 "QC.OVEREXPO", 00507 "%", 00508 "Percentage of overexposed pixels", 00509 setting->instrument); 00510 00511 /* 00512 * Second_raw is NULL if only one raw frame provided: 00513 */ 00514 00515 const fors_image *first_raw = fors_image_list_first_const(sflats); 00516 const fors_image *second_raw = fors_image_list_next_const(sflats); 00517 00518 fors_qc_write_qc_double(qc, 00519 /* Median of bias subtracted frame */ 00520 fors_image_get_median(first_raw, NULL) 00521 / setting->exposure_time, 00522 "QC.FLAT.EFF", 00523 "ADU/s", 00524 "Flat field lamp efficiency", 00525 setting->instrument); 00526 00527 00528 /* 00529 * Here compute QC.FLAT.PHN and QC.FLAT.CONAD: 00530 */ 00531 00532 double master_photon_noise; 00533 double conad, conad_err; 00534 00535 /* FIXME: 00536 * Here in principle we should use the propagated errors to get 00537 * the master flat photon noise, instead of computing it all over 00538 * again... 00539 */ 00540 00541 if (second_raw != NULL) { 00542 00543 /* 00544 * Use central 101x101 window of difference frame 00545 */ 00546 00547 if (fors_image_get_size_x(second_raw) >= 101 && 00548 fors_image_get_size_y(second_raw) >= 101) { 00549 00550 fors_image *center_first = fors_image_duplicate(first_raw); 00551 fors_image *center_second = fors_image_duplicate(second_raw); 00552 00553 int mid_x = (fors_image_get_size_x(center_first) + 1) / 2; 00554 int mid_y = (fors_image_get_size_y(center_first) + 1) / 2; 00555 00556 fors_image_crop(center_first, 00557 mid_x - 50, mid_y - 50, 00558 mid_x + 50, mid_y + 50); 00559 00560 fors_image_crop(center_second, 00561 mid_x - 50, mid_y - 50, 00562 mid_x + 50, mid_y + 50); 00563 00564 /* 00565 * This one is the intensity image, will be used later 00566 * for gain computation 00567 */ 00568 00569 image = fors_image_duplicate(center_first); 00570 00571 /* 00572 * 1) Determine the median value of the ratio F1/F2 = k . 00573 * 2) Compute the frame F1 - k*F2 (whose mean should be 00574 * around 0). 00575 * 3) The expected variance of F1 - k*F2 is V = V1 + k*k*V2, 00576 * where V1 and V2 are the variances of the frames F1 and F2. 00577 * 4) Neglecting the contribution of the readout noise, it is 00578 * V1 = F1/conad and V2 = F2/conad, so it is also V1/V2 = k . 00579 * 5) Therefore we get V = V1 + k*V1, that is V1 = V/(1+k), 00580 * an estimator of the variance of the first frame. 00581 * 6) The noise (1-sigma level) of the first frame is then 00582 * sqrt(V1) = sqrt(V)/sqrt(1+k). 00583 */ 00584 00585 /* 1) */ 00586 fors_image *ratio = fors_image_duplicate(center_first); 00587 fors_image_divide(ratio, center_second); 00588 double k = fors_image_get_median(ratio, NULL); 00589 fors_image_delete(&ratio); 00590 00591 /* 2) */ 00592 fors_image_multiply_scalar(center_second, k, -1); 00593 fors_image_subtract(center_first, center_second); 00594 fors_image_delete(¢er_second); 00595 00596 /* 6) */ 00597 master_photon_noise = fors_image_get_stdev(center_first, NULL); 00598 master_photon_noise /= sqrt(1+k); 00599 00600 /* 00601 * Now estimate the noise of all frames (assuming that all 00602 * frames have similar exposure times...) 00603 */ 00604 00605 master_photon_noise /= sqrt(fors_image_list_size(sflats)); 00606 00607 /* 00608 * Now center_first is used to determine the CONAD: the 00609 * center_first is still to be squared and divided by (1+k) 00610 * to become an estimator of the variance of the first frame. 00611 * 00612 * CONAD is estimated as 1 / mean(variance_i / flux_i) 00613 * 00614 * Note: It is important that the average is made using 00615 * (variance_i / flux_i), and not using (flux_i / variance_i) 00616 * because the variance estimates are very noisy and 00617 * close to zero. 00618 */ 00619 00620 fors_image_square(center_first); 00621 fors_image_divide(center_first, image); 00622 00623 /* 00624 * We _must_ use mean, here, because the mean of the 00625 * squares in center_first is an estimate of the variance, 00626 * but the median is not (asymmetric distribution). 00627 */ 00628 00629 double gain = fors_image_get_mean(center_first, NULL) / (1+k); 00630 double gain_err = fors_image_get_stdev(center_first, NULL) / (1+k); 00631 fors_image_delete(¢er_first); 00632 fors_image_delete(&image); 00633 00634 /* 00635 * Relative error in CONAD and GAIN is the same: 00636 */ 00637 00638 conad = 1.0 / gain; 00639 conad_err = conad * gain_err/gain; 00640 conad_err /= 101; 00641 } 00642 else { 00643 cpl_msg_warning(cpl_func, 00644 "Raw images too small (%dx%d), " 00645 "need size 101x101 to compute master flat " 00646 "photon noise and gain", 00647 fors_image_get_size_x(second_raw), 00648 fors_image_get_size_y(second_raw)); 00649 00650 master_photon_noise = -1; 00651 } 00652 } 00653 else { 00654 cpl_msg_warning(cpl_func, "Only 1 raw frame provided, " 00655 "cannot compute master flat photon noise"); 00656 master_photon_noise = -1; 00657 } 00658 00659 fors_qc_write_qc_double(qc, 00660 master_photon_noise, 00661 "QC.FLAT.PHN", 00662 "ADU", 00663 "Photon noise in master screen flat field", 00664 setting->instrument); 00665 00666 double master_fixed_pattern_noise = 00667 fors_fixed_pattern_noise(master_sflat, 00668 fors_image_get_median(first_raw, NULL), 00669 master_photon_noise); 00670 assure( !cpl_error_get_code(), return, 00671 "Could not compute fixed pattern noise" ); 00672 00673 fors_qc_write_qc_double(qc, 00674 master_fixed_pattern_noise, 00675 "QC.FLAT.FPN", 00676 "ADU", 00677 "Fixed-pattern noise", 00678 setting->instrument); 00679 00680 master_fixed_pattern_noise /= fors_image_get_median(first_raw, NULL); 00681 00682 fors_qc_write_qc_double(qc, 00683 master_fixed_pattern_noise, 00684 "QC.FLAT.FPN.REL", 00685 NULL, 00686 "Relative fixed-pattern noise", 00687 setting->instrument); 00688 00689 fors_qc_write_qc_double(qc, 00690 conad, 00691 "QC.FLAT.CONAD", 00692 "e-/ADU", 00693 "Conversion factor from ADU to electrons", 00694 setting->instrument); 00695 00696 fors_qc_write_qc_double(qc, 00697 conad_err, 00698 "QC.FLAT.CONADERR", 00699 "e-/ADU", 00700 "Error on conversion factor ADU/electrons", 00701 setting->instrument); 00702 00703 #define SALTA 1 00704 #ifndef SALTA 00705 /* 00706 * Estimate CONAD as 1 / mean(variance_i / flux_i) 00707 * 00708 * where variance_i is estimated as (flux_i1 - flux_i2)^2/2 00709 * for the fluxes of the first two raw frames. The systematic 00710 * part of the difference (i.e. due to sligthly different 00711 * exposure times) is subtracted from the difference image. 00712 * 00713 * Note: It is important that the average is made using 00714 * (variance_i / flux_i), and not using (flux_i / variance_i) 00715 * because the variance estimates are very noisy and 00716 * close to zero. 00717 */ 00718 00719 double conad, conad_err; 00720 00721 if (second_raw != NULL) { 00722 image = fors_image_duplicate(first_raw); 00723 fors_image_subtract(image, second_raw); 00724 00725 /* 00726 * At every pixel, variance_i / flux_i is an estimate of the gain. 00727 * 00728 * To get the average use only part of image with flux_i above 00729 * 0.5*median, because regions with zero ~flux cause division by 00730 * zero and do not help estimate the gain. 00731 */ 00732 00733 double gain_mean, gain_err; 00734 00735 { 00736 double first_raw_median = fors_image_get_median(first_raw, NULL); 00737 00738 const float *first_raw_data = fors_image_get_data_const(first_raw); 00739 const float *diff_data = fors_image_get_data_const(image); 00740 00741 const int Ntot = fors_image_get_size_x(image) 00742 * fors_image_get_size_y(image); 00743 00744 /* 00745 * Computing the mean difference between the two raw flats, 00746 * and subtract it from the difference image. This is in 00747 * principle incorrect: the difference between two flat 00748 * exposures is not a constant, but it has the same illumination 00749 * distribution as the original flats: 00750 * 00751 * diff = flat1 - flat2 = flat1 - k*flat1 = (1-k) * flat1 00752 * 00753 * Correcting for a constant offset would not prevent an 00754 * overestimation of the variance in case k is different 00755 * from 1.0! 00756 */ 00757 00758 double diff_mean = 0; 00759 int N = 0; 00760 int i; 00761 for (i = 0; i < Ntot; i++) { 00762 if (first_raw_data[i] > 0.5 * first_raw_median) { 00763 diff_mean += diff_data[i]; 00764 N += 1; 00765 } 00766 } 00767 diff_mean /= N; 00768 fors_image_subtract_scalar(image, diff_mean, -1); 00769 00770 /* 00771 * Pixel per pixel estimate of variance. 00772 */ 00773 00774 fors_image_square(image); 00775 fors_image_divide_scalar(image, 2, -1); 00776 00777 /* 00778 * Now image contains estimates of variance at every pixel, 00779 * i.e., diff_data[] points to variance values. 00780 */ 00781 00782 double sum_vf = 0; /* sum (variance_i / flux_i) */ 00783 double sum_vfvf = 0; /* sum (variance_i / flux_i)^2 */ 00784 N = 0; 00785 for (i = 0; i < Ntot; i++) { 00786 if (first_raw_data[i] > 0.5 * first_raw_median) { 00787 double vf = diff_data[i] / first_raw_data[i]; 00788 sum_vf += vf; 00789 sum_vfvf += vf*vf; 00790 N += 1; 00791 } 00792 } 00793 00794 gain_mean = sum_vf / N; 00795 double vfvf_mean = sum_vfvf / N; 00796 gain_err = sqrt(fabs(vfvf_mean - gain_mean*gain_mean)); 00797 00798 /* 00799 * Iterate, rejecting 5 sigma outliers (e.g. due to cosmics 00800 * in any raw frame). 00801 */ 00802 00803 double hicut = gain_mean + 5*gain_err; 00804 sum_vf = 0; 00805 sum_vfvf = 0; 00806 N = 0; 00807 for (i = 0; i < Ntot; i++) { 00808 if (first_raw_data[i] > 0.5 * first_raw_median) { 00809 double vf = diff_data[i] / first_raw_data[i]; 00810 if (vf < hicut) { 00811 sum_vf += vf; 00812 sum_vfvf += vf*vf; 00813 N += 1; 00814 } 00815 } 00816 } 00817 00818 cpl_msg_info(cpl_func, "Using %d of %d pixels for gain estimation", 00819 N, Ntot); 00820 00821 gain_mean = sum_vf / N; 00822 00823 /* 00824 * If sigma_i follows a Gaussian distribution and flux_i 00825 * is roughly constant, then the distribution of 00826 * variance_i / flux_i = sigma_i^2 / flux_i 00827 * has a predictable (non-symmetric and non-Gaussian) shape. 00828 * 00829 * We want the *mean* of this distribution, and we cannot use 00830 * e.g. the median to estimate the mean. 00831 */ 00832 00833 vfvf_mean = sum_vfvf / N; 00834 00835 gain_err = sqrt(fabs(vfvf_mean - gain_mean*gain_mean)); 00836 00837 /* 00838 * Mean squared is always greater than squared mean, 00839 * the fabs() is there just in case. 00840 */ 00841 00842 /* 00843 * Convert from stdev to error of mean estimate 00844 */ 00845 00846 gain_err /= sqrt(N); 00847 } 00848 00849 assure( gain_mean > 0, return, 00850 "Difference between first two raw input frames is " 00851 "always zero, cannot estimate gain"); 00852 00853 conad = 1.0/gain_mean; 00854 00855 /* 00856 * Relative error in CONAD and GAIN is the same: 00857 */ 00858 00859 conad_err = conad * gain_err/gain_mean; 00860 } 00861 else { 00862 cpl_msg_warning(cpl_func, "Only 1 raw frame provided, " 00863 "cannot estimate CONAD"); 00864 conad = -1; 00865 conad_err = -1; 00866 } 00867 00868 fors_qc_write_qc_double(qc, 00869 conad, 00870 "QC.FLAT.CONAD", 00871 "e-/ADU", 00872 "Conversion factor from ADU to electrons", 00873 setting->instrument); 00874 00875 fors_qc_write_qc_double(qc, 00876 conad_err, 00877 "QC.FLAT.CONADERR", 00878 "e-/ADU", 00879 "Error on conversion factor ADU/electrons", 00880 setting->instrument); 00881 #endif 00882 00883 fors_qc_end_group(); 00884 00885 cleanup; 00886 return; 00887 } 00888 00889