00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044 #include <string.h>
00045 #include <math.h>
00046 #include <assert.h>
00047 #include <cpl.h>
00048
00049 #include "irplib_slitpos.h"
00050 #include "irplib_flat.h"
00051
00052
00053
00054
00055
00056
00057 #ifndef CPL_SIZE_FORMAT
00058 #define CPL_SIZE_FORMAT "d"
00059 #define cpl_size int
00060 #endif
00061
00062
00063 #ifndef IRPLIB_SLITPOS_KERNEL_SIZE_Y
00064 #define IRPLIB_SLITPOS_KERNEL_SIZE_Y 5
00065 #endif
00066
00067 #ifndef IRPLIB_SLITPOS_MAX_EROSION
00068 #define IRPLIB_SLITPOS_MAX_EROSION 1024
00069 #endif
00070
00071
00072
00073
00074
00075 static cpl_error_code irplib_slitpos_find_edges_one_line(const cpl_image *,
00076 int, int *, int *);
00077 static cpl_error_code irplib_slitpos_find_vert_slit_ends(const cpl_image *,
00078 int, int *, int *);
00079 static cpl_error_code irplib_slitpos_find_vert_pos(const cpl_image *, int,
00080 cpl_size *);
00081 static cpl_error_code irplib_image_filter_background_line(cpl_image *,
00082 const cpl_image *,
00083 int, cpl_boolean) ;
00084
00085
00089
00090
00092
00118
00119 cpl_table * irplib_slitpos_analysis(const cpl_image * imslit,
00120 int slit_max_width,
00121 double * slit_flux)
00122 {
00123 const int size_x = cpl_image_get_size_x(imslit);
00124 const int size_y = cpl_image_get_size_y(imslit);
00125 int slit_length;
00126 cpl_size slit_pos;
00127 cpl_image * filtered;
00128 cpl_mask * mask;
00129 cpl_image * thin_im;
00130 int slit_top_y = 0;
00131 int slit_bot_y = 0;
00132 cpl_table * self;
00133 double * slit_y,
00134 * slit_x_l,
00135 * slit_x_r;
00136 double * coeff_r;
00137 double * coeff_l;
00138 int i;
00139 cpl_error_code error = CPL_ERROR_NONE;
00140
00141
00142 if (slit_flux != NULL) *slit_flux = 0.0 ;
00143
00144
00145 mask = cpl_mask_new(3, 3) ;
00146 cpl_mask_not(mask) ;
00147 filtered = cpl_image_new(size_x, size_y, cpl_image_get_type(imslit));
00148 error = cpl_image_filter_mask(filtered, imslit, mask,
00149 CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
00150 cpl_mask_delete(mask);
00151
00152 if (error) {
00153 cpl_image_delete(filtered);
00154 cpl_ensure(0, cpl_error_get_code(), NULL);
00155 }
00156
00157
00158
00159 error = irplib_image_filter_background_line(filtered, NULL, slit_max_width,
00160 CPL_TRUE);
00161
00162 if (error) {
00163 cpl_image_delete(filtered) ;
00164 cpl_ensure(0, cpl_error_get_code(), NULL);
00165 }
00166
00167
00168 if (irplib_slitpos_find_vert_pos(filtered, slit_max_width/2, &slit_pos)) {
00169 cpl_image_delete(filtered);
00170 cpl_msg_error(cpl_func, "Could not find the slit position");
00171 cpl_ensure(0, cpl_error_get_code(), NULL);
00172 }
00173
00174
00175 thin_im = cpl_image_extract(filtered, slit_pos-slit_max_width/2, 1,
00176 slit_pos+slit_max_width/2, size_y);
00177 if (thin_im == NULL) {
00178 cpl_msg_error(cpl_func, "Could not extract the %d pixel thin image "
00179 "around position %"CPL_SIZE_FORMAT,
00180 slit_max_width, slit_pos);
00181 cpl_image_delete(filtered);
00182 cpl_ensure(0, cpl_error_get_code(), NULL);
00183 }
00184
00185
00186 error = irplib_slitpos_find_vert_slit_ends(thin_im,
00187 IRPLIB_SLITPOS_KERNEL_SIZE_Y,
00188 &slit_bot_y,
00189 &slit_top_y);
00190 cpl_image_delete(thin_im);
00191 if (error) {
00192 cpl_image_delete(filtered);
00193 cpl_ensure(0, cpl_error_get_code(), NULL);
00194 }
00195
00196
00197 thin_im = cpl_image_extract(filtered,
00198 slit_pos-slit_max_width/2,
00199 slit_bot_y,
00200 slit_pos+slit_max_width/2,
00201 slit_top_y);
00202 cpl_image_delete(filtered);
00203
00204 cpl_ensure(thin_im != NULL, cpl_error_get_code(), NULL);
00205
00206 slit_length = 1 + slit_top_y - slit_bot_y;
00207
00208
00209 slit_y = cpl_malloc(slit_length * sizeof(double));
00210 slit_x_l = cpl_malloc(slit_length * sizeof(double));
00211 slit_x_r = cpl_malloc(slit_length * sizeof(double));
00212
00213
00214 for (i=0 ; i<slit_length ; i++) {
00215 int right_pos = 0;
00216 int left_pos = 0;
00217
00218 if (irplib_slitpos_find_edges_one_line(thin_im,
00219 i,
00220 &left_pos,
00221 &right_pos)) {
00222 cpl_msg_error(cpl_func, "cannot find the edges of the [%d]th line",
00223 i+1);
00224 cpl_image_delete(thin_im);
00225 return NULL;
00226 }
00227
00228
00229 if (slit_flux != NULL) {
00230 *slit_flux += cpl_image_get_flux_window(thin_im, left_pos+1,
00231 i+1, right_pos+1, i+1) ;
00232 }
00233
00234
00235 slit_x_l[i] = (double)left_pos;
00236 slit_x_r[i] = (double)right_pos;
00237 slit_y[i] = (double)(i+slit_bot_y-1);
00238 }
00239 cpl_image_delete(thin_im);
00240
00241
00242 coeff_l = irplib_flat_fit_slope_robust(slit_y, slit_x_l, slit_length);
00243 coeff_r = irplib_flat_fit_slope_robust(slit_y, slit_x_r, slit_length);
00244 cpl_free(slit_y);
00245 cpl_free(slit_x_l);
00246 cpl_free(slit_x_r);
00247
00248
00249 self = cpl_table_new(slit_length);
00250 error |= cpl_table_new_column(self, "SLIT_Y", CPL_TYPE_INT);
00251 error |= cpl_table_new_column(self, "SLIT_LEFT", CPL_TYPE_DOUBLE);
00252 error |= cpl_table_new_column(self, "SLIT_CENTER", CPL_TYPE_DOUBLE);
00253 error |= cpl_table_new_column(self, "SLIT_RIGHT", CPL_TYPE_DOUBLE);
00254
00255 error |= cpl_table_set_column_unit(self, "SLIT_Y", "pixel");
00256 error |= cpl_table_set_column_unit(self, "SLIT_LEFT", "pixel");
00257 error |= cpl_table_set_column_unit(self, "SLIT_CENTER", "pixel");
00258 error |= cpl_table_set_column_unit(self, "SLIT_RIGHT", "pixel");
00259
00260 cpl_ensure(!error, cpl_error_get_code(), NULL);
00261
00262
00263 for (i=0 ; i < slit_length ; i++) {
00264 const int islity = i + slit_bot_y;
00265 const double dslit = slit_pos - slit_max_width / 2.0;
00266 const double dleft = coeff_l[0] + coeff_l[1] * (double)islity + dslit;
00267 const double dright = coeff_r[0] + coeff_r[1] * (double)islity + dslit;
00268 const double dcent = 0.5 * (dleft + dright);
00269
00270 if (cpl_table_set_int(self, "SLIT_Y", i, islity)) break;
00271 if (cpl_table_set_double(self, "SLIT_LEFT", i, dleft)) break;
00272 if (cpl_table_set_double(self, "SLIT_RIGHT", i, dright)) break;
00273 if (cpl_table_set_double(self, "SLIT_CENTER", i, dcent)) break;
00274 }
00275
00276 cpl_free(coeff_r);
00277 cpl_free(coeff_l);
00278
00279 if (i != slit_length) {
00280 cpl_table_delete(self);
00281 cpl_ensure(0, cpl_error_get_code(), NULL);
00282 }
00283
00284 return self;
00285 }
00286
00289
00301
00302 static cpl_error_code irplib_slitpos_find_edges_one_line(const cpl_image * self,
00303 int line_pos,
00304 int * left_pos,
00305 int * right_pos)
00306 {
00307 const int size_x = cpl_image_get_size_x(self);
00308 const float * pself;
00309 double threshold;
00310 int i;
00311
00312 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
00313 cpl_ensure_code(cpl_image_get_type(self) == CPL_TYPE_FLOAT,
00314 CPL_ERROR_INVALID_TYPE);
00315
00316 pself = cpl_image_get_data_float_const(self);
00317
00318
00319 threshold = cpl_image_get_mean_window(self, 1, line_pos+1, size_x,
00320 line_pos+1);
00321
00322
00323 i = 0;
00324 while (i < size_x && pself[line_pos*size_x+i] < threshold) i++;
00325 *left_pos = i;
00326
00327
00328 i = size_x - 1;
00329 while (i >= 0 && pself[line_pos*size_x+i] < threshold) i--;
00330 *right_pos = i;
00331
00332 return CPL_ERROR_NONE;
00333 }
00334
00335
00346
00347 static
00348 cpl_error_code irplib_slitpos_find_vert_slit_ends(const cpl_image * self,
00349 int kernel_size,
00350 int * bot_slit_y,
00351 int * top_slit_y)
00352 {
00353 cpl_mask * binary;
00354 cpl_mask * copy = NULL;
00355 cpl_mask * kernel;
00356 cpl_image * label_image;
00357 int erosions_nb;
00358 cpl_size nobj ;
00359 const int size_x = cpl_image_get_size_x(self);
00360 const int size_y = cpl_image_get_size_y(self);
00361 const int npix = size_x * size_y;
00362 const cpl_binary * pbinary;
00363 const cpl_binary * pfind;
00364 int i, itop, ibot;
00365
00366
00367 cpl_ensure_code(size_x > 0, cpl_error_get_code());
00368 cpl_ensure_code(kernel_size > 0, cpl_error_get_code());
00369
00370
00371 binary = cpl_mask_threshold_image_create(self, cpl_image_get_mean(self),
00372 cpl_image_get_max(self));
00373 cpl_ensure_code(binary != NULL, cpl_error_get_code());
00374
00375
00376 label_image = cpl_image_labelise_mask_create(binary, &nobj);
00377 cpl_image_delete(label_image);
00378
00379 if (label_image == NULL) {
00380 cpl_mask_delete(binary);
00381 cpl_ensure_code(0, cpl_error_get_code());
00382 }
00383
00384
00385 kernel = cpl_mask_new(kernel_size, 1);
00386 cpl_mask_not(kernel);
00387 copy = cpl_mask_wrap(size_x, size_y, cpl_malloc(size_x * size_y *
00388 sizeof(cpl_binary)));
00389 for (erosions_nb = 0; erosions_nb < IRPLIB_SLITPOS_MAX_EROSION && nobj > 1;
00390 erosions_nb++) {
00391
00392 cpl_mask_copy(copy, binary, 1, 1);
00393 if (cpl_mask_filter(binary, copy, kernel, CPL_FILTER_EROSION,
00394 CPL_BORDER_ZERO)) break;
00395
00396 label_image = cpl_image_labelise_mask_create(binary, &nobj);
00397 if (label_image == NULL) break;
00398 cpl_image_delete(label_image);
00399 }
00400
00401 if (nobj > 1) {
00402 cpl_mask_delete(binary);
00403 cpl_mask_delete(copy);
00404 cpl_mask_delete(kernel);
00405 if (erosions_nb >= IRPLIB_SLITPOS_MAX_EROSION) {
00406 cpl_msg_error(cpl_func, "Number of erosions reached a limit of %d "
00407 "with %"CPL_SIZE_FORMAT" possible slits left",
00408 IRPLIB_SLITPOS_MAX_EROSION, nobj);
00409 cpl_ensure_code(0, CPL_ERROR_CONTINUE);
00410 }
00411 cpl_ensure_code(0, cpl_error_get_code());
00412 } else if (nobj < 1) {
00413 cpl_mask_delete(binary);
00414 cpl_mask_delete(copy);
00415 cpl_mask_delete(kernel);
00416 if (erosions_nb == 0)
00417 cpl_msg_error(cpl_func, "No slit could be detected across %d "
00418 "pixels", size_x);
00419 else
00420 cpl_msg_error(cpl_func, "The last of %d erosions removed all the "
00421 "possible slits", erosions_nb);
00422 cpl_ensure_code(0, CPL_ERROR_DATA_NOT_FOUND);
00423 }
00424
00425
00426 for (i=0 ; i < erosions_nb ; i++) {
00427 cpl_mask_copy(copy, binary, 1, 1);
00428 if (cpl_mask_filter(binary, copy, kernel, CPL_FILTER_DILATION,
00429 CPL_BORDER_ZERO)) break;
00430 }
00431 cpl_mask_delete(copy);
00432 cpl_mask_delete(kernel);
00433
00434 if (i != erosions_nb) {
00435 cpl_msg_error(cpl_func, "Dilation number %d out of %d failed",
00436 i, erosions_nb);
00437 cpl_mask_delete(binary);
00438 cpl_ensure_code(0, cpl_error_get_code());
00439 }
00440
00441
00442 pbinary = cpl_mask_get_data(binary);
00443 assert( pbinary != NULL );
00444
00445 pfind = memchr(pbinary, CPL_BINARY_1, (size_t)npix);
00446 assert( pfind != NULL );
00447
00448 ibot = (int)(pfind - pbinary);
00449
00450 #if defined HAVE_DECL_MEMRCHR && HAVE_DECL_MEMRCHR == 1
00451
00452 pfind = memrchr(pfind, CPL_BINARY_1, (size_t)(npix - ibot));
00453 assert( pfind != NULL );
00454
00455 itop = (int)(pfind - pbinary);
00456 #else
00457
00458 itop = npix - 1;
00459 while (itop > ibot && pbinary[itop] == CPL_BINARY_0) itop--;
00460
00461 #endif
00462
00463 *bot_slit_y = 1 + ibot / size_x;
00464 *top_slit_y = 1 + itop / size_x;
00465
00466 cpl_msg_info(cpl_func,
00467 "Detected %"CPL_SIZE_FORMAT"-pixel slit from pixel %d to %d "
00468 "using %d erosions/dilations", cpl_mask_count(binary),
00469 *bot_slit_y, *top_slit_y, erosions_nb);
00470
00471 cpl_mask_delete(binary);
00472
00473
00474 cpl_ensure_code(ibot <= itop, CPL_ERROR_DATA_NOT_FOUND);
00475
00476 return CPL_ERROR_NONE;
00477 }
00478
00479
00489
00490 static cpl_error_code irplib_slitpos_find_vert_pos(const cpl_image * self,
00491 int xwidth,
00492 cpl_size * slit_pos)
00493 {
00494 const int size_x = cpl_image_get_size_x(self);
00495 cpl_image * image1D;
00496 cpl_size yone;
00497 cpl_error_code error;
00498
00499
00500
00501 image1D = cpl_image_collapse_create(self, 0);
00502
00503 cpl_ensure_code(image1D != NULL, cpl_error_get_code());
00504
00505
00506 error = cpl_image_get_maxpos_window(image1D, 1+xwidth, 1, size_x-xwidth,
00507 1, slit_pos, &yone);
00508
00509 cpl_image_delete(image1D);
00510
00511 cpl_ensure_code(!error, error);
00512
00513 return CPL_ERROR_NONE;
00514 }
00515
00516
00530
00531 static cpl_error_code irplib_image_filter_background_line(cpl_image * self,
00532 const cpl_image * other,
00533 int hsize,
00534 cpl_boolean vertical)
00535 {
00536 const int nx = cpl_image_get_size_x(self);
00537 const int ny = cpl_image_get_size_y(self);
00538 const int msize = 1 + 2 * hsize;
00539 cpl_mask * mask;
00540 cpl_image * background;
00541 cpl_error_code error = CPL_ERROR_NONE;
00542
00543 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
00544 cpl_ensure_code(hsize >= 0, CPL_ERROR_ILLEGAL_INPUT);
00545
00546 if (other == NULL) other = self;
00547
00548 mask = vertical ? cpl_mask_new(msize, 1) : cpl_mask_new(1, msize);
00549
00550 error |= cpl_mask_not(mask);
00551
00552 background = cpl_image_new(nx, ny, cpl_image_get_type(other));
00553
00554 error |= cpl_image_filter_mask(background, other, mask, CPL_FILTER_MEDIAN,
00555 CPL_BORDER_FILTER);
00556 cpl_mask_delete(mask);
00557
00558 if (self != other) {
00559 error |= cpl_image_copy(self, other, 1, 1);
00560 }
00561
00562 error |= cpl_image_subtract(self, background);
00563 cpl_image_delete(background);
00564
00565 return error ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
00566 }
00567
00568