fors_img_screen_flat_impl.c

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_8_6 $
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(&center_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(&center_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 

Generated on Fri Mar 4 09:46:00 2011 for FORS Pipeline Reference Manual by  doxygen 1.4.7