MUSE Pipeline Reference Manual  0.18.1
muclipm_make_image.c
1 /* -*- Mode: C; tab-width: 8; c-basic-offset: 4 -*- */
2 /* vim:set sw=4 ts=8: */
3 /*
4  * This file is part of the MUSE Instrument Pipeline
5  * Copyright (C) 2008-2014 European Southern Observatory
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 /*********************************************************************
23  * E.S.O. - VLT project
24  *
25  * "@(#)"
26  *
27  * who when what
28  * -------- ---------- ----------------------------------------------
29  * apecontal 2013-04-16 improved overscan detection within slices
30  * apecontal 2013-03-01 fixed bug computing overscan median value when
31  * the binning in y direction is greater than 1
32  * apecontal 2013-02-19 added usage of overscan pixels to estimate bias
33  * apecontal 2012-10-30 fixed bug regarding handling of binned mask
34  * apecontal 2012-10-24 fixed bug regarding handling of
35  * cpl_bivector_interpolate_linear output according to
36  * version number
37  * gzins 2012-10-23 passed slices limits as argument
38  * apecontal 2012-09-21 read slices limits into mask instead or computing them
39  * apecontal 2011-11-24 bug fix in slices ref (updated wr_row computation)
40  * gzins 2011-03-09 suppressed muclipm_make_image_zero function
41  * gzins 2011-03-05 re-added muclipm_make_image_zero
42  * apecontal 2011-03-03 added documentation
43  * improved error handling
44  * moved defines to header file, and renamed them
45  * apecontal 2010-02-08 added capability of masks with unfixed width
46  * apecontal 2009-12-15 created
47  */
48 
63 /*-----------------------------------------------------------------------------
64  Includes
65  -----------------------------------------------------------------------------*/
66 
67 #include "muclipm_make_image.h"
68 #include "muclipm_priv_error.h"
69 
70 #include <string.h>
71 
72 /*-----------------------------------------------------------------------------
73  Implementation
74  -----------------------------------------------------------------------------*/
77 /*----------------------------------------------------------------------------*/
101 /*----------------------------------------------------------------------------*/
102 cpl_error_code muclipm_bias_subtract_using_overscan(cpl_image *raw_img)
103 {
104  long raw_image_nx = cpl_image_get_size_x(raw_img);
105  long raw_image_ny = cpl_image_get_size_y(raw_img);
106  long i, j, q;
107 
108  int err=0, shiftx, shifty;
109  double median = 0;
110 
111  double Qd[4][4]; /* Limits of CCD quadrants */
112  double Ov[4][4]; /* Limits of overscan strips */
113 
114  if (raw_img == NULL) {
115  return(CPL_ERROR_INCOMPATIBLE_INPUT);
116  }
117 
118  if (raw_image_nx < 64 || raw_image_ny < 64) {
119  return(CPL_ERROR_INCOMPATIBLE_INPUT);
120  }
121 
122  /* compute CCD quadrant limits */
123  for (q=0; q<4; q++)
124  {
125  if (q % 2 == 0)
126  shiftx = 0;
127  else
128  shiftx = 1;
129 
130  if (q > 1)
131  shifty = 1;
132  else
133  shifty = 0;
134 
135  Qd[q][0] = 0.5*shiftx*raw_image_nx + 1;
136  Qd[q][1] = Qd[q][0] -1 +0.5*raw_image_nx;
137  Qd[q][2] = 0.5*shifty*raw_image_ny + 1;
138  Qd[q][3] = Qd[q][2] -1 +0.5*raw_image_ny;
139  }
140 
141  /* Compute overscan strip limits per quadrant (ignore the 4 pixels all around the strip) */
142  for (q=0; q<4; q++)
143  {
144  if (q % 2 == 0)
145  Ov[q][0] = Qd[q][1] -32 +4;
146  else
147  Ov[q][0] = Qd[q][0] +4;
148 
149  Ov[q][1] = Ov[q][0] +32 - 2*4;
150 
151  Ov[q][2] = Qd[q][2]+4;
152  Ov[q][3] = Qd[q][3]-4;
153  }
154 
155  /* for each CCD quadrant, estimate median of the overscan strip, and subtract from image */
156  for (q=0; q<4; q++)
157  {
158  /* compute median in overscan strip */
159  median = cpl_image_get_median_window(raw_img,Ov[q][0],Ov[q][2],Ov[q][1],Ov[q][3]);
160 
161  for (j=Qd[q][2];j<=Qd[q][3];j++)
162  for (i=Qd[q][0];i<=Qd[q][1];i++)
163  cpl_image_set(raw_img,i,j,cpl_image_get(raw_img,i,j,&err) -median);
164  }
165 
166  return(CPL_ERROR_NONE);
167 }
168 
169 /*----------------------------------------------------------------------------*/
206 /*----------------------------------------------------------------------------*/
207 cpl_error_code muclipm_make_image(const cpl_image *raw_img,
208  const cpl_image *offset,
209  const cpl_image *flat,
210  const cpl_image *mask,
211  const long *slice_xstart,
212  const long *slice_xend,
213  cpl_image **out_img)
214 {
215  /* Local variables */
216  long i, j, k, l, m, n, min, imin, imax, outvec_size;
217  long raw_image_nx = cpl_image_get_size_x(raw_img);
218  long raw_image_ny = cpl_image_get_size_y(raw_img);
219  long wr_col, wr_row, noslice;
220  int err=0, xoverscan=0;
221  float cpl_version;
222  double val=0;
223  double *invec_x_data, *outvec_x_data;
224  double *invec_y_data, *outvec_y_data;
225 
226  cpl_bivector *invec, *outvec;
227  cpl_vector *invec_x, *invec_y;
228  cpl_image *tmp_img, *collapsed_img;
229 
230  /* Slit position per sub-slicer (1/4th of the slicer) */
231  int slit_position[MUCLIPM_NB_SLICES] =
232  {9, 8, 1, 10, 7, 2, 11, 6, 3, 12, 5, 4};
233 
234  MUCLIPM_TRY
235  {
236  MUCLIPM_TRY_CHECK(raw_img != NULL, CPL_ERROR_NULL_INPUT,
237  "raw image", "is mandatory");
238 
239  /* Check e.g. for same matrix dimensions */
240  if (raw_image_ny <= 0)
241  {
242  MUCLIPM_TRY_EXIT_WITH_ERROR_MSG
243  (CPL_ERROR_INCOMPATIBLE_INPUT, "raw image",
244  "should not be empty");
245  }
246  /* init output image as blank image (in case the process fails before completion */
247  err = cpl_image_multiply_scalar(*out_img,(double)0.0);
248  if (err)
249  {
250  MUCLIPM_TRY_EXIT_WITH_ERROR_MSG
251  (cpl_error_get_code(), "output image", "could not set values");
252  }
253 
254  tmp_img = cpl_image_duplicate(raw_img);
255  if (tmp_img == NULL)
256  {
257  MUCLIPM_TRY_EXIT_WITH_ERROR_MSG
258  (cpl_error_get_code(), "temporary image", "could not create");
259  }
260 
261  /* Subtract the bias if offset pointer is valid - if not use overscan pixels to estimate it */
262  if (offset != NULL)
263  {
264  err = cpl_image_subtract(tmp_img, offset);
265  if (err != CPL_ERROR_NONE)
266  {
267  cpl_image_delete(tmp_img);
268  return(err);
269  }
270  }
271  else
272  {
274  }
275 
276  /* Divide by the flat if flat pointer is valid */
277  if (flat != NULL)
278  {
279  err = cpl_image_divide(tmp_img, flat);
280  if (err != CPL_ERROR_NONE)
281  {
282  cpl_image_delete(tmp_img);
283  return(err);
284  }
285  }
286 
287  /* Apply the mask (i.e. keep nly relevant data) */
288  MUCLIPM_TRY_CHECK(mask != NULL, CPL_ERROR_NULL_INPUT,
289  "mask image", "is mandatory");
290 
291  err = cpl_image_multiply(tmp_img, mask);
292  if (err != CPL_ERROR_NONE)
293  {
294  cpl_image_delete(tmp_img);
295  return(err);
296  }
297 
298  /* Collapse image over rows to keep only .... */
299  collapsed_img = cpl_image_collapse_create(tmp_img,0);
300 
301  if (collapsed_img == NULL)
302  {
303  cpl_image_delete(tmp_img);
304  MUCLIPM_TRY_EXIT_WITH_ERROR_MSG
305  (cpl_error_get_code(), "collapsed image", "could not create");
306  }
307 
308  cpl_image_delete(tmp_img);
309 
310  /* Retrieve cpl version to cope with cpl_bivector_interpolate_linear bug
311  * fixed in version 5.2 which doesn't set the first value
312  */
313  cpl_version = cpl_version_get_major() + 0.1*cpl_version_get_minor();
314 
315  /* Create outvec bivector */
316  if (cpl_version >= 5.2) {
317  outvec = cpl_bivector_new(MUCLIPM_NB_SPEC_PER_SLICE);
318  imin = 0;
319  imax = MUCLIPM_NB_SPEC_PER_SLICE;
320  }
321  else
322  {
323  outvec = cpl_bivector_new(MUCLIPM_NB_SPEC_PER_SLICE + 1);
324  imin = 1;
325  imax = MUCLIPM_NB_SPEC_PER_SLICE + 1;
326  }
327 
328  if (outvec == NULL)
329  {
330  MUCLIPM_TRY_EXIT_WITH_ERROR_MSG
331  (cpl_error_get_code(), "bivector outvec", "could not create");
332  }
333 
334  outvec_x_data = cpl_bivector_get_x_data(outvec);
335  outvec_y_data = cpl_bivector_get_y_data(outvec);
336 
337  outvec_x_data[0] = 1.;
338  for (m = imin; m < imax ; m++)
339  {
340  outvec_x_data[m] = (double)(m + 1.0);
341  }
342 
343  /* For each subslicer 1-4*/
344  for (k= 1; k <= MUCLIPM_NB_SUBSLICERS; k++)
345  {
346  /* For each slice 1-12*/
347  for (l= 1; l <= MUCLIPM_NB_SLICES; l++)
348  {
349  noslice = (k-1)*MUCLIPM_NB_SLICES + l-1;
350 
351  n = slice_xend[noslice] - slice_xstart[noslice] +1;
352 
353  if ((n - 2*MUCLIPM_OVERSCAN) > (MUCLIPM_NB_SPEC_PER_SLICE -2))
354  {
355  xoverscan = 1;
356  n -= 2*MUCLIPM_OVERSCAN;
357  }
358  else
359  {
360  xoverscan = 0;
361  }
362 
363  /* Create bivector */
364  invec_x = cpl_vector_new(n);
365  invec_y = cpl_vector_new(n);
366  if (invec_x == NULL || invec_y == NULL)
367  {
368  cpl_image_delete(collapsed_img);
369  MUCLIPM_TRY_EXIT_WITH_ERROR_MSG
370  (cpl_error_get_code(), "vector invecx or invecy",
371  "could not create");
372  }
373 
374  invec_x_data = cpl_vector_get_data(invec_x);
375  invec_y_data = cpl_vector_get_data(invec_y);
376 
377  for (i=slice_xstart[noslice], j=0;
378  (i <= slice_xend[noslice]) && (i < raw_image_nx) ; i++)
379  {
380 
381  val = (double)cpl_image_get(collapsed_img, i, 1, &err);
382  if (err)
383  {
384  val = 0;
385  }
386 
387  /* Store values in a bivector */
388  if (xoverscan != 1) /* care about slices
389  crossing overscans window */
390  {
391  invec_x_data[j] =
392  (double)((j + 1.0) *
393  (MUCLIPM_NB_SPEC_PER_SLICE + 1) / n);
394  invec_y_data[j] = val;
395  j++;
396  }
397  else {
398 
399  if ((i <= raw_image_nx/2 - MUCLIPM_OVERSCAN)
400  || (i > raw_image_nx/2 + MUCLIPM_OVERSCAN))
401  {
402  invec_x_data[j] =
403  (double)((j + 1.0) *
404  (MUCLIPM_NB_SPEC_PER_SLICE + 1) / n);
405  invec_y_data[j] = val;
406  j++;
407  }
408  }
409  }
410 
411  /* Create bivector */
412  invec = cpl_bivector_wrap_vectors(invec_x, invec_y);
413  if (invec == NULL)
414  {
415  cpl_image_delete(collapsed_img);
416  cpl_bivector_delete(outvec);
417  cpl_bivector_delete(invec);
418  MUCLIPM_TRY_EXIT_WITH_ERROR_MSG
419  (cpl_error_get_code(), "bivector invec",
420  "could not create");
421  }
422 
423  outvec_size= cpl_bivector_get_size(outvec);
424 
425  if (outvec_size >= n)
426  {
427  /* Only need to copy vector invec in outvec */
428  min = (n < MUCLIPM_NB_SPEC_PER_SLICE) ?
429  n : MUCLIPM_NB_SPEC_PER_SLICE;
430 
431  for (i=imin, m= 0; i < imax && m < min; i++, m++)
432  outvec_y_data[i] = invec_y_data[m];
433  }
434  else
435  {
436  /* Interpolate */
437  err = cpl_bivector_interpolate_linear(outvec, invec);
438  if (err)
439  {
440  cpl_image_delete(collapsed_img);
441  cpl_bivector_delete(outvec);
442  cpl_bivector_delete(invec);
443  MUCLIPM_TRY_EXIT_WITH_ERROR_MSG
444  (err, "bivector invec", "could not interpolate");
445  }
446  }
447 
448  /* Write result */
449  wr_row = MUCLIPM_NB_SLICES - slit_position[l - 1] + 1;
450  wr_col = k * MUCLIPM_NB_SPEC_PER_SLICE;
451  for (i=imin; i< imax ; i++, wr_col--)
452  {
453  cpl_image_set(*out_img, wr_col, wr_row,
454  (float)outvec_y_data[i]);
455  }
456  cpl_bivector_delete(invec);
457  }
458  }
459 
460  cpl_bivector_delete(outvec);
461  cpl_image_delete(collapsed_img);
462  cpl_error_reset();
463  }
464  MUCLIPM_CATCH
465  {
466  *out_img = NULL;
467  }
468 
469  return MUCLIPM_ERROR_GET_NEW_SINCE_TRY();
470 }
471 
cpl_error_code muclipm_make_image(const cpl_image *raw_img, const cpl_image *offset, const cpl_image *flat, const cpl_image *mask, const long *slice_xstart, const long *slice_xend, cpl_image **out_img)
Collapses spectra over the spectral direction.
cpl_error_code muclipm_bias_subtract_using_overscan(cpl_image *raw_img)
Evaluates per quadrant overscan median values and subtract them from input image. ...