EXAM Pipeline Reference Manual  0.1.0
exam_flat.c
1 /*
2  * This file is part of the EXAM Pipeline
3  * Copyright (C) 2002-2014 European Southern Observatory
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 
24 /*-----------------------------------------------------------------------------
25  Includes
26  -----------------------------------------------------------------------------*/
27 
28 #include "exam_dfs.h"
29 #include "exam_utils.h"
30 
31 #include <cpl.h>
32 
33 #include <string.h>
34 
35 /*-----------------------------------------------------------------------------
36  Plugin registration
37  -----------------------------------------------------------------------------*/
38 
39 int cpl_plugin_get_info(cpl_pluginlist *list);
40 
41 /*-----------------------------------------------------------------------------
42  Private function prototypes
43  -----------------------------------------------------------------------------*/
44 
45 static int _exam_flat_create(cpl_plugin *plugin);
46 static int _exam_flat_exec(cpl_plugin *plugin);
47 static int _exam_flat_destroy(cpl_plugin *plugin);
48 
49 static void _exam_flat_define_parameters(cpl_parameterlist *parameters);
50 static int _exam_flat(cpl_frameset *frameset,
51  const cpl_parameterlist *parameters);
52 
53 
54 /*-----------------------------------------------------------------------------
55  Static variables
56  -----------------------------------------------------------------------------*/
57 
58 static const char *const _exam_flat_name = "exam_flat";
59 
60 static const char *const _exam_flat_description_short =
61  "Compute the master flat frame";
62 
63 static const char *const _exam_flat_description =
64  "This recipe is used to combine input raw FLAT frames into a"
65  " MASTER_FLAT\nframe.\n\n"
66  "Input files:\n\n"
67  " DO category: Type: Explanation: Required:\n"
68  " FLAT Raw Flat frame Y\n"
69  " MASTER_BIAS Raw Master bias frame Y\n\n"
70  "Output files:\n\n"
71  " DO category: Data type: Explanation:\n"
72  " MASTER_FLAT FITS image Master flat frame\n"
73  " SMOOTH_FLAT FITS image Master flat frame\n"
74  " NORM_FLAT FITS image Normalized master flat frame\n\n";
75 
76 
77 
102 int
103 cpl_plugin_get_info(cpl_pluginlist *list)
104 {
105 
106  cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
107  cpl_plugin *plugin = &recipe->interface;
108 
109  cpl_plugin_init(plugin,
110  CPL_PLUGIN_API,
111  EXAM_BINARY_VERSION,
112  CPL_PLUGIN_TYPE_RECIPE,
113  _exam_flat_name,
114  _exam_flat_description_short,
115  _exam_flat_description,
116  exam_get_author(),
117  exam_get_email(),
118  exam_get_license(),
119  _exam_flat_create,
120  _exam_flat_exec,
121  _exam_flat_destroy);
122 
123  cpl_pluginlist_append(list, plugin);
124 
125  return 0;
126 
127 }
128 
129 
143 static int
144 _exam_flat_create(cpl_plugin *plugin)
145 {
146 
147  if (cpl_error_get_code() != CPL_ERROR_NONE) {
148 
149  cpl_msg_error(cpl_func, "%s():%d: An error is already set: %s,"
150  "skipping recipe instantiation", cpl_func, __LINE__,
151  cpl_error_get_where());
152 
153  return (int)cpl_error_get_code();
154 
155  }
156 
157  if (plugin == NULL) {
158 
159  cpl_msg_error(cpl_func, "Null plugin received");
160  cpl_ensure_code(0, (int)CPL_ERROR_NULL_INPUT);
161 
162  }
163 
164 
165  /*
166  * Working with the plugin can only be done if it has the
167  * expected type.
168  */
169 
170  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) {
171 
172  cpl_recipe *recipe = (cpl_recipe *)plugin;
173 
174 
175  /*
176  * Create the parameters list in the cpl_recipe object
177  */
178 
179  recipe->parameters = cpl_parameterlist_new();
180 
181  if (recipe->parameters == NULL) {
182  cpl_msg_error(cpl_func, "Parameter list allocation failed");
183  cpl_ensure_code(0, (int)CPL_ERROR_ILLEGAL_OUTPUT);
184  }
185 
186  _exam_flat_define_parameters(recipe->parameters);
187 
188  if (cpl_error_get_code() != CPL_ERROR_NONE) {
189  cpl_msg_error(cpl_func, "Could not create %s parameters",
190  cpl_plugin_get_name(plugin));
191  cpl_ensure_code(0, (int)CPL_ERROR_ILLEGAL_OUTPUT);
192  }
193 
194  }
195  else {
196 
197  cpl_msg_error(cpl_func, "Plugin is not a recipe");
198  cpl_ensure_code(0, (int)CPL_ERROR_TYPE_MISMATCH);
199 
200  }
201 
202  return 0;
203 
204 }
205 
206 
218 static int
219 _exam_flat_exec(cpl_plugin *plugin)
220 {
221 
222  int recipe_status;
223  cpl_recipe *recipe = (cpl_recipe *)plugin;
224  cpl_errorstate initial_errorstate = cpl_errorstate_get();
225 
226 
227 
228  if (cpl_error_get_code() != CPL_ERROR_NONE) {
229 
230  cpl_msg_error(cpl_func, "%s():%d: An error is already set: %s,"
231  "skipping recipe execution", cpl_func, __LINE__,
232  cpl_error_get_where());
233 
234  return (int)cpl_error_get_code();
235 
236  }
237 
238  if (plugin == NULL) {
239 
240  cpl_msg_error(cpl_func, "Null plugin received");
241  cpl_ensure_code(0, (int)CPL_ERROR_NULL_INPUT);
242 
243  }
244 
245 
246  /*
247  * Working with the plugin can only be done if it has the
248  * expected type.
249  */
250 
251  if (cpl_plugin_get_type(plugin) != CPL_PLUGIN_TYPE_RECIPE) {
252 
253  cpl_msg_error(cpl_func, "Plugin is not a recipe");
254  cpl_ensure_code(0, (int)CPL_ERROR_TYPE_MISMATCH);
255 
256  }
257 
258 
259  /*
260  * Verify parameter list and frame set
261  */
262 
263  if (recipe->parameters == NULL) {
264  cpl_msg_error(cpl_func, "Recipe invoked with NULL parameter list");
265  cpl_ensure_code(0, (int)CPL_ERROR_NULL_INPUT);
266  }
267 
268  if (recipe->frames == NULL) {
269  cpl_msg_error(cpl_func, "Recipe invoked with NULL frame set");
270  cpl_ensure_code(0, (int)CPL_ERROR_NULL_INPUT);
271  }
272 
273  recipe_status = exam_dfs_set_groups(recipe->frames);
274 
275  if (recipe_status == CPL_ERROR_NONE) {
276  recipe_status = _exam_flat(recipe->frames, recipe->parameters);
277  }
278 
279 
280  if (!cpl_errorstate_is_equal(initial_errorstate)) {
281  /* Dump the error history since recipe execution start.
282  At this point the recipe cannot recover from the error */
283  cpl_errorstate_dump(initial_errorstate, CPL_FALSE, NULL);
284  }
285 
286  return recipe_status;
287 
288 }
289 
290 
302 static int
303 _exam_flat_destroy(cpl_plugin *plugin)
304 {
305 
306  cpl_recipe *recipe = (cpl_recipe *)plugin;
307 
308  if (plugin == NULL) {
309 
310  cpl_msg_error(cpl_func, "Null plugin");
311  cpl_ensure_code(0, (int)CPL_ERROR_NULL_INPUT);
312 
313  }
314 
315  if (cpl_plugin_get_type(plugin) != CPL_PLUGIN_TYPE_RECIPE) {
316 
317  cpl_msg_error(cpl_func, "Plugin is not a recipe");
318  cpl_ensure_code(0, (int)CPL_ERROR_TYPE_MISMATCH);
319 
320  }
321 
322  cpl_parameterlist_delete(recipe->parameters);
323 
324  return 0;
325 
326 }
327 
328 
336 inline static void
337 _exam_flat_define_parameters(cpl_parameterlist *parameters)
338 {
339 
340  cpl_parameter *p;
341  const char *full_name = NULL;
342  const char *name;
343 
344 
345  name = "stack-method";
346  full_name = cpl_sprintf("%s.%s.%s", PACKAGE, _exam_flat_name, name);
347  p = cpl_parameter_new_enum(full_name,
348  CPL_TYPE_STRING,
349  "Frames combination method",
350  _exam_flat_name,
351  "average", 4,
352  "average", "median", "minmax", "ksigma");
353  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
354  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
355  cpl_parameterlist_append(parameters, p);
356  cpl_free((void *)full_name);
357 
358 
359  /* Related to minmax */
360  name = "nmin";
361  full_name = cpl_sprintf("%s.%s.minmax.%s", PACKAGE, _exam_flat_name, name);
362  p = cpl_parameter_new_value(full_name,
363  CPL_TYPE_INT,
364  "Number of lowest values to be rejected",
365  _exam_flat_name,
366  1);
367  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
368  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
369  cpl_parameterlist_append(parameters, p);
370  cpl_free((void *)full_name);
371 
372  name = "nmax";
373  full_name = cpl_sprintf("%s.%s.minmax.%s", PACKAGE, _exam_flat_name, name);
374  p = cpl_parameter_new_value(full_name,
375  CPL_TYPE_INT,
376  "Number of highest values to be rejected",
377  _exam_flat_name,
378  1);
379  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
380  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
381  cpl_parameterlist_append(parameters, p);
382  cpl_free((void *)full_name);
383 
384  /* Related to ksigma */
385  name = "klow";
386  full_name = cpl_sprintf("%s.%s.ksigma.%s", PACKAGE, _exam_flat_name, name);
387  p = cpl_parameter_new_value(full_name,
388  CPL_TYPE_DOUBLE,
389  "Low threshold in ksigma method",
390  _exam_flat_name,
391  3.0);
392  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
393  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
394  cpl_parameterlist_append(parameters, p);
395  cpl_free((void *)full_name);
396 
397  name = "khigh";
398  full_name = cpl_sprintf("%s.%s.ksigma.%s", PACKAGE, _exam_flat_name, name);
399  p = cpl_parameter_new_value(full_name,
400  CPL_TYPE_DOUBLE,
401  "High threshold in ksigma method",
402  _exam_flat_name,
403  3.0);
404  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
405  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
406  cpl_parameterlist_append(parameters, p);
407  cpl_free((void *)full_name);
408 
409  name = "kiter";
410  full_name = cpl_sprintf("%s.%s.ksigma.%s", PACKAGE, _exam_flat_name, name);
411  p = cpl_parameter_new_value(full_name,
412  CPL_TYPE_INT,
413  "Max number of iterations in ksigma method",
414  _exam_flat_name,
415  4);
416  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
417  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
418  cpl_parameterlist_append(parameters, p);
419  cpl_free((void *)full_name);
420 
421  name = "saturation";
422  full_name = cpl_sprintf("%s.%s.%s", PACKAGE, _exam_flat_name, name);
423  p = cpl_parameter_new_value(full_name,
424  CPL_TYPE_DOUBLE,
425  "Saturation level (ADU)",
426  _exam_flat_name,
427  50000.0);
428  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
429  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
430  cpl_parameterlist_append(parameters, p);
431  cpl_free((void *)full_name);
432 
433  name = "normalise";
434  full_name = cpl_sprintf("%s.%s.%s", PACKAGE, _exam_flat_name, name);
435  p = cpl_parameter_new_value(full_name,
436  CPL_TYPE_BOOL,
437  "Produce normalised flat field",
438  _exam_flat_name,
439  FALSE);
440  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
441  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
442  cpl_parameterlist_append(parameters, p);
443  cpl_free((void *)full_name);
444 
445  name = "xradius";
446  full_name = cpl_sprintf("%s.%s.normalise.%s", PACKAGE, _exam_flat_name, name);
447  p = cpl_parameter_new_value(full_name,
448  CPL_TYPE_INT,
449  "X radius of smoothing box (pixel)",
450  _exam_flat_name,
451  5);
452  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
453  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
454  cpl_parameterlist_append(parameters, p);
455  cpl_free((void *)full_name);
456 
457  name = "yradius";
458  full_name = cpl_sprintf("%s.%s.normalise.%s", PACKAGE, _exam_flat_name, name);
459  p = cpl_parameter_new_value(full_name,
460  CPL_TYPE_INT,
461  "Y radius of smoothing box (pixel)",
462  _exam_flat_name,
463  5);
464  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
465  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
466  cpl_parameterlist_append(parameters, p);
467  cpl_free((void *)full_name);
468 
469  return;
470 }
471 
472 
473 /* SHORTCUT FOR WORKSHOP: */
474 #define exam_flat_exit(message) \
475  do { \
476  if (message) cpl_msg_error(_exam_flat_name, message); \
477  cpl_image_delete(master_bias); \
478  cpl_image_delete(master_flat); \
479  cpl_image_delete(norm_flat); \
480  cpl_image_delete(smooth_flat); \
481  cpl_image_delete(flat); \
482  cpl_mask_delete(mask); \
483  cpl_propertylist_delete(qclist); \
484  cpl_propertylist_delete(header); \
485  return 1; \
486  } while (0)
487 
488 
501 static int
502 _exam_flat(cpl_frameset *frameset, const cpl_parameterlist *parameters)
503 {
504 
505  /*
506  * Input parameters
507  */
508 
509  const char *stack_method;
510 
511  int nmin;
512  int nmax;
513  int kiter;
514  int normalise;
515  int xradius;
516  int yradius;
517 
518  double saturation;
519  double klow;
520  double khigh;
521 
522 
523  /*
524  * Input data
525  */
526 
527  cpl_image *master_bias = NULL;
528  cpl_image *flat = NULL;
529 
530  cpl_propertylist *header = NULL;
531 
532 
533  /*
534  * Output data
535  */
536 
537  cpl_image *master_flat = NULL;
538  cpl_image *norm_flat = NULL;
539  cpl_image *smooth_flat = NULL;
540 
541  cpl_propertylist *qclist = NULL;
542 
543 
544  /*
545  * Auxiliary data
546  */
547 
548  cpl_mask *mask = NULL;
549 
550  int nbias, nflats;
551  int qc_overexp_flat;
552  int i;
553  int xbox;
554  int ybox;
555 
556  double qc_median_flat;
557 
558 
559  /*
560  * All trivial consistency checks should be performed at the
561  * very beginning, in order to stop immediately the recipe
562  * if something is wrong (sections 1 to 3).
563  */
564 
565 
566  /*
567  * 1) Check that all required input files are present
568  */
569 
570  nbias = cpl_frameset_count_tags(frameset, MASTER_BIAS);
571 
572  if (nbias == 0) {
573  cpl_msg_error(_exam_flat_name, "Missing input: %s", MASTER_BIAS);
574  exam_flat_exit(NULL);
575  }
576 
577  if (nbias > 1) {
578  cpl_msg_error(_exam_flat_name, "Too many in input: %s", MASTER_BIAS);
579  exam_flat_exit(NULL);
580  }
581 
582  nflats = cpl_frameset_count_tags(frameset, FLAT);
583 
584  if (nflats < 1) {
585  cpl_msg_error(_exam_flat_name, "Missing input: %s", FLAT);
586  exam_flat_exit(NULL);
587  }
588 
589 
590  /*
591  * 2) Check the recipe configuration parameters
592  */
593 
594  stack_method = exam_dfs_get_parameter_string(parameters, _exam_flat_name,
595  "stack-method");
596  if (cpl_error_get_code()) {
597  exam_flat_exit("Failure getting the configuration parameters");
598  }
599 
600  if (strcmp(stack_method, "minmax") == 0) {
601 
602  nmin = exam_dfs_get_parameter_int(parameters, _exam_flat_name,
603  "minmax.nmin");
604  nmax = exam_dfs_get_parameter_int(parameters, _exam_flat_name,
605  "minmax.nmax");
606  if (nmin < 0)
607  exam_flat_exit("Parameter nmin must be zero or positive");
608 
609  if (nmax < 0)
610  exam_flat_exit("Parameter nmax must be zero or positive");
611 
612  if (nflats < nmin + nmax + 1) {
613  cpl_msg_error(_exam_flat_name, "Not enough input flats for "
614  "minmax rejection method: at least %d required",
615  nmin + nmax + 1);
616  exam_flat_exit(NULL);
617  }
618  }
619  else if (strcmp(stack_method, "ksigma") == 0) {
620 
621  klow = exam_dfs_get_parameter_double(parameters, _exam_flat_name,
622  "ksigma.klow");
623  khigh = exam_dfs_get_parameter_double(parameters, _exam_flat_name,
624  "ksigma.khigh");
625  kiter = exam_dfs_get_parameter_int (parameters, _exam_flat_name,
626  "ksigma.kiter");
627  if (klow < 1.0)
628  exam_flat_exit("Parameter klow must be at least 1");
629 
630  if (khigh < 1.0)
631  exam_flat_exit("Parameter khigh must be at least 1");
632 
633  if (kiter < 1)
634  exam_flat_exit("Parameter kiter must be at least 1");
635  }
636 
637  saturation = exam_dfs_get_parameter_double(parameters, _exam_flat_name,
638  "saturation");
639  if (saturation < 0.0) {
640  exam_flat_exit("Saturation level should be positive");
641  }
642 
643 
644  normalise = exam_dfs_get_parameter_bool(parameters, _exam_flat_name,
645  "normalise");
646 
647  if (normalise) {
648  xradius = exam_dfs_get_parameter_int(parameters, _exam_flat_name,
649  "normalise.xradius");
650  yradius = exam_dfs_get_parameter_int(parameters, _exam_flat_name,
651  "normalise.yradius");
652 
653  if (xradius < 1 || yradius < 1 || xradius > 50 || yradius > 50) {
654  exam_flat_exit("Invalid smoothing box radius");
655  }
656 
657  xbox = 2*xradius + 1;
658  ybox = 2*yradius + 1;
659  }
660 
661  /*
662  * Here the real stuff begins (sections 4 to 9).
663  * The way data files are loaded and unloaded depends on the
664  * algorithm. Memory is allocated only while needed, and freed
665  * as soon as possible.
666  */
667 
668 
669  /*
670  * 3) Algorithm & I/O
671  */
672 
673  if (strcmp(stack_method, "average") == 0) {
674 
675  cpl_msg_info(_exam_flat_name,
676  "Averaging %d flat field frames...", nflats);
677 
678  master_flat = exam_dfs_load_image(frameset, FLAT, CPL_TYPE_FLOAT, 0);
679 
680  if (master_flat == NULL)
681  exam_flat_exit("Cannot load flat field");
682 
683  for (i = 1; i < nflats; i++) {
684  flat = exam_dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0);
685  if (flat) {
686  cpl_image_add(master_flat, flat);
687  cpl_image_delete(flat); flat = NULL;
688  }
689  else {
690  exam_flat_exit("Cannot load flat field");
691  }
692  }
693 
694  if (nflats > 1) {
695  cpl_image_divide_scalar(master_flat, nflats);
696  }
697 
698  master_bias = exam_dfs_load_image(frameset, MASTER_BIAS,
699  CPL_TYPE_FLOAT, 0);
700 
701  if (master_bias == NULL) {
702  cpl_msg_error(_exam_flat_name, "Cannot load master bias: %s",
703  cpl_error_get_message());
704  exam_flat_exit(NULL);
705  }
706 
707  cpl_image_subtract(master_flat, master_bias);
708  cpl_image_delete(master_bias); master_bias = NULL;
709 
710  if (cpl_error_get_code()) {
711  cpl_msg_error(_exam_flat_name, "Cannot subtract master bias: %s",
712  cpl_error_get_message());
713  exam_flat_exit(NULL);
714  }
715 
716 
717  }
718  else if (strcmp(stack_method, "median") == 0) {
719  exam_flat_exit("Method ksigma not yet implemented");
720  }
721  else if (strcmp(stack_method, "minmax") == 0) {
722  exam_flat_exit("Method minmax not yet implemented");
723  }
724  else if (strcmp(stack_method, "ksigma") == 0) {
725  exam_flat_exit("Method ksigma not yet implemented");
726  }
727 
728 
729  /*
730  * 4) Computing the QC parameters associated to product MASTER_FLAT.
731  */
732 
733  qclist = cpl_propertylist_new();
734 
735 
736  /*
737  * Compute the QC parameters associated to this product.
738  * Median illumination level, QC.FLAT.MEDIAN:
739  */
740 
741  qc_median_flat = cpl_image_get_median(master_flat);
742 
743  cpl_propertylist_update_double(qclist, "ESO QC FLAT MEDIAN",
744  qc_median_flat);
745 
746  if (cpl_error_get_code()) {
747  exam_flat_exit("Cannot write QC FLAT MEDIAN to product header");
748  }
749 
750 
751  /*
752  * ... and, number of overexposed pixels, QC.FLAT.OVEREXP:
753  */
754 
755  mask = cpl_mask_threshold_image_create(master_flat, -DBL_MAX, saturation);
756 
757  if (mask == NULL) {
758  exam_flat_exit("Cannot create threshold image");
759  }
760 
761  cpl_mask_not(mask);
762  qc_overexp_flat = cpl_mask_count(mask);
763 
764  cpl_mask_delete(mask);
765  mask = NULL;
766 
767  if (cpl_error_get_code()) {
768  exam_flat_exit("Cannot handle threshold mask");
769  }
770 
771  cpl_propertylist_update_int(qclist, "ESO QC FLAT OVEREXP NPIXEL",
772  qc_overexp_flat);
773 
774  if (cpl_error_get_code()) {
775  exam_flat_exit("Cannot write QC FLAT OVEREXP NPIXEL to product header");
776  }
777 
778  cpl_propertylist_update_int(qclist, "ESO QC FLAT OVEREXP LEVEL",
779  saturation);
780 
781  if (cpl_error_get_code()) {
782  exam_flat_exit("Cannot write QC FLAT OVEREXP LEVEL to product header");
783  }
784 
785 
786  if (normalise) {
787  mask = cpl_mask_new(xbox, ybox);
788  cpl_mask_not(mask);
789 
790  smooth_flat = cpl_image_new(cpl_image_get_size_x(master_flat),
791  cpl_image_get_size_y(master_flat),
792  cpl_image_get_type(master_flat));
793 
794  cpl_image_filter_mask(smooth_flat, master_flat, mask, CPL_FILTER_MEDIAN,
795  CPL_BORDER_FILTER);
796 
797  if (cpl_error_get_code()) {
798  exam_flat_exit("Wrong smoothing configuration");
799  }
800 
801  cpl_mask_delete(mask);
802  mask = NULL;
803 
804  norm_flat = cpl_image_divide_create(master_flat, smooth_flat);
805  }
806 
807 
808  /*
809  * 5) Saving products to disk
810  */
811 
812  cpl_propertylist_update_string(qclist, CPL_DFS_PRO_CATG, MASTER_FLAT);
813  exam_dfs_save_image(frameset, parameters, master_flat, _exam_flat_name,
814  MASTER_FLAT, qclist, PACKAGE "/" PACKAGE_VERSION);
815 
816  if (cpl_error_get_code()) {
817  exam_flat_exit("Cannot save master flat field");
818  }
819 
820  cpl_image_delete(master_flat);
821  master_flat = NULL;
822 
823  if (normalise) {
824  cpl_propertylist_update_string(qclist, CPL_DFS_PRO_CATG, SMOOTH_FLAT);
825  exam_dfs_save_image(frameset, parameters, smooth_flat, _exam_flat_name,
826  SMOOTH_FLAT, qclist, PACKAGE "/" PACKAGE_VERSION);
827 
828  if (cpl_error_get_code()) {
829  exam_flat_exit("Cannot save smoothed flat field");
830  }
831 
832  cpl_image_delete(smooth_flat);
833  smooth_flat = NULL;
834 
835  cpl_propertylist_update_string(qclist, CPL_DFS_PRO_CATG, NORM_FLAT);
836  exam_dfs_save_image(frameset, parameters, norm_flat, _exam_flat_name,
837  NORM_FLAT, qclist, PACKAGE "/" PACKAGE_VERSION);
838 
839  if (cpl_error_get_code()) {
840  exam_flat_exit("Cannot save normalized flat field");
841  }
842 
843  cpl_propertylist_delete(qclist);
844  qclist = NULL;
845 
846  cpl_image_delete(norm_flat);
847  norm_flat = NULL;
848  }
849 
850  return 0;
851 }
int cpl_plugin_get_info(cpl_pluginlist *list)
Build the list of available plugins, for this module.
Definition: exam_flat.c:103