CR2RE Pipeline Reference Manual 1.6.2
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
61static cpl_error_code irplib_slitpos_find_edges_one_line(const cpl_image *,
62 int, int *, int *);
63static cpl_error_code irplib_slitpos_find_vert_slit_ends(const cpl_image *,
64 int, int *, int *);
65static cpl_error_code irplib_slitpos_find_vert_pos(const cpl_image *, int,
66 cpl_size *);
67static cpl_error_code irplib_image_filter_background_line(cpl_image *,
68 const cpl_image *,
69 int, cpl_boolean) ;
70
71/*----------------------------------------------------------------------------*/
75/*----------------------------------------------------------------------------*/
76
78/*----------------------------------------------------------------------------*/
104/*----------------------------------------------------------------------------*/
105cpl_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 (void)cpl_error_set_where(cpl_func);
141 return NULL;
142 }
143
144 /* The background may vary strongly along the vertical line. */
145 /* Detect and remove background with a 1+2*Slit_max x 1 median filter */
146 error = irplib_image_filter_background_line(filtered, NULL, slit_max_width,
147 CPL_TRUE);
148
149 if (error) {
150 cpl_image_delete(filtered) ;
151 (void)cpl_error_set_where(cpl_func);
152 return NULL;
153 }
154
155 /* Find the position of the slit */
156 if (irplib_slitpos_find_vert_pos(filtered, slit_max_width/2, &slit_pos)) {
157 cpl_image_delete(filtered);
158 cpl_msg_error(cpl_func, "Could not find the slit position");
159 (void)cpl_error_set_where(cpl_func);
160 return NULL;
161 }
162
163 /* Extract a thin image containing the slit */
164 thin_im = cpl_image_extract(filtered, slit_pos-slit_max_width/2, 1,
165 slit_pos+slit_max_width/2, size_y);
166 if (thin_im == NULL) {
167 cpl_msg_error(cpl_func, "Could not extract the %d pixel thin image "
168 "around position %"CPL_SIZE_FORMAT,
169 slit_max_width, slit_pos);
170 cpl_image_delete(filtered);
171 (void)cpl_error_set_where(cpl_func);
172 return NULL;
173 }
174
175 /* Find the ends of the slit */
176 error = irplib_slitpos_find_vert_slit_ends(thin_im,
177 IRPLIB_SLITPOS_KERNEL_SIZE_Y,
178 &slit_bot_y,
179 &slit_top_y);
180 cpl_image_delete(thin_im);
181 if (error) {
182 cpl_image_delete(filtered);
183 (void)cpl_error_set_where(cpl_func);
184 return NULL;
185 }
186
187 /* Extract an image with exactly the slit */
188 thin_im = cpl_image_extract(filtered,
189 slit_pos-slit_max_width/2,
190 slit_bot_y,
191 slit_pos+slit_max_width/2,
192 slit_top_y);
193 cpl_image_delete(filtered);
194
195 cpl_ensure(thin_im != NULL, cpl_error_get_code(), NULL);
196
197 slit_length = 1 + slit_top_y - slit_bot_y;
198
199 /* Allocate some arrays */
200 slit_y = cpl_malloc(slit_length * sizeof(double));
201 slit_x_l = cpl_malloc(slit_length * sizeof(double));
202 slit_x_r = cpl_malloc(slit_length * sizeof(double));
203
204 /* Find the edges of the slit */
205 for (i=0 ; i<slit_length ; i++) {
206 int right_pos = 0; /* Avoid (false) uninit warning */
207 int left_pos = 0; /* Avoid (false) uninit warning */
208
209 if (irplib_slitpos_find_edges_one_line(thin_im,
210 i,
211 &left_pos,
212 &right_pos)) {
213 cpl_msg_error(cpl_func, "cannot find the edges of the [%d]th line",
214 i+1);
215 cpl_image_delete(thin_im);
216 cpl_free(slit_y);
217 cpl_free(slit_x_l);
218 cpl_free(slit_x_r);
219 return NULL;
220 }
221
222 /* Update the slit_flux */
223 if (slit_flux != NULL) {
224 *slit_flux += cpl_image_get_flux_window(thin_im, left_pos+1,
225 i+1, right_pos+1, i+1) ;
226 }
227
228 /* Store the edges for the fit */
229 slit_x_l[i] = (double)left_pos;
230 slit_x_r[i] = (double)right_pos;
231 slit_y[i] = (double)(i+slit_bot_y-1);
232 }
233 cpl_image_delete(thin_im);
234
235 /* Linear regression to find the edges */
236 coeff_l = irplib_flat_fit_slope_robust(slit_y, slit_x_l, slit_length);
237 coeff_r = irplib_flat_fit_slope_robust(slit_y, slit_x_r, slit_length);
238 cpl_free(slit_y);
239 cpl_free(slit_x_l);
240 cpl_free(slit_x_r);
241
242 /* Allocate the table containing the results */
243 self = cpl_table_new(slit_length);
244 error |= cpl_table_new_column(self, "SLIT_Y", CPL_TYPE_INT);
245 error |= cpl_table_new_column(self, "SLIT_LEFT", CPL_TYPE_DOUBLE);
246 error |= cpl_table_new_column(self, "SLIT_CENTER", CPL_TYPE_DOUBLE);
247 error |= cpl_table_new_column(self, "SLIT_RIGHT", CPL_TYPE_DOUBLE);
248
249 error |= cpl_table_set_column_unit(self, "SLIT_Y", "pixel");
250 error |= cpl_table_set_column_unit(self, "SLIT_LEFT", "pixel");
251 error |= cpl_table_set_column_unit(self, "SLIT_CENTER", "pixel");
252 error |= cpl_table_set_column_unit(self, "SLIT_RIGHT", "pixel");
253
254 cpl_ensure(!error, cpl_error_get_code(), NULL);
255
256 /* Rewrite the edges in the out table, and write the center */
257 for (i=0 ; i < slit_length ; i++) {
258 const int islity = i + slit_bot_y;
259 const double dslit = slit_pos - slit_max_width / 2.0;
260 const double dleft = coeff_l[0] + coeff_l[1] * (double)islity + dslit;
261 const double dright = coeff_r[0] + coeff_r[1] * (double)islity + dslit;
262 const double dcent = 0.5 * (dleft + dright);
263
264 if (cpl_table_set_int(self, "SLIT_Y", i, islity)) break;
265 if (cpl_table_set_double(self, "SLIT_LEFT", i, dleft)) break;
266 if (cpl_table_set_double(self, "SLIT_RIGHT", i, dright)) break;
267 if (cpl_table_set_double(self, "SLIT_CENTER", i, dcent)) break;
268 }
269
270 cpl_free(coeff_r);
271 cpl_free(coeff_l);
272
273 if (i != slit_length) {
274 cpl_table_delete(self);
275 cpl_ensure(0, cpl_error_get_code(), NULL);
276 }
277
278 return self;
279}
280
283/*----------------------------------------------------------------------------*/
295/*----------------------------------------------------------------------------*/
296static cpl_error_code irplib_slitpos_find_edges_one_line(const cpl_image * self,
297 int line_pos,
298 int * left_pos,
299 int * right_pos)
300{
301 const int size_x = cpl_image_get_size_x(self);
302 const float * pself;
303 double threshold;
304 int i;
305
306 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
307 cpl_ensure_code(cpl_image_get_type(self) == CPL_TYPE_FLOAT,
308 CPL_ERROR_INVALID_TYPE);
309
310 pself = cpl_image_get_data_float_const(self);
311
312 /* Find the threshold */
313 threshold = cpl_image_get_mean_window(self, 1, line_pos+1, size_x,
314 line_pos+1);
315
316 /* Detect the left edge */
317 i = 0;
318 while (i < size_x && pself[line_pos*size_x+i] < threshold) i++;
319 *left_pos = i;
320
321 /* Detect the right edge */
322 i = size_x - 1;
323 while (i >= 0 && pself[line_pos*size_x+i] < threshold) i--;
324 *right_pos = i;
325
326 return CPL_ERROR_NONE;
327}
328
329/*----------------------------------------------------------------------------*/
340/*----------------------------------------------------------------------------*/
341static
342cpl_error_code irplib_slitpos_find_vert_slit_ends(const cpl_image * self,
343 int kernel_size,
344 int * bot_slit_y,
345 int * top_slit_y)
346{
347 cpl_mask * binary;
348 cpl_mask * copy = NULL;
349 cpl_mask * kernel;
350 cpl_image * label_image;
351 int erosions_nb;
352 cpl_size nobj ;
353 const int size_x = cpl_image_get_size_x(self);
354 const int size_y = cpl_image_get_size_y(self);
355 const int npix = size_x * size_y;
356 const cpl_binary * pbinary;
357 const cpl_binary * pfind;
358 int i, itop, ibot;
359
360
361 cpl_ensure_code(size_x > 0, cpl_error_get_code());
362 cpl_ensure_code(kernel_size > 0, cpl_error_get_code());
363
364 /* Threshold to have a binary image */
365 binary = cpl_mask_threshold_image_create(self, cpl_image_get_mean(self),
366 cpl_image_get_max(self));
367 cpl_ensure_code(binary != NULL, cpl_error_get_code());
368
369 /* Erode until there is 1 object left in the image */
370 label_image = cpl_image_labelise_mask_create(binary, &nobj);
371 cpl_image_delete(label_image);
372
373 if (label_image == NULL) {
374 cpl_mask_delete(binary);
375 cpl_ensure_code(0, cpl_error_get_code());
376 }
377
378 /* Define the kernel for morpho operations */
379 kernel = cpl_mask_new(kernel_size, 1);
380 cpl_mask_not(kernel);
381 copy = cpl_mask_wrap(size_x, size_y, cpl_malloc(size_x * size_y *
382 sizeof(cpl_binary)));
383 for (erosions_nb = 0; erosions_nb < IRPLIB_SLITPOS_MAX_EROSION && nobj > 1;
384 erosions_nb++) {
385 /* Should not be possible to break from this loop */
386 cpl_mask_copy(copy, binary, 1, 1);
387 if (cpl_mask_filter(binary, copy, kernel, CPL_FILTER_EROSION,
388 CPL_BORDER_ZERO)) break;
389
390 label_image = cpl_image_labelise_mask_create(binary, &nobj);
391 if (label_image == NULL) break; /* Assuming nobj was not set to 1 */
392 cpl_image_delete(label_image);
393 }
394
395 if (nobj > 1) {
396 cpl_mask_delete(binary);
397 cpl_mask_delete(copy);
398 cpl_mask_delete(kernel);
399 if (erosions_nb >= IRPLIB_SLITPOS_MAX_EROSION) {
400 cpl_msg_error(cpl_func, "Number of erosions reached a limit of %d "
401 "with %"CPL_SIZE_FORMAT" possible slits left",
402 IRPLIB_SLITPOS_MAX_EROSION, nobj);
403 cpl_ensure_code(0, CPL_ERROR_CONTINUE);
404 }
405 cpl_ensure_code(0, cpl_error_get_code());
406 } else if (nobj < 1) {
407 cpl_mask_delete(binary);
408 cpl_mask_delete(copy);
409 cpl_mask_delete(kernel);
410 if (erosions_nb == 0)
411 cpl_msg_error(cpl_func, "No slit could be detected across %d "
412 "pixels", size_x);
413 else
414 cpl_msg_error(cpl_func, "The last of %d erosions removed all the "
415 "possible slits", erosions_nb);
416 cpl_ensure_code(0, CPL_ERROR_DATA_NOT_FOUND);
417 }
418
419 /* Reconstruct the slit with dilations */
420 for (i=0 ; i < erosions_nb ; i++) {
421 cpl_mask_copy(copy, binary, 1, 1);
422 if (cpl_mask_filter(binary, copy, kernel, CPL_FILTER_DILATION,
423 CPL_BORDER_ZERO)) break;
424 }
425 cpl_mask_delete(copy);
426 cpl_mask_delete(kernel);
427
428 if (i != erosions_nb) {
429 cpl_msg_error(cpl_func, "Dilation number %d out of %d failed",
430 i, erosions_nb);
431 cpl_mask_delete(binary);
432 cpl_ensure_code(0, cpl_error_get_code());
433 }
434
435 /* Find the ends of the slit */
436 pbinary = cpl_mask_get_data(binary);
437 assert( pbinary != NULL );
438
439 pfind = memchr(pbinary, CPL_BINARY_1, (size_t)npix);
440 assert( pfind != NULL );
441
442 ibot = (int)(pfind - pbinary);
443
444#if defined HAVE_DECL_MEMRCHR && HAVE_DECL_MEMRCHR == 1
445 /* FIXME: Not tested */
446 pfind = memrchr(pfind, CPL_BINARY_1, (size_t)(npix - ibot));
447 assert( pfind != NULL );
448
449 itop = (int)(pfind - pbinary);
450#else
451
452 itop = npix - 1;
453 while (itop > ibot && pbinary[itop] == CPL_BINARY_0) itop--;
454
455#endif
456
457 *bot_slit_y = 1 + ibot / size_x;
458 *top_slit_y = 1 + itop / size_x;
459
460 cpl_msg_info(cpl_func,
461 "Detected %"CPL_SIZE_FORMAT"-pixel slit from pixel %d to %d "
462 "using %d erosions/dilations", cpl_mask_count(binary),
463 *bot_slit_y, *top_slit_y, erosions_nb);
464
465 cpl_mask_delete(binary);
466
467 /* Should really be an assert() */
468 cpl_ensure_code(ibot <= itop, CPL_ERROR_DATA_NOT_FOUND);
469
470 return CPL_ERROR_NONE;
471}
472
473/*----------------------------------------------------------------------------*/
483/*----------------------------------------------------------------------------*/
484static cpl_error_code irplib_slitpos_find_vert_pos(const cpl_image * self,
485 int xwidth,
486 cpl_size * slit_pos)
487{
488 const int size_x = cpl_image_get_size_x(self);
489 cpl_image * image1D;
490 cpl_size yone;
491 cpl_error_code error;
492
493
494 /* Collapse the image to a horizontal 1D image */
495 image1D = cpl_image_collapse_create(self, 0);
496
497 cpl_ensure_code(image1D != NULL, cpl_error_get_code());
498
499 /* Search the max of the 1D image to identify the slit position */
500 error = cpl_image_get_maxpos_window(image1D, 1+xwidth, 1, size_x-xwidth,
501 1, slit_pos, &yone);
502
503 cpl_image_delete(image1D);
504
505 cpl_ensure_code(!error, error);
506
507 return CPL_ERROR_NONE;
508}
509
510/*----------------------------------------------------------------------------*/
524/*----------------------------------------------------------------------------*/
525static cpl_error_code irplib_image_filter_background_line(cpl_image * self,
526 const cpl_image * other,
527 int hsize,
528 cpl_boolean vertical)
529{
530 const int nx = cpl_image_get_size_x(self);
531 const int ny = cpl_image_get_size_y(self);
532 const int msize = 1 + 2 * hsize;
533 cpl_mask * mask;
534 cpl_image * background;
535 cpl_error_code error = CPL_ERROR_NONE;
536
537 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
538 cpl_ensure_code(hsize >= 0, CPL_ERROR_ILLEGAL_INPUT);
539
540 if (other == NULL) other = self;
541
542 mask = vertical ? cpl_mask_new(msize, 1) : cpl_mask_new(1, msize);
543
544 error |= cpl_mask_not(mask);
545
546 background = cpl_image_new(nx, ny, cpl_image_get_type(other));
547
548 error |= cpl_image_filter_mask(background, other, mask, CPL_FILTER_MEDIAN,
549 CPL_BORDER_FILTER);
550 cpl_mask_delete(mask);
551
552 if (self != other) {
553 error |= cpl_image_copy(self, other, 1, 1);
554 }
555
556 error |= cpl_image_subtract(self, background);
557 cpl_image_delete(background);
558
559 return error ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
560}
561
562
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:189
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...