IIINSTRUMENT Pipeline Reference Manual  4.4.10
naco_img_jitter.c
1 /* $Id: naco_img_jitter.c,v 1.123 2011-12-22 11:09:36 llundin Exp $
2  *
3  * This file is part of the NACO Pipeline
4  * Copyright (C) 2002,2003 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
19  */
20 
21 /*
22  * $Author: llundin $
23  * $Date: 2011-12-22 11:09:36 $
24  * $Revision: 1.123 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 /*-----------------------------------------------------------------------------
33  Includes
34  -----------------------------------------------------------------------------*/
35 
36 #include "naco_recipe.h"
37 #include "naco_strehl.h"
38 
39 #include "irplib_strehl.h"
40 #include "irplib_calib.h"
41 
42 /* Needed for memmove() */
43 #include <string.h>
44 
45 /*-----------------------------------------------------------------------------
46  Defines
47  -----------------------------------------------------------------------------*/
48 
49 #define RECIPE_STRING "naco_img_jitter"
50 
51 #define EXT0 0
52 
53 #define NACO_MIN(A,B) ((A) < (B) ? (A) : (B))
54 #define NACO_MAX(A,B) ((A) > (B) ? (A) : (B))
55 
56 /*-----------------------------------------------------------------------------
57  Private Functions prototypes
58  -----------------------------------------------------------------------------*/
59 
60 #ifndef NACO_IMG_JITTER_KEEP_SKY_OBJECTS
61 static cpl_error_code naco_img_jitter_reject_objects(cpl_image *, double,
62  double);
63 #endif
64 
65 static cpl_error_code naco_img_jitter_find_strehl(const cpl_imagelist *,
66  const cpl_parameterlist *,
67  const irplib_framelist *);
68 
69 static cpl_image * naco_img_jitter_saa_center(const cpl_imagelist *,
70  cpl_bivector *);
71 
72 static cpl_image * naco_img_jitter_saa_lucky(const cpl_imagelist *,
73  const cpl_vector *,
74  const cpl_bivector *, double);
75 
76 static cpl_image * naco_img_jitter_find_sky_cube(int, int, const cpl_array *,
77  const irplib_framelist *);
78 
79 static cpl_error_code naco_img_jitter_imagelist_wrap_nocube(cpl_imagelist *,
80  const cpl_array *,
81  cpl_imagelist *);
82 
83 
84 static cpl_image * naco_img_jitter_combine_cube(cpl_table *, const cpl_imagelist *,
85  const cpl_propertylist *,
86  const cpl_parameterlist *, int);
87 
88 static cpl_error_code naco_img_jitter_check_cube(const cpl_imagelist *,
89  const cpl_image *);
90 
91 static cpl_error_code naco_img_jitter_do_cube(cpl_imagelist *, cpl_table *,
92  cpl_propertylist *,
93  const cpl_array *,
94  const irplib_framelist *,
95  const cpl_parameterlist *);
96 
97 static
98 cpl_error_code naco_img_jitter_load_objimages(cpl_imagelist *, cpl_array *,
99  const irplib_framelist *,
100  const cpl_parameterlist *);
101 
102 static cpl_image ** naco_img_jitter_reduce(cpl_imagelist *, cpl_table *,
103  cpl_propertylist *,
104  const irplib_framelist *,
105  const irplib_framelist *,
106  const cpl_parameterlist *,
107  const char *,
108  const char *,
109  const char *,
110  cpl_boolean *);
111 
112 static cpl_image * naco_img_jitter_find_sky(cpl_propertylist *,
113  const cpl_imagelist *,
114  const cpl_imagelist *);
115 static cpl_image ** naco_img_jitter_saa(cpl_imagelist *,
116  const irplib_framelist *,
117  const cpl_parameterlist *);
118 static cpl_error_code naco_img_jitter_qc(cpl_propertylist *, cpl_propertylist *,
119  const irplib_framelist *,
120  const cpl_image *, cpl_boolean);
121 static cpl_error_code naco_img_jitter_qc_apertures(cpl_propertylist *,
122  const irplib_framelist *,
123  const cpl_image *);
124 static cpl_error_code naco_img_jitter_save(cpl_frameset *,
125  const cpl_parameterlist *,
126  const cpl_propertylist *,
127  const cpl_propertylist *,
128  const cpl_imagelist *,
129  const cpl_image *,
130  const cpl_image *,
131  const cpl_table *);
132 
133 NACO_RECIPE_DEFINE(naco_img_jitter,
134  NACO_PARAM_OFFSETS |
135  NACO_PARAM_OBJECTS |
136  NACO_PARAM_ODDEVEN |
137  NACO_PARAM_XCORR |
138  NACO_PARAM_UNION |
139  NACO_PARAM_COMBINE |
140  NACO_PARAM_SKYPLANE|
141  NACO_PARAM_CUBEMODE|
142  NACO_PARAM_LUCK_STR|
143  NACO_PARAM_SAVECUBE|
144  NACO_PARAM_REJ_HILO,
145  "Jitter recipe",
146  RECIPE_STRING " -- NACO imaging jitter recipe.\n"
147  "The files listed in the Set Of Frames (sof-file) "
148  "must be tagged:\n"
149  "NACO-raw-file.fits "NACO_IMG_JITTER_OBJ" or\n"
150  "NACO-raw-file.fits "NACO_IMG_JITTER_SKY" or\n"
151  "NACO-raw-file.fits "NACO_IMG_JITTER_OBJ_POL" or\n"
152  "NACO-raw-file.fits "NACO_IMG_JITTER_SKY_POL" or\n"
153  "NACO-flat-file.fits "NACO_CALIB_FLAT" or\n"
154  "NACO-bpm-file.fits "NACO_CALIB_BPM" or\n"
155  "NACO-dark-file.fits "NACO_CALIB_DARK"\n");
156 
157 /*----------------------------------------------------------------------------*/
161 /*----------------------------------------------------------------------------*/
162 
163 /*-----------------------------------------------------------------------------
164  Functions code
165  -----------------------------------------------------------------------------*/
166 
167 /*----------------------------------------------------------------------------*/
174 /*----------------------------------------------------------------------------*/
175 static int naco_img_jitter(cpl_frameset * framelist,
176  const cpl_parameterlist * parlist)
177 {
178  cpl_errorstate cleanstate = cpl_errorstate_get();
179  irplib_framelist * allframes = NULL;
180  irplib_framelist * objframes = NULL;
181  irplib_framelist * skyframes = NULL;
182  cpl_propertylist * qclist = cpl_propertylist_new();
183  cpl_propertylist * paflist = cpl_propertylist_new();
184  cpl_imagelist * objimages = cpl_imagelist_new();
185  cpl_image ** combined = NULL;
186  cpl_table * cubetable = cpl_table_new(1);
187  const char * badpix;
188  const char * dark;
189  const char * flat;
190  cpl_boolean drop_wcs = CPL_FALSE;
191 
192 
193  /* Identify the RAW and CALIB frames in the input frameset */
194  skip_if (naco_dfs_set_groups(framelist));
195 
196  allframes = irplib_framelist_cast(framelist);
197  skip_if(allframes == NULL);
198 
199  objframes = irplib_framelist_extract_regexp(allframes,
200  "^(" NACO_IMG_JITTER_OBJ "|"
201  NACO_IMG_JITTER_OBJ_POL ")$",
202  CPL_FALSE);
203  skip_if(objframes == NULL);
204 
205  skyframes = irplib_framelist_extract_regexp(allframes,
206  "^(" NACO_IMG_JITTER_SKY "|"
207  NACO_IMG_JITTER_SKY_POL ")$",
208  CPL_FALSE);
209  irplib_framelist_empty(allframes);
210  if (skyframes == NULL) {
211  naco_error_reset("The set of frames has no sky frames:");
212  }
213 
214  flat = irplib_frameset_find_file(framelist, NACO_CALIB_FLAT);
215  bug_if(0);
216 
217  badpix = irplib_frameset_find_file(framelist, NACO_CALIB_BPM);
218  bug_if(0);
219 
220  dark = irplib_frameset_find_file(framelist, NACO_CALIB_DARK);
221  bug_if(0);
222 
223  skip_if(irplib_framelist_load_propertylist(objframes, 0, 0, "^("
224  NACO_PFITS_REGEXP_JITTER_FIRST
225  ")$", CPL_FALSE));
226 
227  skip_if(irplib_framelist_load_propertylist_all(objframes, 0, "^("
228  NACO_PFITS_REGEXP_JITTER_ALL
229  ")$", CPL_FALSE));
230 
231  /* Apply the reduction */
232  cpl_msg_info(cpl_func, "Apply the data recombination");
233  combined = naco_img_jitter_reduce(objimages, cubetable, qclist, objframes,
234  skyframes, parlist, dark, flat, badpix,
235  &drop_wcs);
236 
237  skip_if (combined == NULL);
238 
239  irplib_framelist_empty(skyframes);
240 
241  /* Compute QC parameters from the combined image */
242  cpl_msg_info(cpl_func, "Compute QC parameters from the combined image");
243  skip_if (naco_img_jitter_qc(qclist, paflist, objframes, combined[0], drop_wcs));
244 
245  irplib_framelist_empty(objframes);
246 
247  /* Save the products */
248  cpl_msg_info(cpl_func, "Save the products");
249 
250  /* PRO.CATG */
251  bug_if (cpl_propertylist_append_string(paflist, CPL_DFS_PRO_CATG,
252  cpl_table_get_ncol(cubetable) > 0 ?
253  NACO_IMG_JITTER_CUBE :
254  NACO_IMG_JITTER_COMB));
255 
256  skip_if (naco_img_jitter_save(framelist, parlist, qclist, paflist,
257  objimages,
258  combined[0], combined[1], cubetable));
259 
260  end_skip;
261 
262  if (combined != NULL) {
263  cpl_image_delete(combined[0]);
264  cpl_image_delete(combined[1]);
265  cpl_free(combined);
266  }
267  cpl_imagelist_delete(objimages);
268  irplib_framelist_delete(allframes);
269  irplib_framelist_delete(objframes);
270  irplib_framelist_delete(skyframes);
271  cpl_propertylist_delete(qclist);
272  cpl_propertylist_delete(paflist);
273  cpl_table_delete(cubetable);
274 
275  return cpl_error_get_code();
276 }
277 
278 /*----------------------------------------------------------------------------*/
293 /*----------------------------------------------------------------------------*/
294 static cpl_image ** naco_img_jitter_reduce(cpl_imagelist * objimages,
295  cpl_table * cubetable,
296  cpl_propertylist * qclist,
297  const irplib_framelist * obj,
298  const irplib_framelist * sky,
299  const cpl_parameterlist * parlist,
300  const char * dark,
301  const char * flat,
302  const char * bpm,
303  cpl_boolean * pdid_resize)
304 {
305  cpl_errorstate prestate = cpl_errorstate_get();
306  const int nobj = irplib_framelist_get_size(obj);
307  cpl_imagelist * nocubeimgs = NULL;
308  cpl_imagelist * skyimages = NULL;
309  cpl_image * skyimage = NULL;
310  cpl_image ** combined = NULL;
311  cpl_array * iscube = NULL;
312  cpl_boolean has_cube = CPL_FALSE;
313  const char * scubemode = naco_parameterlist_get_string
314  (parlist, RECIPE_STRING, NACO_PARAM_CUBEMODE);
315  int inocube = 0;
316 
317 
318  bug_if(pdid_resize == NULL);
319  bug_if(scubemode == NULL);
320 
321  skip_if (irplib_framelist_contains(obj, NACO_PFITS_DOUBLE_CUMOFFSETX,
322  CPL_TYPE_DOUBLE, CPL_FALSE, 0.0));
323 
324  skip_if (irplib_framelist_contains(obj, NACO_PFITS_DOUBLE_CUMOFFSETY,
325  CPL_TYPE_DOUBLE, CPL_FALSE, 0.0));
326 
327  /* Load the input data */
328  cpl_msg_info(cpl_func, "Loading the %d object and %d sky images",
329  irplib_framelist_get_size(obj),
330  sky == NULL ? 0 : irplib_framelist_get_size(sky));
331 
332  iscube = cpl_array_new(nobj, CPL_TYPE_INT);
333 
334  (void)naco_img_jitter_load_objimages(objimages, iscube, obj, parlist);
335 
336  any_if("Could not load the %d object images", nobj);
337 
338  cpl_msg_info(cpl_func, "Loaded %d object images", nobj);
339 
340  skip_if(cpl_imagelist_get_size(objimages) != nobj);
341 
342  skip_if (irplib_flat_dark_bpm_calib(objimages, flat, dark, bpm));
343 
344  cpl_msg_info(cpl_func, "Sky estimation and correction");
345 
346  if (scubemode[0] == 'a') {
347  /* Just add without shifting */
348  const int mcube = nobj - cpl_array_count_invalid(iscube);
349 
350  if (mcube > 0) {
351  cpl_msg_info(cpl_func, "Collapsing %d cube(s) with no shifting",
352  mcube);
353  cpl_array_delete(iscube);
354  iscube = cpl_array_new(nobj, CPL_TYPE_INT);
355  }
356  }
357 
358  if (cpl_array_has_valid(iscube)) {
359  has_cube = CPL_TRUE;
360 
361  skip_if(naco_img_jitter_do_cube(objimages, cubetable, qclist,
362  iscube, obj, parlist));
363 
364 #ifdef NACO_IMG_JITTER_DEVELOPMENT
365  skip_if(cpl_imagelist_save(objimages, "Collapsed.fits", CPL_BPP_IEEE_FLOAT,
366  NULL, CPL_IO_CREATE));
367 #endif
368  }
369 
370  if (cpl_array_has_invalid(iscube)) {
371 
372  nocubeimgs = has_cube ? cpl_imagelist_new() : objimages;
373  if (has_cube) {
374  skip_if(naco_img_jitter_imagelist_wrap_nocube(nocubeimgs, iscube,
375  objimages));
376  inocube = cpl_imagelist_get_size(nocubeimgs);
377  }
378 
379  /* Estimate the sky */
380  if (sky != NULL) {
381  skyimages = irplib_imagelist_load_framelist(sky, CPL_TYPE_FLOAT, 0, 0);
382  any_if("Could not load sky images");
383  skip_if (irplib_flat_dark_bpm_calib(skyimages, flat, dark, bpm));
384  }
385 
386  skyimage = naco_img_jitter_find_sky(qclist, nocubeimgs, skyimages);
387  any_if("Could not estimate sky");
388 
389  cpl_imagelist_delete(skyimages);
390  skyimages = NULL;
391 
392  /* Apply the sky correction */
393  skip_if (cpl_imagelist_subtract_image(nocubeimgs, skyimage));
394  }
395 
396  /* Find the Strehl ratio of all object frames */
397  if(naco_img_jitter_find_strehl(objimages, parlist, obj)) {
398  irplib_error_recover(prestate, "Could not compute Strehl-ratio "
399  "for %d frame(s)", nobj);
400  }
401 
402  /* Apply the shift and add */
403  cpl_msg_info(cpl_func, "Shift and add");
404  combined = naco_img_jitter_saa(objimages, obj, parlist);
405  skip_if (combined == NULL);
406 
407  *pdid_resize = (cpl_boolean)
408  (cpl_image_get_size_x(combined[0])
409  != cpl_image_get_size_x(cpl_imagelist_get_const(objimages, 0)) ||
410  cpl_image_get_size_y(combined[0])
411  != cpl_image_get_size_y(cpl_imagelist_get_const(objimages, 0)));
412 
413  end_skip;
414 
415  /* Unwrap the wrapped no-cube images */
416  for (;inocube > 0;) {
417  (void)cpl_imagelist_unset(nocubeimgs, --inocube);
418  }
419 
420  if (nocubeimgs != objimages) cpl_imagelist_delete(nocubeimgs);
421  cpl_imagelist_delete(skyimages);
422  cpl_image_delete(skyimage);
423  cpl_array_delete(iscube);
424 
425  return combined;
426 }
427 
428 /*----------------------------------------------------------------------------*/
436 /*----------------------------------------------------------------------------*/
437 static cpl_image * naco_img_jitter_find_sky(cpl_propertylist * qclist,
438  const cpl_imagelist * objs,
439  const cpl_imagelist * skys)
440 {
441  cpl_image * self = NULL;
442 
443  if (skys != NULL) {
444  /* Use sky images */
445  self = cpl_imagelist_collapse_median_create(skys);
446  any_if("Could not compute the median of %d sky images",
447  (int)cpl_imagelist_get_size(skys));
448  } else {
449  /* Use objects images */
450  /* FIXME: This will work badly for few (one!) images */
451  self = cpl_imagelist_collapse_median_create(objs);
452  any_if("Could not compute the median of %d object images",
453  (int)cpl_imagelist_get_size(objs));
454  }
455 
456  /* Get the background value */
457  bug_if(cpl_propertylist_append_double(qclist, "ESO QC BACKGD",
458  cpl_image_get_median(self)));
459  bug_if(cpl_propertylist_append_double(qclist, "ESO QC BACKGD STDEV",
460  cpl_image_get_stdev(self)));
461 
462  end_skip;
463 
464  return self;
465 }
466 
467 /*----------------------------------------------------------------------------*/
475 /*----------------------------------------------------------------------------*/
476 static cpl_image ** naco_img_jitter_saa(cpl_imagelist * imlist,
477  const irplib_framelist * objframes,
478  const cpl_parameterlist * parlist)
479 {
480  const char * sval;
481  cpl_bivector * offsets_est = NULL;
482  cpl_bivector * objs = NULL;
483  cpl_image ** combined = NULL;
484  cpl_vector * sigmas = NULL;
485  double psigmas[] = {5.0, 2.0, 1.0, 0.5};
486  const char * offsets;
487  const char * objects;
488 #ifdef NACO_USE_ODDEVEN
489  cpl_boolean oddeven_flag;
490 #endif
491  const int nsigmas = (int)(sizeof(psigmas)/sizeof(double));
492  const int nfiles = cpl_imagelist_get_size(imlist);
493  int sx, sy, mx, my;
494  int rej_low, rej_high;
495  const char * combine_string;
496  cpl_geom_combine combine_mode;
497 
498 
499  bug_if(0);
500  bug_if (irplib_framelist_get_size(objframes) != nfiles);
501 
502  /* Retrieve input parameters */
503 
504  /* Offsets */
505  offsets = naco_parameterlist_get_string(parlist, RECIPE_STRING,
506  NACO_PARAM_OFFSETS);
507  /* Objects */
508  objects = naco_parameterlist_get_string(parlist, RECIPE_STRING,
509  NACO_PARAM_OBJECTS);
510 #ifdef NACO_USE_ODDEVEN
511  /* Oddeven flag */
512  oddeven_flag = naco_parameterlist_get_bool(parlist, RECIPE_STRING,
513  NACO_PARAM_ODDEVEN);
514 #endif
515 
516  /* Cross correlation windows parameters */
517  sval = naco_parameterlist_get_string(parlist, RECIPE_STRING,
518  NACO_PARAM_XCORR);
519  bug_if (sval == NULL);
520 
521  skip_if_ne(sscanf(sval, "%d %d %d %d", &sx, &sy, &mx, &my), 4,
522  "number(s) in string parameter (%s): \"%s\"",
523  CPL_XSTRINGIFY(NACO_PARAM_XCORR), sval);
524 
525  combine_string = naco_parameterlist_get_string(parlist, RECIPE_STRING,
526  NACO_PARAM_COMBINE);
527 
528  bug_if (combine_string == NULL);
529 
530  if (combine_string[0] == 'u')
531  combine_mode = CPL_GEOM_UNION;
532  else if (combine_string[0] == 'f')
533  combine_mode = CPL_GEOM_FIRST;
534  else if (combine_string[0] == 'i')
535  combine_mode = CPL_GEOM_INTERSECT;
536  else
537  skip_if(1);
538 
539  /* Number of rejected values in stacking */
540  sval = naco_parameterlist_get_string(parlist, RECIPE_STRING,
541  NACO_PARAM_REJ_HILO);
542  bug_if (sval == NULL);
543 
544  skip_if_ne(sscanf(sval, "%d %d", &rej_low, &rej_high), 2,
545  "number(s) in string parameter (%s): \"%s\"",
546  CPL_XSTRINGIFY(NACO_PARAM_REJ_HILO), sval);
547 
548  /* Get the offsets estimation of each input file pair */
549  cpl_msg_info(cpl_func, "Get the offsets estimation");
550  offsets_est = NULL;
551  if (offsets &&
552  offsets[0] != (char)0) {
553  /* A file has been provided on the command line */
554  offsets_est = cpl_bivector_read(offsets);
555  if (offsets_est == NULL ||
556  cpl_bivector_get_size(offsets_est) != nfiles) {
557  cpl_msg_error(cpl_func, "Cannot get offsets from %s",
558  offsets);
559  skip_if(1);
560  }
561  } else {
562  double * offsets_est_x;
563  double * offsets_est_y;
564  double offx0 = DBL_MAX; /* Avoid (false) uninit warning */
565  double offy0 = DBL_MAX; /* Avoid (false) uninit warning */
566  int i;
567 
568  /* Get the offsets from the header */
569  offsets_est = cpl_bivector_new(nfiles);
570  offsets_est_x = cpl_bivector_get_x_data(offsets_est);
571  offsets_est_y = cpl_bivector_get_y_data(offsets_est);
572  for (i=0 ; i < nfiles ; i++) {
573  const cpl_propertylist * plist
574  = irplib_framelist_get_propertylist_const(objframes, i);
575 
576  /* X and Y offsets */
577  if (i == 0) {
578  offx0 = naco_pfits_get_cumoffsetx(plist);
579  offy0 = naco_pfits_get_cumoffsety(plist);
580  }
581 
582  /* Subtract the first offset to all offsets */
583  offsets_est_x[i] = offx0 - naco_pfits_get_cumoffsetx(plist);
584  offsets_est_y[i] = offy0 - naco_pfits_get_cumoffsety(plist);
585 
586  bug_if(0);
587  }
588  }
589 
590  /* Read the provided objects file if provided */
591  if (objects &&
592  objects[0] != (char)0) {
593  cpl_msg_info(cpl_func, "Get the user provided correlation objects");
594  /* A file has been provided on the command line */
595  objs = cpl_bivector_read(objects);
596  if (objs==NULL) {
597  cpl_msg_error(cpl_func, "Cannot get objects from %s",
598  objects);
599  skip_if (1);
600  }
601  }
602 
603  /* Create the vector for the detection thresholds */
604  sigmas = cpl_vector_wrap(nsigmas, psigmas);
605 
606  /* Recombine the images */
607  cpl_msg_info(cpl_func, "Recombine the images set");
608  combined = cpl_geom_img_offset_combine(imlist, offsets_est, 1, objs,
609  sigmas, NULL, sx, sy, mx, my,
610  rej_low, rej_high, combine_mode);
611 
612  skip_if (combined == NULL);
613 
614  end_skip;
615 
616  cpl_bivector_delete(offsets_est);
617  cpl_bivector_delete(objs);
618  cpl_vector_unwrap(sigmas);
619 
620  return combined;
621 }
622 
623 /*----------------------------------------------------------------------------*/
633 /*----------------------------------------------------------------------------*/
634 static cpl_error_code naco_img_jitter_qc(cpl_propertylist * qclist,
635  cpl_propertylist * paflist,
636  const irplib_framelist * objframes,
637  const cpl_image * combined,
638  cpl_boolean drop_wcs)
639 {
640  cpl_errorstate cleanstate = cpl_errorstate_get();
641  const cpl_propertylist * reflist
642  = irplib_framelist_get_propertylist_const(objframes, 0);
643  const char * sval;
644 
645 
646  bug_if(combined == NULL);
647 
648  bug_if(cpl_propertylist_copy_property_regexp(paflist, reflist, "^("
649  NACO_PFITS_REGEXP_JITTER_PAF
650  ")$", 0));
651 
652  if (naco_img_jitter_qc_apertures(qclist, objframes, combined)) {
653  naco_error_reset("Could not compute all QC parameters");
654  }
655 
656  sval = naco_pfits_get_filter(reflist);
657  if (cpl_error_get_code()) {
658  naco_error_reset("Could not get FITS key:");
659  } else {
660  cpl_propertylist_append_string(qclist, "ESO QC FILTER OBS", sval);
661  }
662  sval = naco_pfits_get_opti3_name(reflist);
663  if (cpl_error_get_code()) {
664  naco_error_reset("Could not get FITS key:");
665  } else {
666  cpl_propertylist_append_string(qclist, "ESO QC FILTER NDENS", sval);
667  }
668  sval = naco_pfits_get_opti4_name(reflist);
669  if (cpl_error_get_code()) {
670  naco_error_reset("Could not get FITS key:");
671  } else {
672  cpl_propertylist_append_string(qclist, "ESO QC FILTER POL", sval);
673  }
674 
675  bug_if(0);
676 
677  bug_if (cpl_propertylist_append(paflist, qclist));
678 
679  if (drop_wcs) {
680  cpl_propertylist * pcopy = cpl_propertylist_new();
681  const cpl_error_code error
682  = cpl_propertylist_copy_property_regexp(pcopy, reflist, "^("
683  IRPLIB_PFITS_WCS_REGEXP
684  ")$", 0);
685  if (!error && cpl_propertylist_get_size(pcopy) > 0) {
686  cpl_msg_warning(cpl_func, "Combined image will have no WCS "
687  "coordinates");
688  }
689  cpl_propertylist_delete(pcopy);
690 
691  bug_if (cpl_propertylist_copy_property_regexp(qclist, reflist, "^("
692  NACO_PFITS_REGEXP_JITTER_COPY
693  ")$", 0));
694  } else {
695  bug_if(cpl_propertylist_copy_property_regexp(qclist, reflist, "^("
696  IRPLIB_PFITS_WCS_REGEXP "|"
697  NACO_PFITS_REGEXP_JITTER_COPY
698  ")$", 0));
699  }
700 
701  if (cpl_propertylist_has(qclist, "AIRMASS"))
702  bug_if (irplib_pfits_set_airmass(qclist, objframes));
703 
704  end_skip;
705 
706  return cpl_error_get_code();
707 }
708 
709 /*----------------------------------------------------------------------------*/
717 /*----------------------------------------------------------------------------*/
718 static cpl_error_code naco_img_jitter_qc_apertures(cpl_propertylist * qclist,
719  const irplib_framelist *
720  objframes,
721  const cpl_image * combined)
722 {
723  const cpl_propertylist * reflist
724  = irplib_framelist_get_propertylist_const(objframes, 0);
725 
726  cpl_apertures * aperts = NULL;
727  cpl_bivector * fwhms = NULL;
728  cpl_vector * fwhms_sum = NULL;
729  cpl_vector * sigmas = NULL;
730  double * fwhms_x;
731  double * fwhms_y;
732  double * fwhms_sum_data;
733  int nb_val, nb_good;
734  double f_min, f_max;
735  double psigmas[] = {5.0, 2.0, 1.0, 0.5}; /* Not modified */
736  const int nsigmas = (int)(sizeof(psigmas)/sizeof(double));
737  cpl_size isigma;
738  int i;
739 
740  const double pixscale = naco_pfits_get_pixscale(reflist);
741  /* FIXME: Some hard-coded constants */
742  const double seeing_min_arcsec = 0.1;
743  const double seeing_max_arcsec = 5.0;
744  const double seeing_fwhm_var = 0.2;
745 
746  double iq, fwhm_pix, fwhm_arcsec;
747 
748 
749  /* Detect apertures */
750  cpl_msg_info(cpl_func, "Detecting apertures using %d sigma-levels "
751  "ranging from %g down to %g", nsigmas, psigmas[0],
752  psigmas[nsigmas-1]);
753 
754  /* pixscale could be mising */
755  skip_if( pixscale <= 0.0 );
756  bug_if( seeing_min_arcsec < 0.0);
757 
758  /* Create the vector for the detection thresholds. (Not modified) */
759  sigmas = cpl_vector_wrap(nsigmas, psigmas);
760 
761  aperts = cpl_apertures_extract(combined, sigmas, &isigma);
762  if (aperts == NULL) {
763  cpl_msg_warning(cpl_func, "Could not detect any apertures in combined "
764  "image");
765  skip_if(1);
766  }
767  bug_if(0);
768 
769  /* Compute the FHWM of the detected apertures */
770  CPL_DIAG_PRAGMA_PUSH_IGN(-Wdeprecated-declarations);
771  fwhms = cpl_apertures_get_fwhm(combined, aperts);
772  CPL_DIAG_PRAGMA_POP;
773 
774  if (fwhms == NULL) {
775  cpl_msg_warning(cpl_func, "Could not compute any FWHMs of the "
776  "apertures in the combined image");
777  skip_if(1);
778  }
779  bug_if(0);
780  cpl_apertures_delete(aperts);
781  aperts = NULL;
782 
783  /* Access the data */
784  nb_val = cpl_bivector_get_size(fwhms);
785  fwhms_x = cpl_bivector_get_x_data(fwhms);
786  fwhms_y = cpl_bivector_get_y_data(fwhms);
787 
788  /* Get the number of good values */
789  nb_good = 0;
790  for (i=0 ; i < nb_val ; i++) {
791  if (fwhms_x[i] <= 0.0 || fwhms_y[i] <= 0.0) continue;
792  fwhms_x[nb_good] = fwhms_x[i];
793  fwhms_y[nb_good] = fwhms_y[i];
794  nb_good++;
795  }
796 
797  cpl_msg_info(cpl_func, "Detected %d (%d) apertures at sigma=%g",
798  nb_good, nb_val, psigmas[isigma]);
799 
800  skip_if_lt (nb_good, 1, "aperture with a FWHM");
801 
802  nb_val = nb_good;
803  /* This resizing is not really needed */
804  bug_if(cpl_vector_set_size(cpl_bivector_get_x(fwhms), nb_val));
805  bug_if(cpl_vector_set_size(cpl_bivector_get_y(fwhms), nb_val));
806  fwhms_x = cpl_bivector_get_x_data(fwhms);
807  fwhms_y = cpl_bivector_get_y_data(fwhms);
808 
809  /* Get the good values */
810  fwhms_sum = cpl_vector_new(nb_good);
811  fwhms_sum_data = cpl_vector_get_data(fwhms_sum);
812  for (i=0; i < nb_good; i++) {
813  fwhms_sum_data[i] = fwhms_x[i] + fwhms_y[i];
814  }
815  /* Compute the fwhm as the median of the average of the FHWMs in x and y */
816  fwhm_pix = 0.5 * cpl_vector_get_median(fwhms_sum);
817 
818  bug_if(cpl_propertylist_append_double(qclist, "ESO QC FWHM PIX", fwhm_pix));
819 
820  fwhm_arcsec = fwhm_pix * pixscale;
821 
822  bug_if(cpl_propertylist_append_double(qclist, "ESO QC FWHM ARCSEC",
823  fwhm_arcsec));
824 
825  /* iq is the median of the (fwhm_x+fwhm_y)/2 */
826  /* of the good stars */
827 
828  /* Compute f_min and f_max */
829  f_min = seeing_min_arcsec / pixscale;
830  f_max = seeing_max_arcsec / pixscale;
831 
832  /* Sum the the good values */
833  nb_good = 0;
834  for (i=0 ; i < nb_val ; i++) {
835  const double fx = fwhms_x[i];
836  const double fy = fwhms_y[i];
837 
838  if (fx <= f_min || f_max <= fx || fy <= f_min || f_max <= fy) continue;
839  if (fabs(fx-fy) >= 0.5 * (fx + fy) * seeing_fwhm_var) continue;
840 
841  fwhms_sum_data[nb_good] = fx + fy;
842  nb_good++;
843  }
844 
845  cpl_msg_info(cpl_func, "%d of the apertures have FWHMs within the range "
846  "%g to %g and eccentricity within %g", nb_good, f_min, f_max,
847  seeing_fwhm_var);
848 
849  skip_if_lt (nb_good, 1, "aperture with a good FWHM");
850 
851  bug_if(cpl_vector_set_size(fwhms_sum, nb_good));
852 
853  /* Compute the fwhm */
854  iq = pixscale * 0.5 * cpl_vector_get_median(fwhms_sum);
855 
856  bug_if(cpl_propertylist_append_double(qclist, "ESO QC IQ", iq));
857 
858  end_skip;
859 
860  cpl_vector_delete(fwhms_sum);
861  cpl_bivector_delete(fwhms);
862  cpl_vector_unwrap(sigmas);
863  cpl_apertures_delete(aperts);
864 
865  return cpl_error_get_code();
866 }
867 
868 /*----------------------------------------------------------------------------*/
881 /*----------------------------------------------------------------------------*/
882 static cpl_error_code naco_img_jitter_save(cpl_frameset * set,
883  const cpl_parameterlist * parlist,
884  const cpl_propertylist * qclist,
885  const cpl_propertylist * paflist,
886  const cpl_imagelist * objimages,
887  const cpl_image * combined,
888  const cpl_image * contrib,
889  const cpl_table * cubetable)
890 {
891 
892  const cpl_boolean is_cube = cpl_table_get_ncol(cubetable) > 0
893  ? CPL_TRUE : CPL_FALSE;
894  const char * procatg = is_cube ? NACO_IMG_JITTER_CUBE
895  : NACO_IMG_JITTER_COMB;
896  cpl_propertylist * xtlist = cpl_propertylist_new();
897  const cpl_boolean save_cube =
898  naco_parameterlist_get_bool(parlist, RECIPE_STRING,
899  NACO_PARAM_SAVECUBE);
900 
901  bug_if (0);
902  bug_if (contrib == NULL);
903 
904  /* Write the FITS file */
905  skip_if (irplib_dfs_save_image(set, parlist, set, combined,
906  CPL_BPP_IEEE_FLOAT, RECIPE_STRING,
907  procatg, qclist, NULL,
908  naco_pipe_id, RECIPE_STRING CPL_DFS_FITS));
909 
910  bug_if(cpl_propertylist_append_string(xtlist, "EXTNAME",
911  "Contribution Map"));
912  skip_if (cpl_image_save(contrib, RECIPE_STRING CPL_DFS_FITS,
913  CPL_BPP_16_SIGNED, xtlist, CPL_IO_EXTEND));
914 
915  if (is_cube) {
916 
917  bug_if(cpl_propertylist_update_string(xtlist, "EXTNAME",
918  "Plane Properties"));
919  skip_if (cpl_table_save(cubetable, NULL, xtlist,
920  RECIPE_STRING CPL_DFS_FITS, CPL_IO_EXTEND));
921  }
922 
923  if (save_cube) {
924  bug_if(cpl_propertylist_update_string(xtlist, "EXTNAME",
925  "Corrected object images"));
926  skip_if (cpl_imagelist_save(objimages, RECIPE_STRING CPL_DFS_FITS,
927  CPL_BPP_IEEE_FLOAT, xtlist, CPL_IO_EXTEND));
928  }
929 
930 #ifdef NACO_SAVE_PAF
931  skip_if (cpl_dfs_save_paf("NACO", RECIPE_STRING, paflist,
932  RECIPE_STRING CPL_DFS_PAF));
933 #else
934  bug_if(paflist == NULL);
935 #endif
936 
937  end_skip;
938 
939  cpl_propertylist_delete(xtlist);
940 
941  return cpl_error_get_code();
942 
943 }
944 
945 /*----------------------------------------------------------------------------*/
957 /*----------------------------------------------------------------------------*/
958 static
959 cpl_error_code naco_img_jitter_load_objimages(cpl_imagelist * objlist,
960  cpl_array * iscube,
961  const irplib_framelist * self,
962  const cpl_parameterlist * parlist)
963 {
964  const int nframes = irplib_framelist_get_size(self);
965  cpl_image * image = NULL;
966  int i;
967 
968  bug_if(objlist == NULL);
969  bug_if(self == NULL);
970  bug_if(iscube == NULL);
971  bug_if(parlist == NULL);
972 
973  for (i=0; i < nframes; i++, image = NULL) {
974  const char * filename
975  = cpl_frame_get_filename(irplib_framelist_get_const(self, i));
976  const cpl_propertylist * plist
977  = irplib_framelist_get_propertylist_const(self, i);
978  const int naxis3 = cpl_propertylist_has(plist, "NAXIS3") ?
979  cpl_propertylist_get_int(plist, "NAXIS3") : 1;
980 
981 
982  if (naxis3 > 2) { /* Real cube mode has at least two frames + sum */
983 
984  bug_if(cpl_array_set_int(iscube, i, 1));
985 
986  /* EPompei 2009-11-13:
987  The last frame in the cube is the sum of all the previous ones.
988  This is not DICB compliant and may change.
989  See also the NACO user manual.
990  */
991 
992  image = cpl_image_load(filename, CPL_TYPE_FLOAT, naxis3-1, EXT0);
993 
994  any_if("Could not load %d-cube-sum from frame %d/%d, file=%s",
995  naxis3-1, i+1, nframes, filename);
996 
997  } else {
998  image = cpl_image_load(filename, CPL_TYPE_FLOAT, 0, EXT0);
999 
1000  any_if("Could not load FITS-image from extension %d in frame "
1001  "%d/%d, file=%s", EXT0, i+1, nframes, filename);
1002 
1003  }
1004  bug_if(cpl_imagelist_set(objlist, image, i));
1005  }
1006 
1007  end_skip;
1008 
1009  cpl_image_delete(image);
1010 
1011  return cpl_error_get_code();
1012 
1013 }
1014 
1015 /*----------------------------------------------------------------------------*/
1023 /*----------------------------------------------------------------------------*/
1024 static cpl_error_code naco_img_jitter_check_cube(const cpl_imagelist * self,
1025  const cpl_image * sum)
1026 {
1027  cpl_image * accu = cpl_imagelist_collapse_create(self);
1028  double err;
1029 
1030  cpl_image_subtract(accu, sum);
1031 
1032  err = cpl_image_get_absflux(accu);
1033 
1034  if (err > 0.0) {
1035  err /= cpl_image_get_size_x(sum) * cpl_image_get_size_y(sum);
1036 
1037  cpl_msg_info(cpl_func, "Average per-pixel error in final sum frame "
1038  "in %d-cube: %g", (int)cpl_imagelist_get_size(self), err);
1039  }
1040 
1041  cpl_image_delete(accu);
1042 
1043  return cpl_error_get_code();
1044 }
1045 
1046 
1047 /*----------------------------------------------------------------------------*/
1059 /*----------------------------------------------------------------------------*/
1060 static cpl_error_code naco_img_jitter_do_cube(cpl_imagelist * self,
1061  cpl_table * cubetable,
1062  cpl_propertylist * qclist,
1063  const cpl_array * iscube,
1064  const irplib_framelist * obj,
1065  const cpl_parameterlist * parlist)
1066 {
1067 
1068  const int nframes = irplib_framelist_get_size(obj);
1069  cpl_imagelist * onelist = NULL;
1070  cpl_image * sum = NULL;
1071  cpl_image * onesky = NULL;
1072  const int nskyplane = naco_parameterlist_get_int(parlist,
1073  RECIPE_STRING,
1074  NACO_PARAM_SKYPLANE);
1075  cpl_vector * skymedians = cpl_vector_new(nframes);
1076  int nsky = 0;
1077  int i;
1078 
1079  bug_if(self == NULL);
1080  bug_if(cubetable == NULL);
1081  bug_if(qclist == NULL);
1082  bug_if(obj == NULL);
1083  bug_if(parlist == NULL);
1084  bug_if(nframes != cpl_array_get_size(iscube));
1085  bug_if(nframes != cpl_imagelist_get_size(self));
1086 
1087 
1088  for (i=0; i < nframes; i++) {
1089  int is_invalid = 0;
1090 
1091  (void)cpl_array_get_int(iscube, i, &is_invalid);
1092 
1093  if (!is_invalid) {
1094  const char * filename
1095  = cpl_frame_get_filename(irplib_framelist_get_const(obj, i));
1096  const cpl_propertylist * plist
1097  = irplib_framelist_get_propertylist_const(obj, i);
1098  double skymedian;
1099  int naxis3;
1100 
1101  /* Estimate the sky from nearby cube(s) */
1102  cpl_image_delete(onesky);
1103  onesky = naco_img_jitter_find_sky_cube(i, nskyplane, iscube, obj);
1104  any_if("Could not estimate sky for frame %d/%d, file=%s",
1105  i+1, nframes, filename);
1106 
1107  skymedian = cpl_image_get_median(onesky);
1108 #ifdef NACO_IMG_JITTER_DEVELOPMENT
1109  if (onelist == NULL) {
1110 
1111  /* FIXME: Compute from all cube-sky frames ? */
1112  /* Get the background value */
1113  bug_if(cpl_propertylist_append_double(qclist, "ESO QC BACKGD",
1114  skymedian));
1115 
1116  cpl_image_save(onesky, "Sky.fits", CPL_BPP_IEEE_FLOAT, NULL,
1117  CPL_IO_CREATE);
1118  } else {
1119  cpl_image_save(onesky, "Sky.fits", CPL_BPP_IEEE_FLOAT, NULL,
1120  CPL_IO_EXTEND);
1121  }
1122 #endif
1123  cpl_imagelist_delete(onelist);
1124  onelist = cpl_imagelist_load(filename, CPL_TYPE_FLOAT, EXT0);
1125 
1126  any_if("Could not load cube from frame %d/%d, file=%s",
1127  i+1, nframes, filename);
1128 
1129  naxis3 = cpl_imagelist_get_size(onelist);
1130 
1131  cpl_msg_info(cpl_func, "Processing %d-cube from frame %d/%d, "
1132  "file=%s. Median of sky-estimate: %g",
1133  naxis3-1, i+1, nframes, filename, skymedian);
1134  cpl_vector_set(skymedians, nsky++, skymedian);
1135 
1136  bug_if(naxis3 < 3);
1137 
1138  sum = cpl_imagelist_unset(onelist, naxis3-1);
1139 
1140  bug_if(naco_img_jitter_check_cube(onelist, sum));
1141 
1142  skip_if(cpl_imagelist_subtract_image(onelist, onesky));
1143 
1144  cpl_image_delete(sum);
1145  sum = naco_img_jitter_combine_cube(cubetable, onelist, plist,
1146  parlist, i);
1147  any_if("Could not combine %d images from frame %d/%d, file=%s",
1148  naxis3-1, i+1, nframes, filename);
1149 
1150  bug_if(cpl_imagelist_set(self, sum, i));
1151  sum = NULL;
1152  }
1153  }
1154 
1155  if (nsky > 1) {
1156  double skystdev, skymean;
1157 
1158  bug_if(cpl_vector_set_size(skymedians, nsky));
1159 
1160  skymean = cpl_vector_get_mean(skymedians);
1161  skystdev = cpl_vector_get_stdev(skymedians);
1162 
1163  cpl_msg_info(cpl_func, "Mean and stdev of %d medians of sky estimates: "
1164  "%g %g", nsky, skymean, skystdev);
1165 
1166  bug_if(cpl_propertylist_append_double(qclist, "ESO QC SKY STDEV",
1167  skystdev));
1168  }
1169 
1170  end_skip;
1171 
1172  cpl_imagelist_delete(onelist);
1173  cpl_image_delete(sum);
1174  cpl_image_delete(onesky);
1175  cpl_vector_delete(skymedians);
1176 
1177  return cpl_error_get_code();
1178 }
1179 
1180 
1181 
1182 /*----------------------------------------------------------------------------*/
1193 /*----------------------------------------------------------------------------*/
1194 static
1195 cpl_image * naco_img_jitter_combine_cube(cpl_table * cubetable,
1196  const cpl_imagelist * cube,
1197  const cpl_propertylist * plist,
1198  const cpl_parameterlist * parlist,
1199  int idx)
1200 {
1201 
1202  cpl_errorstate prestate = cpl_errorstate_get();
1203  cpl_image * self = NULL;
1204  const int ncube = cpl_imagelist_get_size(cube);
1205  cpl_image ** combined = NULL;
1206  cpl_apertures * apert = NULL;
1207  cpl_bivector * offs = cpl_bivector_new(ncube);
1208  cpl_vector * offsx = cpl_bivector_get_x(offs);
1209  cpl_vector * offsy = cpl_bivector_get_y(offs);
1210  cpl_vector * sigmas = NULL;
1211  cpl_vector * vstrehl = cpl_vector_new(ncube);
1212  double psigmas[] = {5.0, 2.0, 1.0, 0.5}; /* not modified */
1213  const int nsigmas = (int)(sizeof(psigmas)/sizeof(double));
1214  /* Use half of default value to support images windowed to 128 x 130 */
1215  /* FIXME: Adjust according to image size ? */
1216  const double rstar = 0.5 * IRPLIB_STREHL_STAR_RADIUS;
1217  const double rbgint = 0.5 * IRPLIB_STREHL_BACKGROUND_R1;
1218  const double rbgext = 0.5 * IRPLIB_STREHL_BACKGROUND_R2;
1219  const double pixscale = naco_pfits_get_pixscale(plist);
1220  const double lucky = naco_parameterlist_get_double(parlist,
1221  RECIPE_STRING,
1222  NACO_PARAM_LUCK_STR);
1223  const char * filter = naco_pfits_get_filter(plist);
1224  double lam = DBL_MAX; /* Avoid uninit warning */
1225  double dlam = DBL_MAX; /* Avoid uninit warning */
1226  cpl_size isigma = 0;
1227  const int ncubetable = cpl_table_get_nrow(cubetable);
1228  int icubetable; /* 1st table row to write to */
1229  int i;
1230 
1231 
1232  skip_if(rbgext <= rbgint);
1233  skip_if(pixscale <= 0.0);
1234 
1235  irplib_check(naco_get_filter_infos(filter, &lam, &dlam),
1236  "Frame has no info for filter %s", filter);
1237 
1238  cpl_msg_debug(cpl_func, "Frame %d has pixelscale [Arcsecond/pixel]=%g, "
1239  "Central wavelength [micron]=%g, Filter bandwidth "
1240  "[micron]=%g", 1+idx, pixscale, lam, dlam);
1241  cpl_msg_debug(cpl_func, "Frame %d assumes Rstar [pixel]=%g, Rint "
1242  "[pixel]=%g, Rext [pixel]=%g", 1+idx, rstar/pixscale,
1243  rbgint/pixscale, rbgext/pixscale);
1244 
1245  if (cpl_table_get_ncol(cubetable) == 0) {
1246  cpl_table_new_column(cubetable, "Frame", CPL_TYPE_INT);
1247  cpl_table_new_column(cubetable, "Plane", CPL_TYPE_INT);
1248  cpl_table_new_column(cubetable, "XCentroid", CPL_TYPE_DOUBLE);
1249  cpl_table_new_column(cubetable, "YCentroid", CPL_TYPE_DOUBLE);
1250  cpl_table_set_column_unit(cubetable, "XCentroid", "pixel");
1251  cpl_table_set_column_unit(cubetable, "YCentroid", "pixel");
1252  cpl_table_new_column(cubetable, "Strehl", CPL_TYPE_DOUBLE);
1253  cpl_table_new_column(cubetable, "Strehl_Error", CPL_TYPE_DOUBLE);
1254  cpl_table_new_column(cubetable, "Star_Background", CPL_TYPE_DOUBLE);
1255  cpl_table_new_column(cubetable, "Star_Peak", CPL_TYPE_DOUBLE);
1256  cpl_table_new_column(cubetable, "Star_Flux", CPL_TYPE_DOUBLE);
1257  cpl_table_new_column(cubetable, "PSF_Peak_per_Flux", CPL_TYPE_DOUBLE);
1258  cpl_table_set_column_unit(cubetable, "PSF_Peak_per_Flux", "unitless");
1259  cpl_table_new_column(cubetable, "Background_Noise", CPL_TYPE_DOUBLE);
1260  cpl_table_set_size(cubetable, ncube);
1261  icubetable = 0;
1262  } else {
1263  cpl_table_set_size(cubetable, ncubetable + ncube);
1264  icubetable = ncubetable;
1265  }
1266 
1267  /* Create the vector for the detection thresholds */
1268  sigmas = cpl_vector_wrap(nsigmas, psigmas);
1269  for (i = 0; i < ncube; i++) {
1270  const cpl_image * one = cpl_imagelist_get_const(cube, i);
1271  double cent_x, cent_y;
1272  double strehl, strehl_err, star_bg,star_peak, star_flux;
1273  double psf_peak, psf_flux, bg_noise;
1274  int iflux;
1275 
1276 
1277  cpl_apertures_delete(apert);
1278  apert = cpl_apertures_extract(one, sigmas, &isigma);
1279 
1280  any_if("No object found in image %d/%d in frame %d", i+1, ncube, 1+idx);
1281 
1282  bug_if(irplib_apertures_find_max_flux(apert, &iflux, 1));
1283 
1284  cent_x = cpl_apertures_get_centroid_x(apert, iflux);
1285  cent_y = cpl_apertures_get_centroid_y(apert, iflux);
1286  cpl_vector_set(offsx, i, cent_x);
1287  cpl_vector_set(offsy, i, cent_y);
1288 
1289  cpl_msg_debug(cpl_func, "Detected %d/%d sigma=%g-aperture(s) (x,y)"
1290  "=(%g,%g) (%d/%d) in %d/%d-image in frame %d", iflux,
1291  (int)cpl_apertures_get_size(apert), psigmas[isigma],
1292  cent_x, cent_y, 1+(int)isigma, nsigmas, 1+i, ncube,
1293  1+idx);
1294 
1295  cpl_table_set_int(cubetable, "Frame", i+icubetable, 1+idx);
1296  cpl_table_set_int(cubetable, "Plane", i+icubetable, 1+i);
1297  cpl_table_set_double(cubetable, "XCentroid", i+icubetable, cent_x);
1298  cpl_table_set_double(cubetable, "YCentroid", i+icubetable, cent_y);
1299 
1300 
1301  if (naco_strehl_compute(one, parlist, RECIPE_STRING, lam, dlam,
1302  cent_x, cent_y, pixscale,
1303  &strehl, &strehl_err,
1304  &star_bg, &star_peak, &star_flux,
1305  &psf_peak, &psf_flux,
1306  &bg_noise)) {
1307  cpl_msg_info(cpl_func, "Could not compute strehl for "
1308  "image %d in frame %d", 1+i, 1+idx);
1309  cpl_errorstate_dump(prestate, CPL_FALSE,
1310  irplib_errorstate_dump_debug);
1311  cpl_errorstate_set(prestate);
1312 
1313  cpl_table_set_invalid(cubetable, "Strehl", i+icubetable);
1314  cpl_table_set_invalid(cubetable, "Strehl_Error", i+icubetable);
1315  cpl_table_set_invalid(cubetable, "Star_Background", i+icubetable);
1316  cpl_table_set_invalid(cubetable, "Star_Peak", i+icubetable);
1317  cpl_table_set_invalid(cubetable, "Star_Flux", i+icubetable);
1318  cpl_table_set_invalid(cubetable, "PSF_Peak_per_Flux", i+icubetable);
1319  cpl_table_set_invalid(cubetable, "Background_Noise", i+icubetable);
1320  cpl_vector_set(vstrehl, i, 0.0);
1321 
1322  } else {
1323  cpl_vector_set(vstrehl, i, strehl);
1324  cpl_table_set_double(cubetable, "Strehl", i+icubetable, strehl);
1325  cpl_table_set_double(cubetable, "Strehl_Error", i+icubetable,
1326  strehl_err);
1327  cpl_table_set_double(cubetable, "Star_Background", i+icubetable,
1328  star_bg);
1329  cpl_table_set_double(cubetable, "Star_Peak", i+icubetable,
1330  star_peak);
1331  cpl_table_set_double(cubetable, "Star_Flux", i+icubetable,
1332  star_flux);
1333  cpl_table_set_double(cubetable, "PSF_Peak_per_Flux", i+icubetable,
1334  psf_peak/psf_flux);
1335  cpl_table_set_double(cubetable, "Background_Noise", i+icubetable,
1336  bg_noise);
1337  }
1338  }
1339 
1340  self = naco_img_jitter_saa_lucky(cube, vstrehl, offs, lucky);
1341 
1342  end_skip;
1343 
1344  if (combined != NULL) {
1345  cpl_image_delete(combined[0]);
1346  cpl_free(combined);
1347  }
1348 
1349  cpl_bivector_delete(offs);
1350  (void)cpl_vector_unwrap(sigmas);
1351  cpl_apertures_delete(apert);
1352  cpl_vector_delete(vstrehl);
1353 
1354  cpl_ensure(self != NULL, cpl_error_get_code(), NULL);
1355 
1356  return self;
1357 
1358 }
1359 
1360 
1361 /*----------------------------------------------------------------------------*/
1371 /*----------------------------------------------------------------------------*/
1372 static cpl_image * naco_img_jitter_find_sky_cube(int isky, int nskyplane,
1373  const cpl_array * iscube,
1374  const irplib_framelist * obj)
1375 {
1376 
1377  cpl_image * self = NULL;
1378  const int nframes = irplib_framelist_get_size(obj);
1379  cpl_imagelist * belowcube = NULL;
1380  cpl_imagelist * abovecube = NULL;
1381  cpl_imagelist * skycube = NULL;
1382  cpl_imagelist * mycube = NULL;
1383  cpl_image * mysky = NULL;
1384  cpl_mask * fillbpm = NULL;
1385  int is_invalid = 0;
1386  int ibelow, iabove;
1387  int i;
1388 
1389  bug_if(isky < 0);
1390  bug_if(isky >= nframes);
1391  bug_if(nframes != cpl_array_get_size(iscube));
1392  if (nskyplane > 0) skip_if_lt(nskyplane, 3, "sky planes for median");
1393 
1394 
1395  (void)cpl_array_get_int(iscube, isky, &is_invalid);
1396 
1397  bug_if(is_invalid); /* isky must be a cube */
1398 
1399  /* Find 1st cube below isky */
1400  for (i = isky - 1; i >= 0; i++) {
1401 
1402  (void)cpl_array_get_int(iscube, i, &is_invalid);
1403 
1404  if (!is_invalid) break;
1405  }
1406 
1407  ibelow = i; /* -1 means no cube below */
1408 
1409  /* Find 1st cube above isky */
1410  for (i = isky + 1; i < nframes; i++) {
1411 
1412  (void)cpl_array_get_int(iscube, i, &is_invalid);
1413 
1414  if (!is_invalid) break;
1415  }
1416 
1417  iabove = i; /* nframes means no cube above */
1418 
1419  cpl_msg_info(cpl_func, "Estimating sky for cube %d/%d via cubes %d and %d",
1420  1+isky, nframes, 1+ibelow, 1+iabove);
1421 
1422 
1423  if (ibelow >= 0) {
1424  const char * filename
1425  = cpl_frame_get_filename(irplib_framelist_get_const(obj, ibelow));
1426 
1427  if (nskyplane > 0) {
1428  /* Load the last nskyplane frames */
1429  const cpl_propertylist * plist
1430  = irplib_framelist_get_propertylist_const(obj, ibelow);
1431  /* Ignore the last sum plane */
1432  const int istop = irplib_pfits_get_int(plist, "NAXIS3") - 2;
1433  const int istart = NACO_MAX(0, istop - nskyplane + 1);
1434 
1435  skip_if_lt(istop - istart, 2, "sky planes for median");
1436 
1437  belowcube = cpl_imagelist_new();
1438 
1439  for (i = istart; i <= istop; i++) {
1440  self = cpl_image_load(filename, CPL_TYPE_FLOAT, i, EXT0);
1441 
1442  any_if("Could not load plane %d from frame %d/%d, file=%s",
1443  1+i, 1+ibelow, nframes, filename);
1444 
1445  bug_if(cpl_imagelist_set(belowcube, self, i - istart));
1446  }
1447 
1448  } else {
1449  int naxis3;
1450 
1451  belowcube = cpl_imagelist_load(filename, CPL_TYPE_FLOAT, EXT0);
1452 
1453  any_if("Could not load cube from frame %d/%d, file=%s",
1454  1+ibelow, nframes, filename);
1455 
1456  naxis3 = cpl_imagelist_get_size(belowcube);
1457 
1458  bug_if(naxis3 < 3);
1459 
1460  cpl_image_delete(cpl_imagelist_unset(belowcube, naxis3-1));
1461 
1462  }
1463 
1464  }
1465 
1466  if (iabove < nframes) {
1467  const char * filename
1468  = cpl_frame_get_filename(irplib_framelist_get_const(obj, iabove));
1469 
1470  if (nskyplane > 0) {
1471  /* Load the first nskyplane frames */
1472  const cpl_propertylist * plist
1473  = irplib_framelist_get_propertylist_const(obj, iabove);
1474  const int istart = 0;
1475  /* Ignore the last sum plane */
1476  const int istop = NACO_MIN(nskyplane-1, irplib_pfits_get_int
1477  (plist, "NAXIS3") - 2);
1478 
1479  skip_if_lt(istop - istart, 2, "sky planes for median");
1480 
1481  abovecube = cpl_imagelist_new();
1482 
1483  for (i = istart; i <= istop; i++) {
1484  self = cpl_image_load(filename, CPL_TYPE_FLOAT, i, EXT0);
1485 
1486  any_if("Could not load plane %d from frame %d/%d, file=%s",
1487  1+i, 1+iabove, nframes, filename);
1488 
1489  bug_if(cpl_imagelist_set(abovecube, self, i - istart));
1490  }
1491 
1492  } else {
1493 
1494  int naxis3;
1495 
1496  abovecube = cpl_imagelist_load(filename, CPL_TYPE_FLOAT, EXT0);
1497 
1498  any_if("Could not load cube from frame %d/%d, file=%s",
1499  1+iabove, nframes, filename);
1500 
1501  naxis3 = cpl_imagelist_get_size(abovecube);
1502 
1503  bug_if(naxis3 < 3);
1504 
1505  cpl_image_delete(cpl_imagelist_unset(abovecube, naxis3-1));
1506  }
1507 
1508  }
1509 
1510  error_if(belowcube == NULL && abovecube == NULL, CPL_ERROR_DATA_NOT_FOUND,
1511  "No cube(s) available for sky estimation among %d object frames",
1512  nframes);
1513 
1514  if (belowcube == NULL) {
1515  skycube = abovecube;
1516  } else if (abovecube == NULL) {
1517  skycube = belowcube;
1518  } else {
1519  /* Wrap around the images in the two cubes */
1520 
1521  const int nbelow = cpl_imagelist_get_size(belowcube);
1522  const int nabove = cpl_imagelist_get_size(abovecube);
1523  int nwrap = 0;
1524 
1525  skycube = cpl_imagelist_new();
1526 
1527  for (i = 0; i < nbelow; i++) {
1528  skip_if(cpl_imagelist_set(skycube, cpl_imagelist_get(belowcube, i),
1529  nwrap++));
1530  }
1531  for (i = 0; i < nabove; i++) {
1532  skip_if(cpl_imagelist_set(skycube, cpl_imagelist_get(abovecube, i),
1533  nwrap++));
1534  }
1535  skip_if(cpl_imagelist_get_size(skycube) != nwrap);
1536  skip_if(nbelow + nabove != nwrap);
1537  }
1538 
1539  self = cpl_imagelist_collapse_median_create(skycube);
1540 
1541 #ifndef NACO_IMG_JITTER_KEEP_SKY_OBJECTS
1542  if (belowcube == NULL || abovecube == NULL) {
1543  /* Try to replace object-pixels with sky-pixels from the object image */
1544  const cpl_mask * fill2bpm;
1545  const cpl_mask * selfbpm = NULL;
1546  const double lo_skysigma = 0.2;
1547  const double hi_skysigma = 5.0;
1548 
1549  skip_if(naco_img_jitter_reject_objects(self, lo_skysigma, hi_skysigma));
1550 
1551  selfbpm = cpl_image_get_bpm_const(self);
1552 
1553  if (selfbpm != NULL) {
1554  const cpl_mask * mybpm;
1555 
1556  /* Substitute the objects pixels with sky pixels from isky */
1557 
1558  const char * filename
1559  = cpl_frame_get_filename(irplib_framelist_get_const(obj, isky));
1560  int naxis3;
1561 
1562  mycube = cpl_imagelist_load(filename, CPL_TYPE_FLOAT, EXT0);
1563 
1564  any_if("Could not load cube from frame %d/%d, file=%s",
1565  1+isky, nframes, filename);
1566 
1567  naxis3 = cpl_imagelist_get_size(mycube);
1568 
1569  bug_if(naxis3 < 3);
1570 
1571  cpl_image_delete(cpl_imagelist_unset(mycube, naxis3-1));
1572 
1573  mysky = cpl_imagelist_collapse_median_create(mycube);
1574 
1575  skip_if(naco_img_jitter_reject_objects(mysky, lo_skysigma,
1576  hi_skysigma));
1577 
1578  /* When a pixel is bad in self and good in mysky: Set to mysky */
1579  /* Other pixels in self are unchanged */
1580 
1581  mybpm = cpl_image_get_bpm_const(mysky);
1582 
1583  if (mybpm == NULL) {
1584  /* Fill all bad pixels in self with values from mysky */
1585  fill2bpm = selfbpm;
1586  } else {
1587  /* Fill those bad pixels in self with values from mysky
1588  when those pixels are good */
1589  fillbpm = cpl_mask_duplicate(mybpm);
1590  bug_if(cpl_mask_not(fillbpm));
1591  bug_if(cpl_mask_and(fillbpm, selfbpm));
1592 
1593  fill2bpm = fillbpm;
1594 
1595  }
1596  cpl_msg_info(cpl_func, "Filling %d object-pixels in sky image "
1597  "with %d sky-pixels from object image",
1598  (int)cpl_mask_count(selfbpm),
1599  (int)cpl_mask_count(fill2bpm));
1600  if (fill2bpm != selfbpm) {
1601  /* These rejected pixels will be filled */
1602  bug_if(cpl_image_reject_from_mask(self, fill2bpm));
1603  }
1604 
1605  if (fillbpm == NULL) {
1606  fillbpm = cpl_mask_duplicate(fill2bpm);
1607  } else if (fillbpm != fill2bpm) {
1608  bug_if(cpl_mask_copy(fillbpm, fill2bpm, 1, 1));
1609  }
1610 
1611  bug_if(cpl_image_fill_rejected(self, 0.0)); /* Use addition to fill */
1612  bug_if(cpl_image_accept_all(self)); /* fill2bpm may be invalid now */
1613 
1614  bug_if(cpl_mask_not(fillbpm));
1615  bug_if(cpl_image_reject_from_mask(mysky, fillbpm));
1616  bug_if(cpl_image_fill_rejected(mysky, 0.0)); /* Use addition to fill */
1617  bug_if(cpl_image_accept_all(mysky));
1618  bug_if(cpl_image_add(self, mysky));
1619  }
1620  }
1621 #endif
1622 
1623  end_skip;
1624 
1625  if (cpl_error_get_code()) {
1626  cpl_image_delete(self);
1627  self = NULL;
1628  }
1629 
1630 
1631  if (skycube != belowcube && skycube != abovecube) {
1632  int nwrap = cpl_imagelist_get_size(skycube);
1633 
1634  /* Unwrap the wrapped images */
1635  for (;nwrap > 0;) {
1636  (void)cpl_imagelist_unset(skycube, --nwrap);
1637  }
1638 
1639  cpl_imagelist_delete(skycube);
1640  }
1641 
1642  cpl_mask_delete(fillbpm);
1643  cpl_image_delete(mysky);
1644  cpl_imagelist_delete(mycube);
1645  cpl_imagelist_delete(belowcube);
1646  cpl_imagelist_delete(abovecube);
1647 
1648  return self;
1649 }
1650 
1651 
1652 /*----------------------------------------------------------------------------*/
1663 /*----------------------------------------------------------------------------*/
1664 static
1665 cpl_error_code naco_img_jitter_imagelist_wrap_nocube(cpl_imagelist * self,
1666  const cpl_array * iscube,
1667  cpl_imagelist * other)
1668 {
1669 
1670  const int ncube = cpl_imagelist_get_size(other);
1671  int nwrap = cpl_imagelist_get_size(self);
1672  int i;
1673 
1674  bug_if(self == NULL);
1675  bug_if(iscube == NULL);
1676  bug_if(other == NULL);
1677  bug_if(nwrap != 0);
1678  bug_if(cpl_array_get_size(iscube) != ncube);
1679 
1680  for (i = 0; i < ncube; i++) {
1681  int is_invalid;
1682 
1683  (void)cpl_array_get_int(iscube, i, &is_invalid);
1684 
1685  if (is_invalid) {
1686  cpl_imagelist_set(self, cpl_imagelist_get(other, i), nwrap);
1687  nwrap++;
1688  }
1689  }
1690 
1691  bug_if(cpl_imagelist_get_size(self) != nwrap);
1692 
1693  end_skip;
1694 
1695  return cpl_error_get_code();
1696 }
1697 
1698 
1699 #ifndef NACO_IMG_JITTER_KEEP_SKY_OBJECTS
1700 
1701 /*----------------------------------------------------------------------------*/
1712 /*----------------------------------------------------------------------------*/
1713 static cpl_error_code naco_img_jitter_reject_objects(cpl_image * self,
1714  double lo_sigma,
1715  double hi_sigma)
1716 {
1717  double median;
1718  double med_dist = DBL_MAX;
1719  double hi_threshold;
1720  cpl_mask * hi_objects = NULL;
1721  cpl_mask * lo_objects = NULL;
1722  cpl_mask * rejects = NULL;
1723  cpl_image * hi_label = NULL;
1724  cpl_image * lo_label = NULL;
1725  cpl_apertures * hi_apert = NULL;
1726  cpl_mask * kernel = NULL;
1727 
1728 
1729  bug_if(self == NULL);
1730  bug_if(lo_sigma <= 0.0);
1731  bug_if(hi_sigma < lo_sigma);
1732 
1733  /* Compute the threshold */
1734  median = cpl_image_get_median_dev(self, &med_dist);
1735  hi_threshold = median + hi_sigma * med_dist;
1736 
1737  /* Binarise the image with the high sigma threshold */
1738  hi_objects = cpl_mask_threshold_image_create(self, hi_threshold, DBL_MAX);
1739  bug_if(hi_objects == NULL);
1740 
1741  /* Apply a morphological opening to remove the single pixel detections */
1742  /* Copied from cpl_apertures_extract_sigma() */
1743  kernel = cpl_mask_new(3, 3);
1744  bug_if(cpl_mask_not(kernel));
1745  bug_if(cpl_mask_filter(hi_objects, hi_objects, kernel, CPL_FILTER_OPENING,
1746  CPL_BORDER_ZERO));
1747 
1748  if (!cpl_mask_is_empty(hi_objects)) {
1749  /* Any low-sigma aperture overlapping a high-sigma
1750  aperture is assumed to be (all of) an object */
1751 
1752  const double lo_threshold = median + lo_sigma * med_dist;
1753  cpl_size hi_ilabel, hi_nlabel, lo_nlabel;
1754 
1755  /* Binarise the image with the low sigma threshold */
1756  lo_objects = cpl_mask_threshold_image_create(self, lo_threshold, DBL_MAX);
1757  bug_if(lo_objects == NULL);
1758  bug_if(cpl_mask_filter(lo_objects, lo_objects, kernel,
1759  CPL_FILTER_OPENING, CPL_BORDER_ZERO));
1760 
1761  hi_label = cpl_image_labelise_mask_create(hi_objects, &hi_nlabel);
1762  lo_label = cpl_image_labelise_mask_create(lo_objects, &lo_nlabel);
1763 
1764  hi_apert = cpl_apertures_new_from_image(self, hi_label);
1765  bug_if(hi_apert == NULL);
1766 
1767  for (hi_ilabel = 1; hi_ilabel <= hi_nlabel; hi_ilabel++) {
1768  /* Get one pixel from the high-sigma aperture */
1769  const int pos_x = cpl_apertures_get_top_x(hi_apert, hi_ilabel);
1770  const int pos_y = cpl_apertures_get_top(hi_apert, hi_ilabel);
1771  /* The corresponding low-sigma aperture */
1772  int is_rejected;
1773  const int lo_ilabel = (int)cpl_image_get(lo_label, pos_x, pos_y,
1774  &is_rejected);
1775 
1776  /* The mask of pixels with the corresponding low-sigma aperture */
1777  cpl_mask_delete(rejects);
1778  rejects = cpl_mask_threshold_image_create(lo_label,
1779  (double)lo_ilabel - 0.5,
1780  (double)lo_ilabel + 0.5);
1781 
1782  /* Add to the rejection mask */
1783  cpl_mask_or(hi_objects, rejects);
1784 
1785  }
1786 
1787  cpl_msg_info(cpl_func, "Found %d object(s) of %d pixel(s) "
1788  "in sky image using sigmas %g and %g", (int)hi_nlabel,
1789  (int)cpl_mask_count(hi_objects), lo_sigma, hi_sigma);
1790  bug_if(cpl_image_reject_from_mask(self, hi_objects));
1791 
1792  }
1793 
1794  end_skip;
1795 
1796  cpl_apertures_delete(hi_apert);
1797  cpl_image_delete(hi_label);
1798  cpl_image_delete(lo_label);
1799  cpl_mask_delete(kernel);
1800  cpl_mask_delete(hi_objects);
1801  cpl_mask_delete(lo_objects);
1802  cpl_mask_delete(rejects);
1803 
1804  return cpl_error_get_code();
1805 }
1806 #endif
1807 
1808 
1809 
1810 /*----------------------------------------------------------------------------*/
1820 /*----------------------------------------------------------------------------*/
1821 static cpl_image * naco_img_jitter_saa_lucky(const cpl_imagelist * cube,
1822  const cpl_vector * strehl,
1823  const cpl_bivector * offs,
1824  double fraction)
1825 {
1826 
1827  cpl_image * self = NULL;
1828  const int ncube = cpl_imagelist_get_size(cube);
1829  const int mcube = NACO_MAX(NACO_MIN(ncube, (int)(0.5 + fraction * ncube)),
1830  1);
1831  cpl_imagelist * lcube = NULL;
1832  const cpl_imagelist * ucube;
1833  cpl_vector * lstrehl = NULL;
1834  /* Always need to duplicate due to pesky offset convention in
1835  cpl_geom_img_offset_saa() */
1836  cpl_bivector * loffs = cpl_bivector_duplicate(offs);
1837  cpl_vector * loffsx = cpl_bivector_get_x(loffs);
1838  cpl_vector * loffsy = cpl_bivector_get_y(loffs);
1839  cpl_table * tsort = NULL;
1840  cpl_propertylist * psort = NULL;
1841  int i;
1842 
1843 
1844  bug_if(cpl_vector_get_size(strehl) != ncube);
1845  bug_if(cpl_bivector_get_size(offs) != ncube);
1846  bug_if(fraction <= 0.0);
1847  bug_if(fraction > 1.0);
1848 
1849  if (mcube < ncube) {
1850  double strehlmin; /* Smallest to be used */
1851  int * pindex;
1852 
1853  lstrehl = cpl_vector_duplicate(strehl);
1854 
1855  tsort = cpl_table_new(ncube);
1856  psort = cpl_propertylist_new();
1857 
1858  /* Largest Strehl 1st */
1859  bug_if(cpl_propertylist_append_bool(psort, "LSTREHL", CPL_TRUE));
1860 
1861  bug_if(cpl_table_wrap_double(tsort, cpl_vector_get_data(lstrehl),
1862  "LSTREHL"));
1863  bug_if(cpl_table_wrap_double(tsort, cpl_vector_get_data(loffsx),
1864  "LOFFSX"));
1865  bug_if(cpl_table_wrap_double(tsort, cpl_vector_get_data(loffsy),
1866  "LOFFSY"));
1867  bug_if(cpl_table_new_column(tsort, "INDEX", CPL_TYPE_INT));
1868 
1869  /* The indices for the imagelist */
1870  pindex = cpl_table_get_data_int(tsort, "INDEX");
1871  for (i = 0; i < ncube; i++) {
1872  pindex[i] = i;
1873  }
1874 
1875  bug_if(cpl_table_sort(tsort, psort));
1876  /* Strehl, offsets and the (imagelist) indices have been sorted */
1877 
1878  lcube = cpl_imagelist_new();
1879 
1880  /* Use the index column to create a sorted list of wrapped images */
1881  for (i = 0; i < mcube; i++) {
1882  const int j = pindex[i];
1883  const cpl_image * image = cpl_imagelist_get_const(cube, j);
1884 
1885  cpl_imagelist_set(lcube, (cpl_image*)image, i);
1886  }
1887  /* tsort no longer accessed */
1888 
1889  /* loffs and the imagelist must both have length mcube */
1890  bug_if(cpl_vector_set_size(loffsx, mcube));
1891  bug_if(cpl_vector_set_size(loffsy, mcube));
1892 
1893  strehlmin = cpl_vector_get(lstrehl, mcube - 1);
1894  cpl_vector_delete(lstrehl);
1895  lstrehl = NULL;
1896 
1897  cpl_msg_info(cpl_func, "%g%% (%d/%d) lucky mode at Strehl=%g",
1898  100.0*fraction, mcube, ncube, strehlmin);
1899  }
1900  ucube = lcube ? lcube : cube;
1901 
1902  self = naco_img_jitter_saa_center(ucube, loffs);
1903  any_if("Could not center and saa %d-cube", ncube);
1904 
1905  end_skip;
1906 
1907  if (lcube != NULL) {
1908  /* Unwrap the wrapped images */
1909  for (i = cpl_imagelist_get_size(lcube); i > 0;) {
1910  (void)cpl_imagelist_unset(lcube, --i);
1911  }
1912 
1913  cpl_imagelist_delete(lcube);
1914  }
1915 
1916  cpl_vector_delete(lstrehl);
1917 
1918  cpl_bivector_delete(loffs);
1919 
1920  if (tsort != NULL) {
1921  if (cpl_table_has_column(tsort, "LSTREHL"))
1922  (void)cpl_table_unwrap(tsort, "LSTREHL");
1923  if (cpl_table_has_column(tsort, "LOFFSX"))
1924  (void)cpl_table_unwrap(tsort, "LOFFSX");
1925  if (cpl_table_has_column(tsort, "LOFFSY"))
1926  (void)cpl_table_unwrap(tsort, "LOFFSY");
1927  cpl_table_delete(tsort);
1928  }
1929  cpl_propertylist_delete(psort);
1930 
1931  return self;
1932 }
1933 
1934 
1935 /*----------------------------------------------------------------------------*/
1943 /*----------------------------------------------------------------------------*/
1944 static
1945 cpl_error_code naco_img_jitter_find_strehl(const cpl_imagelist * self,
1946  const cpl_parameterlist * parlist,
1947  const irplib_framelist * objframes)
1948 {
1949 
1950  const int nobj = irplib_framelist_get_size(objframes);
1951  cpl_apertures * apert = NULL;
1952  cpl_vector * sigmas = NULL;
1953  double psigmas[] = {5.0, 2.0, 1.0, 0.5}; /* not modified */
1954  const int nsigmas = (int)(sizeof(psigmas)/sizeof(double));
1955  cpl_size isigma = 0;
1956  int i;
1957 
1958 
1959  bug_if(cpl_imagelist_get_size(self) != nobj);
1960 
1961  /* Create the vector for the detection thresholds */
1962  sigmas = cpl_vector_wrap(nsigmas, psigmas);
1963 
1964  for (i = 0; i < nobj; i++) {
1965  const cpl_propertylist * plist
1966  = irplib_framelist_get_propertylist_const(objframes, i);
1967  const cpl_image * oimage = cpl_imagelist_get_const(self, i);
1968 
1969  const double pixscale = naco_pfits_get_pixscale(plist);
1970  const char * filter = naco_pfits_get_filter(plist);
1971  double lam = DBL_MAX; /* Avoid uninit warning */
1972  double dlam = DBL_MAX; /* Avoid uninit warning */
1973 
1974  double cent_x, cent_y;
1975  double strehl = 0, strehl_err, star_bg,star_peak, star_flux;
1976  double psf_peak, psf_flux, bg_noise;
1977  int iflux;
1978 
1979 
1980  skip_if(pixscale <= 0.0);
1981 
1982  irplib_check(naco_get_filter_infos(filter, &lam, &dlam),
1983  "Frame %d has no info for filter %s", 1+i, filter);
1984 
1985  cpl_apertures_delete(apert);
1986  apert = cpl_apertures_extract(oimage, sigmas, &isigma);
1987 
1988  any_if("No object found in combined image of frame %d", 1+i);
1989 
1990  bug_if(irplib_apertures_find_max_flux(apert, &iflux, 1));
1991 
1992  cent_x = cpl_apertures_get_centroid_x(apert, iflux);
1993  cent_y = cpl_apertures_get_centroid_y(apert, iflux);
1994 
1995  skip_if (naco_strehl_compute(oimage, parlist, RECIPE_STRING, lam, dlam,
1996  cent_x, cent_y, pixscale,
1997  &strehl, &strehl_err,
1998  &star_bg, &star_peak, &star_flux,
1999  &psf_peak, &psf_flux,
2000  &bg_noise));
2001  cpl_msg_info(cpl_func, "Image of frame %d/%d has strehl=%g at (x,y)"
2002  "=(%g,%g)", 1+i, nobj, strehl, cent_x, cent_y);
2003  }
2004 
2005  end_skip;
2006 
2007  (void)cpl_vector_unwrap(sigmas);
2008  cpl_apertures_delete(apert);
2009 
2010  return cpl_error_get_code();
2011 }
2012 
2013 
2014 
2015 /*----------------------------------------------------------------------------*/
2023 /*----------------------------------------------------------------------------*/
2024 static cpl_image * naco_img_jitter_saa_center(const cpl_imagelist * cube,
2025  cpl_bivector * offs)
2026 {
2027 
2028  const int ncube = cpl_imagelist_get_size(cube);
2029  cpl_image * self = NULL;
2030  cpl_image ** combined = NULL;
2031  cpl_imagelist * ccube = NULL;
2032  const cpl_imagelist * ucube;
2033  cpl_vector * offsx = cpl_bivector_get_x(offs);
2034  cpl_vector * offsy = cpl_bivector_get_y(offs);
2035  double * doffsx = cpl_vector_get_data(offsx);
2036  double * doffsy = cpl_vector_get_data(offsy);
2037  const double med_x = cpl_vector_get_median_const(offsx);
2038  const double med_y = cpl_vector_get_median_const(offsy);
2039  double pos_x, pos_y;
2040  double minsqdist = DBL_MAX;
2041  int imin = -1;
2042  int i;
2043 
2044 
2045  bug_if(cpl_bivector_get_size(offs) != ncube);
2046 
2047  /* Find image with object closest to object median location */
2048  for (i = 0; i < ncube; i++) {
2049  const double x = cpl_vector_get(offsx, i);
2050  const double y = cpl_vector_get(offsy, i);
2051  const double sqdist
2052  = (x - med_x) * (x - med_x) + (y - med_y) * (y - med_y);
2053 
2054  if (sqdist < minsqdist) {
2055  minsqdist = sqdist;
2056  imin = i;
2057  }
2058  }
2059 
2060  cpl_msg_info(cpl_func, "Plane %d/%d has minimal object distance %g "
2061  "from median (x,y)=(%g,%g)", 1+imin, ncube,
2062  sqrt(minsqdist), med_x, med_y);
2063 
2064 
2065  if (imin > 0) {
2066  /* Wrap ccube around the images - with image imin first */
2067  /* - the images from cube will _not_ be modified */
2068  const cpl_image * image = cpl_imagelist_get_const(cube, imin);
2069 
2070  ccube = cpl_imagelist_new();
2071 
2072  bug_if(cpl_imagelist_set(ccube, (cpl_image*)image, 0));
2073 
2074  for (i = 0; i < imin; i++) {
2075  image = cpl_imagelist_get_const(cube, i);
2076  bug_if(cpl_imagelist_set(ccube, (cpl_image*)image, 1+i));
2077  }
2078  for (i = 1+imin; i < ncube; i++) {
2079  image = cpl_imagelist_get_const(cube, i);
2080  bug_if(cpl_imagelist_set(ccube, (cpl_image*)image, i));
2081  }
2082 
2083  bug_if(cpl_imagelist_get_size(ccube) != ncube);
2084 
2085  /* Reorder offs accordingly */
2086  pos_x = cpl_vector_get(offsx, imin);
2087  pos_y = cpl_vector_get(offsy, imin);
2088 
2089  /* Move all offsets below imin 1 place up */
2090  (void)memmove(doffsx + 1, doffsx, (size_t)imin * sizeof(*doffsx));
2091  (void)memmove(doffsy + 1, doffsy, (size_t)imin * sizeof(*doffsy));
2092 
2093  /* Copy the imin offset to the 1st element */
2094  cpl_vector_set(offsx, 0, pos_x);
2095  cpl_vector_set(offsy, 0, pos_y);
2096  }
2097  ucube = ccube ? ccube : cube;
2098 
2099  /* Strange convention for the offsets :-(((((((((((((((( */
2100  cpl_vector_subtract_scalar(offsx, cpl_vector_get(offsx, 0));
2101  cpl_vector_subtract_scalar(offsy, cpl_vector_get(offsy, 0));
2102  cpl_vector_multiply_scalar(offsx, -1.0);
2103  cpl_vector_multiply_scalar(offsy, -1.0);
2104 
2105  combined = cpl_geom_img_offset_saa(ucube, offs, CPL_KERNEL_DEFAULT,
2106  0, 0, CPL_GEOM_FIRST, &pos_x, &pos_y);
2107 
2108  any_if("Could not shift and add %d-cube", ncube);
2109 
2110  cpl_msg_info(cpl_func, "Shift-and-added %d-cube, 1st pos=(%g,%g)",
2111  ncube, pos_x, pos_y);
2112 
2113  self = combined[0];
2114  cpl_image_delete(combined[1]);
2115  combined[0] = NULL;
2116  combined[1] = NULL;
2117 
2118  end_skip;
2119 
2120  if (combined != NULL) {
2121  cpl_image_delete(combined[0]);
2122  cpl_image_delete(combined[1]);
2123  cpl_free(combined);
2124  }
2125 
2126  if (ccube != NULL) {
2127  /* Unwrap the wrapped no-cube images */
2128  for (i = cpl_imagelist_get_size(ccube); i > 0;) {
2129  (void)cpl_imagelist_unset(ccube, --i);
2130  }
2131 
2132  cpl_imagelist_delete(ccube);
2133  }
2134 
2135  return self;
2136 }
naco_pfits_get_cumoffsety
double naco_pfits_get_cumoffsety(const cpl_propertylist *self)
find out the cumulative offset in Y
Definition: naco_pfits.c:107
naco_pfits_get_cumoffsetx
double naco_pfits_get_cumoffsetx(const cpl_propertylist *self)
find out the cumulative offset in X
Definition: naco_pfits.c:95
naco_strehl_compute
cpl_error_code naco_strehl_compute(const cpl_image *self, const cpl_parameterlist *parlist, const char *recipename, double lam, double dlam, double pos_x, double pos_y, double pixscale, double *pstrehl, double *pstrehl_err, double *pstar_bg, double *pstar_peak, double *pstar_flux, double *ppsf_peak, double *ppsf_flux, double *pbg_noise)
Compute the strehl ratio in an image.
Definition: naco_strehl.c:80
irplib_pfits_set_airmass
cpl_error_code irplib_pfits_set_airmass(cpl_propertylist *self, const irplib_framelist *rawframes)
Update/Set the AIRMASS property.
Definition: irplib_pfits.c:373
naco_parameterlist_get_double
double naco_parameterlist_get_double(const cpl_parameterlist *self, const char *recipe, naco_parameter bitmask)
Retrieve the value of a NACO parameter of type double.
Definition: naco_parameter.c:538
naco_parameterlist_get_string
const char * naco_parameterlist_get_string(const cpl_parameterlist *self, const char *recipe, naco_parameter bitmask)
Retrieve the value of a NACO string parameter.
Definition: naco_parameter.c:604
naco_pfits_get_filter
const char * naco_pfits_get_filter(const cpl_propertylist *self)
find out the filter
Definition: naco_pfits.c:167
naco_pfits_get_pixscale
double naco_pfits_get_pixscale(const cpl_propertylist *self)
find out the pixel scale
Definition: naco_pfits.c:341
naco_pfits_get_opti4_name
const char * naco_pfits_get_opti4_name(const cpl_propertylist *self)
find out the OPTI4.NAME key
Definition: naco_pfits.c:316
naco_pfits_get_opti3_name
const char * naco_pfits_get_opti3_name(const cpl_propertylist *self)
find out the OPTI3.NAME key
Definition: naco_pfits.c:304
naco_parameterlist_get_int
int naco_parameterlist_get_int(const cpl_parameterlist *self, const char *recipe, naco_parameter bitmask)
Retrieve the value of a NACO integer parameter.
Definition: naco_parameter.c:474
naco_get_filter_infos
cpl_error_code naco_get_filter_infos(const char *f, double *lam, double *dlam)
Get the infos of one of the filters.
Definition: naco_utils.c:61
naco_dfs_set_groups
int naco_dfs_set_groups(cpl_frameset *set)
Set the group as RAW or CALIB in a frameset.
Definition: naco_dfs.c:62
naco_parameterlist_get_bool
cpl_boolean naco_parameterlist_get_bool(const cpl_parameterlist *self, const char *recipe, naco_parameter bitmask)
Retrieve the value of a NACO boolean parameter.
Definition: naco_parameter.c:419