NACO Pipeline Reference Manual  4.4.0
irplib_slitpos.c
1 /* $Id: irplib_slitpos.c,v 1.31 2013-07-04 12:10:12 jtaylor Exp $
2  *
3  * This file is part of the irplib package
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: jtaylor $
23  * $Date: 2013-07-04 12:10:12 $
24  * $Revision: 1.31 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 /* The IRPLIB-based application may have checked for the availability of
33  memrchr() in which case the macro HAVE_DECL_MEMRCHR is defined as either
34  0 or 1. Without checks it is assumed that the function is not available.
35  With a suitable version of autoconf the macro can be defined with this
36  entry in configure.ac:
37  AC_CHECK_DECLS([memrchr])
38 */
39 
40 /*-----------------------------------------------------------------------------
41  Includes
42  -----------------------------------------------------------------------------*/
43 
44 #include <string.h>
45 #include <math.h>
46 #include <assert.h>
47 #include <cpl.h>
48 
49 #include "irplib_slitpos.h"
50 #include "irplib_flat.h"
51 
52 /*-----------------------------------------------------------------------------
53  Defines
54  -----------------------------------------------------------------------------*/
55 
56 /* TEMPORARY SUPPORT OF CPL 5.x */
57 #ifndef CPL_SIZE_FORMAT
58 #define CPL_SIZE_FORMAT "d"
59 #define cpl_size int
60 #endif
61 /* END TEMPORARY SUPPORT OF CPL 5.x */
62 
63 #ifndef IRPLIB_SLITPOS_KERNEL_SIZE_Y
64 #define IRPLIB_SLITPOS_KERNEL_SIZE_Y 5
65 #endif
66 
67 #ifndef IRPLIB_SLITPOS_MAX_EROSION
68 #define IRPLIB_SLITPOS_MAX_EROSION 1024
69 #endif
70 
71 /*-----------------------------------------------------------------------------
72  Functions prototypes
73  -----------------------------------------------------------------------------*/
74 
75 static cpl_error_code irplib_slitpos_find_edges_one_line(const cpl_image *,
76  int, int *, int *);
77 static cpl_error_code irplib_slitpos_find_vert_slit_ends(const cpl_image *,
78  int, int *, int *);
79 static cpl_error_code irplib_slitpos_find_vert_pos(const cpl_image *, int,
80  cpl_size *);
81 static cpl_error_code irplib_image_filter_background_line(cpl_image *,
82  const cpl_image *,
83  int, cpl_boolean) ;
84 
85 /*----------------------------------------------------------------------------*/
89 /*----------------------------------------------------------------------------*/
90 
92 /*----------------------------------------------------------------------------*/
118 /*----------------------------------------------------------------------------*/
119 cpl_table * irplib_slitpos_analysis(const cpl_image * imslit,
120  int slit_max_width,
121  double * slit_flux)
122 {
123  const int size_x = cpl_image_get_size_x(imslit);
124  const int size_y = cpl_image_get_size_y(imslit);
125  int slit_length;
126  cpl_size slit_pos;
127  cpl_image * filtered;
128  cpl_mask * mask;
129  cpl_image * thin_im;
130  int slit_top_y = 0; /* Avoid (false) uninit warning */
131  int slit_bot_y = 0; /* Avoid (false) uninit warning */
132  cpl_table * self;
133  double * slit_y,
134  * slit_x_l,
135  * slit_x_r;
136  double * coeff_r;
137  double * coeff_l;
138  int i;
139  cpl_error_code error = CPL_ERROR_NONE;
140 
141  /* Initialize */
142  if (slit_flux != NULL) *slit_flux = 0.0 ;
143 
144  /* Median vertical filtering 3x3 */
145  mask = cpl_mask_new(3, 3) ;
146  cpl_mask_not(mask) ;
147  filtered = cpl_image_new(size_x, size_y, cpl_image_get_type(imslit));
148  error = cpl_image_filter_mask(filtered, imslit, mask,
149  CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
150  cpl_mask_delete(mask);
151 
152  if (error) {
153  cpl_image_delete(filtered);
154  cpl_ensure(0, cpl_error_get_code(), NULL);
155  }
156 
157  /* The background may vary strongly along the vertical line. */
158  /* Detect and remove background with a 1+2*Slit_max x 1 median filter */
159  error = irplib_image_filter_background_line(filtered, NULL, slit_max_width,
160  CPL_TRUE);
161 
162  if (error) {
163  cpl_image_delete(filtered) ;
164  cpl_ensure(0, cpl_error_get_code(), NULL);
165  }
166 
167  /* Find the position of the slit */
168  if (irplib_slitpos_find_vert_pos(filtered, slit_max_width/2, &slit_pos)) {
169  cpl_image_delete(filtered);
170  cpl_msg_error(cpl_func, "Could not find the slit position");
171  cpl_ensure(0, cpl_error_get_code(), NULL);
172  }
173 
174  /* Extract a thin image containing the slit */
175  thin_im = cpl_image_extract(filtered, slit_pos-slit_max_width/2, 1,
176  slit_pos+slit_max_width/2, size_y);
177  if (thin_im == NULL) {
178  cpl_msg_error(cpl_func, "Could not extract the %d pixel thin image "
179  "around position %"CPL_SIZE_FORMAT,
180  slit_max_width, slit_pos);
181  cpl_image_delete(filtered);
182  cpl_ensure(0, cpl_error_get_code(), NULL);
183  }
184 
185  /* Find the ends of the slit */
186  error = irplib_slitpos_find_vert_slit_ends(thin_im,
187  IRPLIB_SLITPOS_KERNEL_SIZE_Y,
188  &slit_bot_y,
189  &slit_top_y);
190  cpl_image_delete(thin_im);
191  if (error) {
192  cpl_image_delete(filtered);
193  cpl_ensure(0, cpl_error_get_code(), NULL);
194  }
195 
196  /* Extract an image with exactly the slit */
197  thin_im = cpl_image_extract(filtered,
198  slit_pos-slit_max_width/2,
199  slit_bot_y,
200  slit_pos+slit_max_width/2,
201  slit_top_y);
202  cpl_image_delete(filtered);
203 
204  cpl_ensure(thin_im != NULL, cpl_error_get_code(), NULL);
205 
206  slit_length = 1 + slit_top_y - slit_bot_y;
207 
208  /* Allocate some arrays */
209  slit_y = cpl_malloc(slit_length * sizeof(double));
210  slit_x_l = cpl_malloc(slit_length * sizeof(double));
211  slit_x_r = cpl_malloc(slit_length * sizeof(double));
212 
213  /* Find the edges of the slit */
214  for (i=0 ; i<slit_length ; i++) {
215  int right_pos = 0; /* Avoid (false) uninit warning */
216  int left_pos = 0; /* Avoid (false) uninit warning */
217 
218  if (irplib_slitpos_find_edges_one_line(thin_im,
219  i,
220  &left_pos,
221  &right_pos)) {
222  cpl_msg_error(cpl_func, "cannot find the edges of the [%d]th line",
223  i+1);
224  cpl_image_delete(thin_im);
225  return NULL;
226  }
227 
228  /* Update the slit_flux */
229  if (slit_flux != NULL) {
230  *slit_flux += cpl_image_get_flux_window(thin_im, left_pos+1,
231  i+1, right_pos+1, i+1) ;
232  }
233 
234  /* Store the edges for the fit */
235  slit_x_l[i] = (double)left_pos;
236  slit_x_r[i] = (double)right_pos;
237  slit_y[i] = (double)(i+slit_bot_y-1);
238  }
239  cpl_image_delete(thin_im);
240 
241  /* Linear regression to find the edges */
242  coeff_l = irplib_flat_fit_slope_robust(slit_y, slit_x_l, slit_length);
243  coeff_r = irplib_flat_fit_slope_robust(slit_y, slit_x_r, slit_length);
244  cpl_free(slit_y);
245  cpl_free(slit_x_l);
246  cpl_free(slit_x_r);
247 
248  /* Allocate the table containing the results */
249  self = cpl_table_new(slit_length);
250  error |= cpl_table_new_column(self, "SLIT_Y", CPL_TYPE_INT);
251  error |= cpl_table_new_column(self, "SLIT_LEFT", CPL_TYPE_DOUBLE);
252  error |= cpl_table_new_column(self, "SLIT_CENTER", CPL_TYPE_DOUBLE);
253  error |= cpl_table_new_column(self, "SLIT_RIGHT", CPL_TYPE_DOUBLE);
254 
255  error |= cpl_table_set_column_unit(self, "SLIT_Y", "pixel");
256  error |= cpl_table_set_column_unit(self, "SLIT_LEFT", "pixel");
257  error |= cpl_table_set_column_unit(self, "SLIT_CENTER", "pixel");
258  error |= cpl_table_set_column_unit(self, "SLIT_RIGHT", "pixel");
259 
260  cpl_ensure(!error, cpl_error_get_code(), NULL);
261 
262  /* Rewrite the edges in the out table, and write the center */
263  for (i=0 ; i < slit_length ; i++) {
264  const int islity = i + slit_bot_y;
265  const double dslit = slit_pos - slit_max_width / 2.0;
266  const double dleft = coeff_l[0] + coeff_l[1] * (double)islity + dslit;
267  const double dright = coeff_r[0] + coeff_r[1] * (double)islity + dslit;
268  const double dcent = 0.5 * (dleft + dright);
269 
270  if (cpl_table_set_int(self, "SLIT_Y", i, islity)) break;
271  if (cpl_table_set_double(self, "SLIT_LEFT", i, dleft)) break;
272  if (cpl_table_set_double(self, "SLIT_RIGHT", i, dright)) break;
273  if (cpl_table_set_double(self, "SLIT_CENTER", i, dcent)) break;
274  }
275 
276  cpl_free(coeff_r);
277  cpl_free(coeff_l);
278 
279  if (i != slit_length) {
280  cpl_table_delete(self);
281  cpl_ensure(0, cpl_error_get_code(), NULL);
282  }
283 
284  return self;
285 }
286 
289 /*----------------------------------------------------------------------------*/
301 /*----------------------------------------------------------------------------*/
302 static cpl_error_code irplib_slitpos_find_edges_one_line(const cpl_image * self,
303  int line_pos,
304  int * left_pos,
305  int * right_pos)
306 {
307  const int size_x = cpl_image_get_size_x(self);
308  const float * pself;
309  double threshold;
310  int i;
311 
312  cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
313  cpl_ensure_code(cpl_image_get_type(self) == CPL_TYPE_FLOAT,
314  CPL_ERROR_INVALID_TYPE);
315 
316  pself = cpl_image_get_data_float_const(self);
317 
318  /* Find the threshold */
319  threshold = cpl_image_get_mean_window(self, 1, line_pos+1, size_x,
320  line_pos+1);
321 
322  /* Detect the left edge */
323  i = 0;
324  while (i < size_x && pself[line_pos*size_x+i] < threshold) i++;
325  *left_pos = i;
326 
327  /* Detect the right edge */
328  i = size_x - 1;
329  while (i >= 0 && pself[line_pos*size_x+i] < threshold) i--;
330  *right_pos = i;
331 
332  return CPL_ERROR_NONE;
333 }
334 
335 /*----------------------------------------------------------------------------*/
346 /*----------------------------------------------------------------------------*/
347 static
348 cpl_error_code irplib_slitpos_find_vert_slit_ends(const cpl_image * self,
349  int kernel_size,
350  int * bot_slit_y,
351  int * top_slit_y)
352 {
353  cpl_mask * binary;
354  cpl_mask * copy = NULL;
355  cpl_mask * kernel;
356  cpl_image * label_image;
357  int erosions_nb;
358  cpl_size nobj ;
359  const int size_x = cpl_image_get_size_x(self);
360  const int size_y = cpl_image_get_size_y(self);
361  const int npix = size_x * size_y;
362  const cpl_binary * pbinary;
363  const cpl_binary * pfind;
364  int i, itop, ibot;
365 
366 
367  cpl_ensure_code(size_x > 0, cpl_error_get_code());
368  cpl_ensure_code(kernel_size > 0, cpl_error_get_code());
369 
370  /* Threshold to have a binary image */
371  binary = cpl_mask_threshold_image_create(self, cpl_image_get_mean(self),
372  cpl_image_get_max(self));
373  cpl_ensure_code(binary != NULL, cpl_error_get_code());
374 
375  /* Erode until there is 1 object left in the image */
376  label_image = cpl_image_labelise_mask_create(binary, &nobj);
377  cpl_image_delete(label_image);
378 
379  if (label_image == NULL) {
380  cpl_mask_delete(binary);
381  cpl_ensure_code(0, cpl_error_get_code());
382  }
383 
384  /* Define the kernel for morpho operations */
385  kernel = cpl_mask_new(kernel_size, 1);
386  cpl_mask_not(kernel);
387  copy = cpl_mask_wrap(size_x, size_y, cpl_malloc(size_x * size_y *
388  sizeof(cpl_binary)));
389  for (erosions_nb = 0; erosions_nb < IRPLIB_SLITPOS_MAX_EROSION && nobj > 1;
390  erosions_nb++) {
391  /* Should not be possible to break from this loop */
392  cpl_mask_copy(copy, binary, 1, 1);
393  if (cpl_mask_filter(binary, copy, kernel, CPL_FILTER_EROSION,
394  CPL_BORDER_ZERO)) break;
395 
396  label_image = cpl_image_labelise_mask_create(binary, &nobj);
397  if (label_image == NULL) break; /* Assuming nobj was not set to 1 */
398  cpl_image_delete(label_image);
399  }
400 
401  if (nobj > 1) {
402  cpl_mask_delete(binary);
403  cpl_mask_delete(copy);
404  cpl_mask_delete(kernel);
405  if (erosions_nb >= IRPLIB_SLITPOS_MAX_EROSION) {
406  cpl_msg_error(cpl_func, "Number of erosions reached a limit of %d "
407  "with %"CPL_SIZE_FORMAT" possible slits left",
408  IRPLIB_SLITPOS_MAX_EROSION, nobj);
409  cpl_ensure_code(0, CPL_ERROR_CONTINUE);
410  }
411  cpl_ensure_code(0, cpl_error_get_code());
412  } else if (nobj < 1) {
413  cpl_mask_delete(binary);
414  cpl_mask_delete(copy);
415  cpl_mask_delete(kernel);
416  if (erosions_nb == 0)
417  cpl_msg_error(cpl_func, "No slit could be detected across %d "
418  "pixels", size_x);
419  else
420  cpl_msg_error(cpl_func, "The last of %d erosions removed all the "
421  "possible slits", erosions_nb);
422  cpl_ensure_code(0, CPL_ERROR_DATA_NOT_FOUND);
423  }
424 
425  /* Reconstruct the slit with dilations */
426  for (i=0 ; i < erosions_nb ; i++) {
427  cpl_mask_copy(copy, binary, 1, 1);
428  if (cpl_mask_filter(binary, copy, kernel, CPL_FILTER_DILATION,
429  CPL_BORDER_ZERO)) break;
430  }
431  cpl_mask_delete(copy);
432  cpl_mask_delete(kernel);
433 
434  if (i != erosions_nb) {
435  cpl_msg_error(cpl_func, "Dilation number %d out of %d failed",
436  i, erosions_nb);
437  cpl_mask_delete(binary);
438  cpl_ensure_code(0, cpl_error_get_code());
439  }
440 
441  /* Find the ends of the slit */
442  pbinary = cpl_mask_get_data(binary);
443  assert( pbinary != NULL );
444 
445  pfind = memchr(pbinary, CPL_BINARY_1, (size_t)npix);
446  assert( pfind != NULL );
447 
448  ibot = (int)(pfind - pbinary);
449 
450 #if defined HAVE_DECL_MEMRCHR && HAVE_DECL_MEMRCHR == 1
451  /* FIXME: Not tested */
452  pfind = memrchr(pfind, CPL_BINARY_1, (size_t)(npix - ibot));
453  assert( pfind != NULL );
454 
455  itop = (int)(pfind - pbinary);
456 #else
457 
458  itop = npix - 1;
459  while (itop > ibot && pbinary[itop] == CPL_BINARY_0) itop--;
460 
461 #endif
462 
463  *bot_slit_y = 1 + ibot / size_x;
464  *top_slit_y = 1 + itop / size_x;
465 
466  cpl_msg_info(cpl_func,
467  "Detected %"CPL_SIZE_FORMAT"-pixel slit from pixel %d to %d "
468  "using %d erosions/dilations", cpl_mask_count(binary),
469  *bot_slit_y, *top_slit_y, erosions_nb);
470 
471  cpl_mask_delete(binary);
472 
473  /* Should really be an assert() */
474  cpl_ensure_code(ibot <= itop, CPL_ERROR_DATA_NOT_FOUND);
475 
476  return CPL_ERROR_NONE;
477 }
478 
479 /*----------------------------------------------------------------------------*/
489 /*----------------------------------------------------------------------------*/
490 static cpl_error_code irplib_slitpos_find_vert_pos(const cpl_image * self,
491  int xwidth,
492  cpl_size * slit_pos)
493 {
494  const int size_x = cpl_image_get_size_x(self);
495  cpl_image * image1D;
496  cpl_size yone;
497  cpl_error_code error;
498 
499 
500  /* Collapse the image to a horizontal 1D image */
501  image1D = cpl_image_collapse_create(self, 0);
502 
503  cpl_ensure_code(image1D != NULL, cpl_error_get_code());
504 
505  /* Search the max of the 1D image to identify the slit position */
506  error = cpl_image_get_maxpos_window(image1D, 1+xwidth, 1, size_x-xwidth,
507  1, slit_pos, &yone);
508 
509  cpl_image_delete(image1D);
510 
511  cpl_ensure_code(!error, error);
512 
513  return CPL_ERROR_NONE;
514 }
515 
516 /*----------------------------------------------------------------------------*/
530 /*----------------------------------------------------------------------------*/
531 static cpl_error_code irplib_image_filter_background_line(cpl_image * self,
532  const cpl_image * other,
533  int hsize,
534  cpl_boolean vertical)
535 {
536  const int nx = cpl_image_get_size_x(self);
537  const int ny = cpl_image_get_size_y(self);
538  const int msize = 1 + 2 * hsize;
539  cpl_mask * mask;
540  cpl_image * background;
541  cpl_error_code error = CPL_ERROR_NONE;
542 
543  cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
544  cpl_ensure_code(hsize >= 0, CPL_ERROR_ILLEGAL_INPUT);
545 
546  if (other == NULL) other = self;
547 
548  mask = vertical ? cpl_mask_new(msize, 1) : cpl_mask_new(1, msize);
549 
550  error |= cpl_mask_not(mask);
551 
552  background = cpl_image_new(nx, ny, cpl_image_get_type(other));
553 
554  error |= cpl_image_filter_mask(background, other, mask, CPL_FILTER_MEDIAN,
555  CPL_BORDER_FILTER);
556  cpl_mask_delete(mask);
557 
558  if (self != other) {
559  error |= cpl_image_copy(self, other, 1, 1);
560  }
561 
562  error |= cpl_image_subtract(self, background);
563  cpl_image_delete(background);
564 
565  return error ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
566 }
567 
568 
double * irplib_flat_fit_slope_robust(double *x, double *y, int np)
Fit a slope to a list of points (robust fit).
Definition: irplib_flat.c:191
cpl_table * irplib_slitpos_analysis(const cpl_image *imslit, int slit_max_width, double *slit_flux)
Detect the slit position, detect its ends, extract a thin image containing only the slit and find its...