VISIR Pipeline Reference Manual  4.1.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  cpl_free(slit_y);
212  cpl_free(slit_x_l);
213  cpl_free(slit_x_r);
214  return NULL;
215  }
216 
217  /* Update the slit_flux */
218  if (slit_flux != NULL) {
219  *slit_flux += cpl_image_get_flux_window(thin_im, left_pos+1,
220  i+1, right_pos+1, i+1) ;
221  }
222 
223  /* Store the edges for the fit */
224  slit_x_l[i] = (double)left_pos;
225  slit_x_r[i] = (double)right_pos;
226  slit_y[i] = (double)(i+slit_bot_y-1);
227  }
228  cpl_image_delete(thin_im);
229 
230  /* Linear regression to find the edges */
231  coeff_l = irplib_flat_fit_slope_robust(slit_y, slit_x_l, slit_length);
232  coeff_r = irplib_flat_fit_slope_robust(slit_y, slit_x_r, slit_length);
233  cpl_free(slit_y);
234  cpl_free(slit_x_l);
235  cpl_free(slit_x_r);
236 
237  /* Allocate the table containing the results */
238  self = cpl_table_new(slit_length);
239  error |= cpl_table_new_column(self, "SLIT_Y", CPL_TYPE_INT);
240  error |= cpl_table_new_column(self, "SLIT_LEFT", CPL_TYPE_DOUBLE);
241  error |= cpl_table_new_column(self, "SLIT_CENTER", CPL_TYPE_DOUBLE);
242  error |= cpl_table_new_column(self, "SLIT_RIGHT", CPL_TYPE_DOUBLE);
243 
244  error |= cpl_table_set_column_unit(self, "SLIT_Y", "pixel");
245  error |= cpl_table_set_column_unit(self, "SLIT_LEFT", "pixel");
246  error |= cpl_table_set_column_unit(self, "SLIT_CENTER", "pixel");
247  error |= cpl_table_set_column_unit(self, "SLIT_RIGHT", "pixel");
248 
249  cpl_ensure(!error, cpl_error_get_code(), NULL);
250 
251  /* Rewrite the edges in the out table, and write the center */
252  for (i=0 ; i < slit_length ; i++) {
253  const int islity = i + slit_bot_y;
254  const double dslit = slit_pos - slit_max_width / 2.0;
255  const double dleft = coeff_l[0] + coeff_l[1] * (double)islity + dslit;
256  const double dright = coeff_r[0] + coeff_r[1] * (double)islity + dslit;
257  const double dcent = 0.5 * (dleft + dright);
258 
259  if (cpl_table_set_int(self, "SLIT_Y", i, islity)) break;
260  if (cpl_table_set_double(self, "SLIT_LEFT", i, dleft)) break;
261  if (cpl_table_set_double(self, "SLIT_RIGHT", i, dright)) break;
262  if (cpl_table_set_double(self, "SLIT_CENTER", i, dcent)) break;
263  }
264 
265  cpl_free(coeff_r);
266  cpl_free(coeff_l);
267 
268  if (i != slit_length) {
269  cpl_table_delete(self);
270  cpl_ensure(0, cpl_error_get_code(), NULL);
271  }
272 
273  return self;
274 }
275 
278 /*----------------------------------------------------------------------------*/
290 /*----------------------------------------------------------------------------*/
291 static cpl_error_code irplib_slitpos_find_edges_one_line(const cpl_image * self,
292  int line_pos,
293  int * left_pos,
294  int * right_pos)
295 {
296  const int size_x = cpl_image_get_size_x(self);
297  const float * pself;
298  double threshold;
299  int i;
300 
301  cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
302  cpl_ensure_code(cpl_image_get_type(self) == CPL_TYPE_FLOAT,
303  CPL_ERROR_INVALID_TYPE);
304 
305  pself = cpl_image_get_data_float_const(self);
306 
307  /* Find the threshold */
308  threshold = cpl_image_get_mean_window(self, 1, line_pos+1, size_x,
309  line_pos+1);
310 
311  /* Detect the left edge */
312  i = 0;
313  while (i < size_x && pself[line_pos*size_x+i] < threshold) i++;
314  *left_pos = i;
315 
316  /* Detect the right edge */
317  i = size_x - 1;
318  while (i >= 0 && pself[line_pos*size_x+i] < threshold) i--;
319  *right_pos = i;
320 
321  return CPL_ERROR_NONE;
322 }
323 
324 /*----------------------------------------------------------------------------*/
335 /*----------------------------------------------------------------------------*/
336 static
337 cpl_error_code irplib_slitpos_find_vert_slit_ends(const cpl_image * self,
338  int kernel_size,
339  int * bot_slit_y,
340  int * top_slit_y)
341 {
342  cpl_mask * binary;
343  cpl_mask * copy = NULL;
344  cpl_mask * kernel;
345  cpl_image * label_image;
346  int erosions_nb;
347  cpl_size nobj ;
348  const int size_x = cpl_image_get_size_x(self);
349  const int size_y = cpl_image_get_size_y(self);
350  const int npix = size_x * size_y;
351  const cpl_binary * pbinary;
352  const cpl_binary * pfind;
353  int i, itop, ibot;
354 
355 
356  cpl_ensure_code(size_x > 0, cpl_error_get_code());
357  cpl_ensure_code(kernel_size > 0, cpl_error_get_code());
358 
359  /* Threshold to have a binary image */
360  binary = cpl_mask_threshold_image_create(self, cpl_image_get_mean(self),
361  cpl_image_get_max(self));
362  cpl_ensure_code(binary != NULL, cpl_error_get_code());
363 
364  /* Erode until there is 1 object left in the image */
365  label_image = cpl_image_labelise_mask_create(binary, &nobj);
366  cpl_image_delete(label_image);
367 
368  if (label_image == NULL) {
369  cpl_mask_delete(binary);
370  cpl_ensure_code(0, cpl_error_get_code());
371  }
372 
373  /* Define the kernel for morpho operations */
374  kernel = cpl_mask_new(kernel_size, 1);
375  cpl_mask_not(kernel);
376  copy = cpl_mask_wrap(size_x, size_y, cpl_malloc(size_x * size_y *
377  sizeof(cpl_binary)));
378  for (erosions_nb = 0; erosions_nb < IRPLIB_SLITPOS_MAX_EROSION && nobj > 1;
379  erosions_nb++) {
380  /* Should not be possible to break from this loop */
381  cpl_mask_copy(copy, binary, 1, 1);
382  if (cpl_mask_filter(binary, copy, kernel, CPL_FILTER_EROSION,
383  CPL_BORDER_ZERO)) break;
384 
385  label_image = cpl_image_labelise_mask_create(binary, &nobj);
386  if (label_image == NULL) break; /* Assuming nobj was not set to 1 */
387  cpl_image_delete(label_image);
388  }
389 
390  if (nobj > 1) {
391  cpl_mask_delete(binary);
392  cpl_mask_delete(copy);
393  cpl_mask_delete(kernel);
394  if (erosions_nb >= IRPLIB_SLITPOS_MAX_EROSION) {
395  cpl_msg_error(cpl_func, "Number of erosions reached a limit of %d "
396  "with %"CPL_SIZE_FORMAT" possible slits left",
397  IRPLIB_SLITPOS_MAX_EROSION, nobj);
398  cpl_ensure_code(0, CPL_ERROR_CONTINUE);
399  }
400  cpl_ensure_code(0, cpl_error_get_code());
401  } else if (nobj < 1) {
402  cpl_mask_delete(binary);
403  cpl_mask_delete(copy);
404  cpl_mask_delete(kernel);
405  if (erosions_nb == 0)
406  cpl_msg_error(cpl_func, "No slit could be detected across %d "
407  "pixels", size_x);
408  else
409  cpl_msg_error(cpl_func, "The last of %d erosions removed all the "
410  "possible slits", erosions_nb);
411  cpl_ensure_code(0, CPL_ERROR_DATA_NOT_FOUND);
412  }
413 
414  /* Reconstruct the slit with dilations */
415  for (i=0 ; i < erosions_nb ; i++) {
416  cpl_mask_copy(copy, binary, 1, 1);
417  if (cpl_mask_filter(binary, copy, kernel, CPL_FILTER_DILATION,
418  CPL_BORDER_ZERO)) break;
419  }
420  cpl_mask_delete(copy);
421  cpl_mask_delete(kernel);
422 
423  if (i != erosions_nb) {
424  cpl_msg_error(cpl_func, "Dilation number %d out of %d failed",
425  i, erosions_nb);
426  cpl_mask_delete(binary);
427  cpl_ensure_code(0, cpl_error_get_code());
428  }
429 
430  /* Find the ends of the slit */
431  pbinary = cpl_mask_get_data(binary);
432  assert( pbinary != NULL );
433 
434  pfind = memchr(pbinary, CPL_BINARY_1, (size_t)npix);
435  assert( pfind != NULL );
436 
437  ibot = (int)(pfind - pbinary);
438 
439 #if defined HAVE_DECL_MEMRCHR && HAVE_DECL_MEMRCHR == 1
440  /* FIXME: Not tested */
441  pfind = memrchr(pfind, CPL_BINARY_1, (size_t)(npix - ibot));
442  assert( pfind != NULL );
443 
444  itop = (int)(pfind - pbinary);
445 #else
446 
447  itop = npix - 1;
448  while (itop > ibot && pbinary[itop] == CPL_BINARY_0) itop--;
449 
450 #endif
451 
452  *bot_slit_y = 1 + ibot / size_x;
453  *top_slit_y = 1 + itop / size_x;
454 
455  cpl_msg_info(cpl_func,
456  "Detected %"CPL_SIZE_FORMAT"-pixel slit from pixel %d to %d "
457  "using %d erosions/dilations", cpl_mask_count(binary),
458  *bot_slit_y, *top_slit_y, erosions_nb);
459 
460  cpl_mask_delete(binary);
461 
462  /* Should really be an assert() */
463  cpl_ensure_code(ibot <= itop, CPL_ERROR_DATA_NOT_FOUND);
464 
465  return CPL_ERROR_NONE;
466 }
467 
468 /*----------------------------------------------------------------------------*/
478 /*----------------------------------------------------------------------------*/
479 static cpl_error_code irplib_slitpos_find_vert_pos(const cpl_image * self,
480  int xwidth,
481  cpl_size * slit_pos)
482 {
483  const int size_x = cpl_image_get_size_x(self);
484  cpl_image * image1D;
485  cpl_size yone;
486  cpl_error_code error;
487 
488 
489  /* Collapse the image to a horizontal 1D image */
490  image1D = cpl_image_collapse_create(self, 0);
491 
492  cpl_ensure_code(image1D != NULL, cpl_error_get_code());
493 
494  /* Search the max of the 1D image to identify the slit position */
495  error = cpl_image_get_maxpos_window(image1D, 1+xwidth, 1, size_x-xwidth,
496  1, slit_pos, &yone);
497 
498  cpl_image_delete(image1D);
499 
500  cpl_ensure_code(!error, error);
501 
502  return CPL_ERROR_NONE;
503 }
504 
505 /*----------------------------------------------------------------------------*/
519 /*----------------------------------------------------------------------------*/
520 static cpl_error_code irplib_image_filter_background_line(cpl_image * self,
521  const cpl_image * other,
522  int hsize,
523  cpl_boolean vertical)
524 {
525  const int nx = cpl_image_get_size_x(self);
526  const int ny = cpl_image_get_size_y(self);
527  const int msize = 1 + 2 * hsize;
528  cpl_mask * mask;
529  cpl_image * background;
530  cpl_error_code error = CPL_ERROR_NONE;
531 
532  cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
533  cpl_ensure_code(hsize >= 0, CPL_ERROR_ILLEGAL_INPUT);
534 
535  if (other == NULL) other = self;
536 
537  mask = vertical ? cpl_mask_new(msize, 1) : cpl_mask_new(1, msize);
538 
539  error |= cpl_mask_not(mask);
540 
541  background = cpl_image_new(nx, ny, cpl_image_get_type(other));
542 
543  error |= cpl_image_filter_mask(background, other, mask, CPL_FILTER_MEDIAN,
544  CPL_BORDER_FILTER);
545  cpl_mask_delete(mask);
546 
547  if (self != other) {
548  error |= cpl_image_copy(self, other, 1, 1);
549  }
550 
551  error |= cpl_image_subtract(self, background);
552  cpl_image_delete(background);
553 
554  return error ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
555 }
556 
557 
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...