DETMON Pipeline Reference Manual  1.3.0
irplib_slitpos.c
1 /*
2  * This file is part of the irplib package
3  * Copyright (C) 2002,2003,2014 European Southern Observatory
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 /* The IRPLIB-based application may have checked for the availability of
25  memrchr() in which case the macro HAVE_DECL_MEMRCHR is defined as either
26  0 or 1. Without checks it is assumed that the function is not available.
27  With a suitable version of autoconf the macro can be defined with this
28  entry in configure.ac:
29  AC_CHECK_DECLS([memrchr])
30 */
31 
32 /*-----------------------------------------------------------------------------
33  Includes
34  -----------------------------------------------------------------------------*/
35 
36 #include "irplib_slitpos.h"
37 #include "irplib_flat.h"
38 
39 #include <cpl.h>
40 
41 #include <string.h>
42 #include <math.h>
43 #include <assert.h>
44 
45 /*-----------------------------------------------------------------------------
46  Defines
47  -----------------------------------------------------------------------------*/
48 
49 #ifndef IRPLIB_SLITPOS_KERNEL_SIZE_Y
50 #define IRPLIB_SLITPOS_KERNEL_SIZE_Y 5
51 #endif
52 
53 #ifndef IRPLIB_SLITPOS_MAX_EROSION
54 #define IRPLIB_SLITPOS_MAX_EROSION 1024
55 #endif
56 
57 /*-----------------------------------------------------------------------------
58  Functions prototypes
59  -----------------------------------------------------------------------------*/
60 
61 static cpl_error_code irplib_slitpos_find_edges_one_line(const cpl_image *,
62  int, int *, int *);
63 static cpl_error_code irplib_slitpos_find_vert_slit_ends(const cpl_image *,
64  int, int *, int *);
65 static cpl_error_code irplib_slitpos_find_vert_pos(const cpl_image *, int,
66  cpl_size *);
67 static cpl_error_code irplib_image_filter_background_line(cpl_image *,
68  const cpl_image *,
69  int, cpl_boolean) ;
70 
71 /*----------------------------------------------------------------------------*/
75 /*----------------------------------------------------------------------------*/
76 
78 /*----------------------------------------------------------------------------*/
104 /*----------------------------------------------------------------------------*/
105 cpl_table * irplib_slitpos_analysis(const cpl_image * imslit,
106  int slit_max_width,
107  double * slit_flux)
108 {
109  const int size_x = cpl_image_get_size_x(imslit);
110  const int size_y = cpl_image_get_size_y(imslit);
111  int slit_length;
112  cpl_size slit_pos;
113  cpl_image * filtered;
114  cpl_mask * mask;
115  cpl_image * thin_im;
116  int slit_top_y = 0; /* Avoid (false) uninit warning */
117  int slit_bot_y = 0; /* Avoid (false) uninit warning */
118  cpl_table * self;
119  double * slit_y,
120  * slit_x_l,
121  * slit_x_r;
122  double * coeff_r;
123  double * coeff_l;
124  int i;
125  cpl_error_code error = CPL_ERROR_NONE;
126 
127  /* Initialize */
128  if (slit_flux != NULL) *slit_flux = 0.0 ;
129 
130  /* Median vertical filtering 3x3 */
131  mask = cpl_mask_new(3, 3) ;
132  cpl_mask_not(mask) ;
133  filtered = cpl_image_new(size_x, size_y, cpl_image_get_type(imslit));
134  error = cpl_image_filter_mask(filtered, imslit, mask,
135  CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
136  cpl_mask_delete(mask);
137 
138  if (error) {
139  cpl_image_delete(filtered);
140  cpl_ensure(0, cpl_error_get_code(), NULL);
141  }
142 
143  /* The background may vary strongly along the vertical line. */
144  /* Detect and remove background with a 1+2*Slit_max x 1 median filter */
145  error = irplib_image_filter_background_line(filtered, NULL, slit_max_width,
146  CPL_TRUE);
147 
148  if (error) {
149  cpl_image_delete(filtered) ;
150  cpl_ensure(0, cpl_error_get_code(), NULL);
151  }
152 
153  /* Find the position of the slit */
154  if (irplib_slitpos_find_vert_pos(filtered, slit_max_width/2, &slit_pos)) {
155  cpl_image_delete(filtered);
156  cpl_msg_error(cpl_func, "Could not find the slit position");
157  cpl_ensure(0, cpl_error_get_code(), NULL);
158  }
159 
160  /* Extract a thin image containing the slit */
161  thin_im = cpl_image_extract(filtered, slit_pos-slit_max_width/2, 1,
162  slit_pos+slit_max_width/2, size_y);
163  if (thin_im == NULL) {
164  cpl_msg_error(cpl_func, "Could not extract the %d pixel thin image "
165  "around position %"CPL_SIZE_FORMAT,
166  slit_max_width, slit_pos);
167  cpl_image_delete(filtered);
168  cpl_ensure(0, cpl_error_get_code(), NULL);
169  }
170 
171  /* Find the ends of the slit */
172  error = irplib_slitpos_find_vert_slit_ends(thin_im,
173  IRPLIB_SLITPOS_KERNEL_SIZE_Y,
174  &slit_bot_y,
175  &slit_top_y);
176  cpl_image_delete(thin_im);
177  if (error) {
178  cpl_image_delete(filtered);
179  cpl_ensure(0, cpl_error_get_code(), NULL);
180  }
181 
182  /* Extract an image with exactly the slit */
183  thin_im = cpl_image_extract(filtered,
184  slit_pos-slit_max_width/2,
185  slit_bot_y,
186  slit_pos+slit_max_width/2,
187  slit_top_y);
188  cpl_image_delete(filtered);
189 
190  cpl_ensure(thin_im != NULL, cpl_error_get_code(), NULL);
191 
192  slit_length = 1 + slit_top_y - slit_bot_y;
193 
194  /* Allocate some arrays */
195  slit_y = cpl_malloc(slit_length * sizeof(double));
196  slit_x_l = cpl_malloc(slit_length * sizeof(double));
197  slit_x_r = cpl_malloc(slit_length * sizeof(double));
198 
199  /* Find the edges of the slit */
200  for (i=0 ; i<slit_length ; i++) {
201  int right_pos = 0; /* Avoid (false) uninit warning */
202  int left_pos = 0; /* Avoid (false) uninit warning */
203 
204  if (irplib_slitpos_find_edges_one_line(thin_im,
205  i,
206  &left_pos,
207  &right_pos)) {
208  cpl_msg_error(cpl_func, "cannot find the edges of the [%d]th line",
209  i+1);
210  cpl_image_delete(thin_im);
211  return NULL;
212  }
213 
214  /* Update the slit_flux */
215  if (slit_flux != NULL) {
216  *slit_flux += cpl_image_get_flux_window(thin_im, left_pos+1,
217  i+1, right_pos+1, i+1) ;
218  }
219 
220  /* Store the edges for the fit */
221  slit_x_l[i] = (double)left_pos;
222  slit_x_r[i] = (double)right_pos;
223  slit_y[i] = (double)(i+slit_bot_y-1);
224  }
225  cpl_image_delete(thin_im);
226 
227  /* Linear regression to find the edges */
228  coeff_l = irplib_flat_fit_slope_robust(slit_y, slit_x_l, slit_length);
229  coeff_r = irplib_flat_fit_slope_robust(slit_y, slit_x_r, slit_length);
230  cpl_free(slit_y);
231  cpl_free(slit_x_l);
232  cpl_free(slit_x_r);
233 
234  /* Allocate the table containing the results */
235  self = cpl_table_new(slit_length);
236  error |= cpl_table_new_column(self, "SLIT_Y", CPL_TYPE_INT);
237  error |= cpl_table_new_column(self, "SLIT_LEFT", CPL_TYPE_DOUBLE);
238  error |= cpl_table_new_column(self, "SLIT_CENTER", CPL_TYPE_DOUBLE);
239  error |= cpl_table_new_column(self, "SLIT_RIGHT", CPL_TYPE_DOUBLE);
240 
241  error |= cpl_table_set_column_unit(self, "SLIT_Y", "pixel");
242  error |= cpl_table_set_column_unit(self, "SLIT_LEFT", "pixel");
243  error |= cpl_table_set_column_unit(self, "SLIT_CENTER", "pixel");
244  error |= cpl_table_set_column_unit(self, "SLIT_RIGHT", "pixel");
245 
246  cpl_ensure(!error, cpl_error_get_code(), NULL);
247 
248  /* Rewrite the edges in the out table, and write the center */
249  for (i=0 ; i < slit_length ; i++) {
250  const int islity = i + slit_bot_y;
251  const double dslit = slit_pos - slit_max_width / 2.0;
252  const double dleft = coeff_l[0] + coeff_l[1] * (double)islity + dslit;
253  const double dright = coeff_r[0] + coeff_r[1] * (double)islity + dslit;
254  const double dcent = 0.5 * (dleft + dright);
255 
256  if (cpl_table_set_int(self, "SLIT_Y", i, islity)) break;
257  if (cpl_table_set_double(self, "SLIT_LEFT", i, dleft)) break;
258  if (cpl_table_set_double(self, "SLIT_RIGHT", i, dright)) break;
259  if (cpl_table_set_double(self, "SLIT_CENTER", i, dcent)) break;
260  }
261 
262  cpl_free(coeff_r);
263  cpl_free(coeff_l);
264 
265  if (i != slit_length) {
266  cpl_table_delete(self);
267  cpl_ensure(0, cpl_error_get_code(), NULL);
268  }
269 
270  return self;
271 }
272 
275 /*----------------------------------------------------------------------------*/
287 /*----------------------------------------------------------------------------*/
288 static cpl_error_code irplib_slitpos_find_edges_one_line(const cpl_image * self,
289  int line_pos,
290  int * left_pos,
291  int * right_pos)
292 {
293  const int size_x = cpl_image_get_size_x(self);
294  const float * pself;
295  double threshold;
296  int i;
297 
298  cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
299  cpl_ensure_code(cpl_image_get_type(self) == CPL_TYPE_FLOAT,
300  CPL_ERROR_INVALID_TYPE);
301 
302  pself = cpl_image_get_data_float_const(self);
303 
304  /* Find the threshold */
305  threshold = cpl_image_get_mean_window(self, 1, line_pos+1, size_x,
306  line_pos+1);
307 
308  /* Detect the left edge */
309  i = 0;
310  while (i < size_x && pself[line_pos*size_x+i] < threshold) i++;
311  *left_pos = i;
312 
313  /* Detect the right edge */
314  i = size_x - 1;
315  while (i >= 0 && pself[line_pos*size_x+i] < threshold) i--;
316  *right_pos = i;
317 
318  return CPL_ERROR_NONE;
319 }
320 
321 /*----------------------------------------------------------------------------*/
332 /*----------------------------------------------------------------------------*/
333 static
334 cpl_error_code irplib_slitpos_find_vert_slit_ends(const cpl_image * self,
335  int kernel_size,
336  int * bot_slit_y,
337  int * top_slit_y)
338 {
339  cpl_mask * binary;
340  cpl_mask * copy = NULL;
341  cpl_mask * kernel;
342  cpl_image * label_image;
343  int erosions_nb;
344  cpl_size nobj ;
345  const int size_x = cpl_image_get_size_x(self);
346  const int size_y = cpl_image_get_size_y(self);
347  const int npix = size_x * size_y;
348  const cpl_binary * pbinary;
349  const cpl_binary * pfind;
350  int i, itop, ibot;
351 
352 
353  cpl_ensure_code(size_x > 0, cpl_error_get_code());
354  cpl_ensure_code(kernel_size > 0, cpl_error_get_code());
355 
356  /* Threshold to have a binary image */
357  binary = cpl_mask_threshold_image_create(self, cpl_image_get_mean(self),
358  cpl_image_get_max(self));
359  cpl_ensure_code(binary != NULL, cpl_error_get_code());
360 
361  /* Erode until there is 1 object left in the image */
362  label_image = cpl_image_labelise_mask_create(binary, &nobj);
363  cpl_image_delete(label_image);
364 
365  if (label_image == NULL) {
366  cpl_mask_delete(binary);
367  cpl_ensure_code(0, cpl_error_get_code());
368  }
369 
370  /* Define the kernel for morpho operations */
371  kernel = cpl_mask_new(kernel_size, 1);
372  cpl_mask_not(kernel);
373  copy = cpl_mask_wrap(size_x, size_y, cpl_malloc(size_x * size_y *
374  sizeof(cpl_binary)));
375  for (erosions_nb = 0; erosions_nb < IRPLIB_SLITPOS_MAX_EROSION && nobj > 1;
376  erosions_nb++) {
377  /* Should not be possible to break from this loop */
378  cpl_mask_copy(copy, binary, 1, 1);
379  if (cpl_mask_filter(binary, copy, kernel, CPL_FILTER_EROSION,
380  CPL_BORDER_ZERO)) break;
381 
382  label_image = cpl_image_labelise_mask_create(binary, &nobj);
383  if (label_image == NULL) break; /* Assuming nobj was not set to 1 */
384  cpl_image_delete(label_image);
385  }
386 
387  if (nobj > 1) {
388  cpl_mask_delete(binary);
389  cpl_mask_delete(copy);
390  cpl_mask_delete(kernel);
391  if (erosions_nb >= IRPLIB_SLITPOS_MAX_EROSION) {
392  cpl_msg_error(cpl_func, "Number of erosions reached a limit of %d "
393  "with %"CPL_SIZE_FORMAT" possible slits left",
394  IRPLIB_SLITPOS_MAX_EROSION, nobj);
395  cpl_ensure_code(0, CPL_ERROR_CONTINUE);
396  }
397  cpl_ensure_code(0, cpl_error_get_code());
398  } else if (nobj < 1) {
399  cpl_mask_delete(binary);
400  cpl_mask_delete(copy);
401  cpl_mask_delete(kernel);
402  if (erosions_nb == 0)
403  cpl_msg_error(cpl_func, "No slit could be detected across %d "
404  "pixels", size_x);
405  else
406  cpl_msg_error(cpl_func, "The last of %d erosions removed all the "
407  "possible slits", erosions_nb);
408  cpl_ensure_code(0, CPL_ERROR_DATA_NOT_FOUND);
409  }
410 
411  /* Reconstruct the slit with dilations */
412  for (i=0 ; i < erosions_nb ; i++) {
413  cpl_mask_copy(copy, binary, 1, 1);
414  if (cpl_mask_filter(binary, copy, kernel, CPL_FILTER_DILATION,
415  CPL_BORDER_ZERO)) break;
416  }
417  cpl_mask_delete(copy);
418  cpl_mask_delete(kernel);
419 
420  if (i != erosions_nb) {
421  cpl_msg_error(cpl_func, "Dilation number %d out of %d failed",
422  i, erosions_nb);
423  cpl_mask_delete(binary);
424  cpl_ensure_code(0, cpl_error_get_code());
425  }
426 
427  /* Find the ends of the slit */
428  pbinary = cpl_mask_get_data(binary);
429  assert( pbinary != NULL );
430 
431  pfind = memchr(pbinary, CPL_BINARY_1, (size_t)npix);
432  assert( pfind != NULL );
433 
434  ibot = (int)(pfind - pbinary);
435 
436 #if defined HAVE_DECL_MEMRCHR && HAVE_DECL_MEMRCHR == 1
437  /* FIXME: Not tested */
438  pfind = memrchr(pfind, CPL_BINARY_1, (size_t)(npix - ibot));
439  assert( pfind != NULL );
440 
441  itop = (int)(pfind - pbinary);
442 #else
443 
444  itop = npix - 1;
445  while (itop > ibot && pbinary[itop] == CPL_BINARY_0) itop--;
446 
447 #endif
448 
449  *bot_slit_y = 1 + ibot / size_x;
450  *top_slit_y = 1 + itop / size_x;
451 
452  cpl_msg_info(cpl_func,
453  "Detected %"CPL_SIZE_FORMAT"-pixel slit from pixel %d to %d "
454  "using %d erosions/dilations", cpl_mask_count(binary),
455  *bot_slit_y, *top_slit_y, erosions_nb);
456 
457  cpl_mask_delete(binary);
458 
459  /* Should really be an assert() */
460  cpl_ensure_code(ibot <= itop, CPL_ERROR_DATA_NOT_FOUND);
461 
462  return CPL_ERROR_NONE;
463 }
464 
465 /*----------------------------------------------------------------------------*/
475 /*----------------------------------------------------------------------------*/
476 static cpl_error_code irplib_slitpos_find_vert_pos(const cpl_image * self,
477  int xwidth,
478  cpl_size * slit_pos)
479 {
480  const int size_x = cpl_image_get_size_x(self);
481  cpl_image * image1D;
482  cpl_size yone;
483  cpl_error_code error;
484 
485 
486  /* Collapse the image to a horizontal 1D image */
487  image1D = cpl_image_collapse_create(self, 0);
488 
489  cpl_ensure_code(image1D != NULL, cpl_error_get_code());
490 
491  /* Search the max of the 1D image to identify the slit position */
492  error = cpl_image_get_maxpos_window(image1D, 1+xwidth, 1, size_x-xwidth,
493  1, slit_pos, &yone);
494 
495  cpl_image_delete(image1D);
496 
497  cpl_ensure_code(!error, error);
498 
499  return CPL_ERROR_NONE;
500 }
501 
502 /*----------------------------------------------------------------------------*/
516 /*----------------------------------------------------------------------------*/
517 static cpl_error_code irplib_image_filter_background_line(cpl_image * self,
518  const cpl_image * other,
519  int hsize,
520  cpl_boolean vertical)
521 {
522  const int nx = cpl_image_get_size_x(self);
523  const int ny = cpl_image_get_size_y(self);
524  const int msize = 1 + 2 * hsize;
525  cpl_mask * mask;
526  cpl_image * background;
527  cpl_error_code error = CPL_ERROR_NONE;
528 
529  cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
530  cpl_ensure_code(hsize >= 0, CPL_ERROR_ILLEGAL_INPUT);
531 
532  if (other == NULL) other = self;
533 
534  mask = vertical ? cpl_mask_new(msize, 1) : cpl_mask_new(1, msize);
535 
536  error |= cpl_mask_not(mask);
537 
538  background = cpl_image_new(nx, ny, cpl_image_get_type(other));
539 
540  error |= cpl_image_filter_mask(background, other, mask, CPL_FILTER_MEDIAN,
541  CPL_BORDER_FILTER);
542  cpl_mask_delete(mask);
543 
544  if (self != other) {
545  error |= cpl_image_copy(self, other, 1, 1);
546  }
547 
548  error |= cpl_image_subtract(self, background);
549  cpl_image_delete(background);
550 
551  return error ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
552 }
553 
554 
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...