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 #include <math.h>
00037 #include <float.h>
00038 #include <cpl.h>
00039
00040 #include "irplib_spectrum.h"
00041
00042
00043
00044
00045
00046 #define SPECTRUM_HW 16
00047 #define MIN_THRESH_FACT 0.9
00048 #define MAX_THRESH_FACT 1.1
00049 #define SPEC_SHADOW_FACT 30.0
00050 #define SPEC_MAXWIDTH 48
00051
00052
00053
00054
00055
00056 static int select_valid_spectra(cpl_image *, cpl_apertures *, int,
00057 spec_shadows, int, int *, int **) ;
00058 static int valid_spectrum(cpl_image *, cpl_apertures *, int, spec_shadows, int,
00059 int) ;
00060
00061
00065
00066
00069
00084
00085 int irplib_spectrum_find_brightest(
00086 const cpl_image * in,
00087 int offset,
00088 spec_shadows shadows,
00089 double min_bright,
00090 int orient,
00091 double * pos)
00092 {
00093 cpl_image * loc_ima ;
00094 cpl_image * filt_image ;
00095 cpl_matrix * kernel ;
00096 cpl_image * collapsed ;
00097 float * pcollapsed ;
00098 cpl_vector * line ;
00099 double * pline ;
00100 cpl_vector * line_filt ;
00101 double threshold ;
00102 double median, stdev, max, mean ;
00103 cpl_mask * mask ;
00104 cpl_image * labels ;
00105 int nlabels ;
00106 cpl_apertures * aperts ;
00107 int n_valid_specs ;
00108 int * valid_specs ;
00109 double brightness, brightest ;
00110 int i ;
00111
00112
00113 if (in == NULL) return -1 ;
00114 if (orient!=0 && orient!=1) return -1 ;
00115
00116
00117 if (orient == 1) {
00118 loc_ima = cpl_image_duplicate(in) ;
00119 cpl_image_flip(loc_ima, 1) ;
00120 } else {
00121 loc_ima = cpl_image_duplicate(in) ;
00122 }
00123
00124
00125 kernel = cpl_matrix_new(3, 3) ;
00126 cpl_matrix_fill(kernel, 1.0) ;
00127 if ((filt_image = cpl_image_filter_median(loc_ima, kernel)) == NULL) {
00128 cpl_matrix_delete(kernel) ;
00129 cpl_image_delete(loc_ima) ;
00130 cpl_msg_error(cpl_func, "cannot filter the image") ;
00131 return -1 ;
00132 }
00133 cpl_image_delete(loc_ima) ;
00134 cpl_matrix_delete(kernel) ;
00135
00136
00137 if ((collapsed = cpl_image_collapse_median_create(filt_image, 1, 0,
00138 0)) == NULL) {
00139 cpl_msg_error(cpl_func, "collapsing image: aborting spectrum detection");
00140 cpl_image_delete(filt_image) ;
00141 return -1 ;
00142 }
00143 cpl_image_delete(filt_image) ;
00144
00145
00146 line = cpl_vector_new_from_image_column(collapsed, 1) ;
00147 cpl_image_delete(collapsed) ;
00148 line_filt = cpl_vector_filter_median_create(line, SPECTRUM_HW) ;
00149 cpl_vector_subtract(line, line_filt) ;
00150 cpl_vector_delete(line_filt) ;
00151
00152
00153 median = cpl_vector_get_median_const(line) ;
00154 stdev = cpl_vector_get_stdev(line) ;
00155 max = cpl_vector_get_max(line) ;
00156 mean = cpl_vector_get_mean(line) ;
00157
00158
00159 threshold = median + stdev ;
00160 if (threshold > MIN_THRESH_FACT * max) threshold = MIN_THRESH_FACT * max ;
00161 if (threshold < MAX_THRESH_FACT * mean) threshold = MAX_THRESH_FACT * mean;
00162
00163
00164 collapsed = cpl_image_new(1, cpl_vector_get_size(line), CPL_TYPE_FLOAT) ;
00165 pcollapsed = cpl_image_get_data_float(collapsed) ;
00166 pline = cpl_vector_get_data(line) ;
00167 for (i=0 ; i<cpl_vector_get_size(line) ; i++)
00168 pcollapsed[i] = (float)pline[i] ;
00169 cpl_vector_delete(line) ;
00170
00171
00172 if ((mask = cpl_mask_threshold_image_create(collapsed, threshold,
00173 DBL_MAX)) == NULL) {
00174 cpl_msg_error(cpl_func, "cannot binarise") ;
00175 cpl_image_delete(collapsed) ;
00176 return -1 ;
00177 }
00178 if (cpl_mask_count(mask) < 1) {
00179 cpl_msg_error(cpl_func, "not enough signal to detect spectra") ;
00180 cpl_image_delete(collapsed) ;
00181 cpl_mask_delete(mask) ;
00182 return -1 ;
00183 }
00184
00185 if ((labels = cpl_image_labelise_mask_create(mask, &nlabels))==NULL) {
00186 cpl_msg_error(cpl_func, "cannot labelise") ;
00187 cpl_image_delete(collapsed) ;
00188 cpl_mask_delete(mask) ;
00189 return -1 ;
00190 }
00191 cpl_mask_delete(mask) ;
00192
00193
00194 if ((aperts = cpl_apertures_new_from_image(collapsed, labels)) == NULL) {
00195 cpl_msg_error(cpl_func, "cannot compute apertures") ;
00196 cpl_image_delete(collapsed) ;
00197 cpl_image_delete(labels) ;
00198 return -1 ;
00199 }
00200 cpl_image_delete(labels) ;
00201
00202
00203 if (select_valid_spectra(collapsed, aperts, offset, shadows, SPEC_MAXWIDTH,
00204 &n_valid_specs, &valid_specs) == -1) {
00205 cpl_msg_debug(cpl_func, "cannot select valid spectra") ;
00206 cpl_image_delete(collapsed) ;
00207 cpl_apertures_delete(aperts) ;
00208 return -1 ;
00209 }
00210 cpl_image_delete(collapsed) ;
00211 if (n_valid_specs < 1) {
00212 cpl_msg_error(cpl_func, "no valid spectrum detected") ;
00213 cpl_free(valid_specs) ;
00214 cpl_apertures_delete(aperts) ;
00215 return -1 ;
00216 }
00217
00218
00219 *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[0]+1) ;
00220 brightest = valid_specs[0] ;
00221 brightness = cpl_apertures_get_flux(aperts, valid_specs[0]+1) ;
00222 for (i=0 ; i<n_valid_specs ; i++) {
00223 if (cpl_apertures_get_flux(aperts, valid_specs[i]+1) > brightness) {
00224 *pos = cpl_apertures_get_centroid_y(aperts, valid_specs[i]+1) ;
00225 brightest = valid_specs[i] ;
00226 brightness = cpl_apertures_get_flux(aperts, valid_specs[i]+1) ;
00227 }
00228 }
00229 cpl_apertures_delete(aperts) ;
00230 cpl_free(valid_specs) ;
00231
00232
00233 if (brightness < min_bright) {
00234 cpl_msg_error(cpl_func, "brightness %f too low <%f", brightness,
00235 min_bright) ;
00236 return -1 ;
00237 }
00238
00239
00240 return 0 ;
00241 }
00242
00245
00257
00258 static int select_valid_spectra(
00259 cpl_image * in,
00260 cpl_apertures * aperts,
00261 int offset,
00262 spec_shadows shadows,
00263 int max_spec_width,
00264 int * n_valid_specs,
00265 int ** valid_specs)
00266 {
00267 int nb_aperts ;
00268 int i, j ;
00269
00270
00271 *valid_specs = NULL ;
00272 nb_aperts = cpl_apertures_get_size(aperts) ;
00273 *n_valid_specs = 0 ;
00274
00275
00276 if (nb_aperts < 1) return -1 ;
00277
00278
00279 j = 0 ;
00280 for (i=0 ; i<nb_aperts ; i++)
00281 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
00282 i+1)) (*n_valid_specs)++ ;
00283
00284
00285 if (*n_valid_specs) {
00286 *valid_specs = cpl_calloc(*n_valid_specs, sizeof(int)) ;
00287 j = 0 ;
00288 for (i=0 ; i<nb_aperts ; i++)
00289 if (valid_spectrum(in, aperts, offset, shadows, max_spec_width,
00290 i+1)) {
00291 (*valid_specs)[j] = i ;
00292 j++ ;
00293 }
00294 } else return -1 ;
00295
00296 return 0 ;
00297 }
00298
00299
00310
00311 static int valid_spectrum(
00312 cpl_image * in,
00313 cpl_apertures * aperts,
00314 int offset,
00315 spec_shadows shadows,
00316 int max_spec_width,
00317 int objnum)
00318 {
00319 int objwidth ;
00320 double valover, valunder, valcenter ;
00321
00322
00323 objwidth = cpl_apertures_get_top(aperts, objnum) -
00324 cpl_apertures_get_bottom(aperts, objnum) + 1 ;
00325 if (objwidth > max_spec_width) {
00326 cpl_msg_error(cpl_func, "object is too wide") ;
00327 return 0 ;
00328 }
00329
00330
00331 if (cpl_apertures_get_npix(aperts, objnum) < 2) return 0 ;
00332
00333
00334 if (shadows == NO_SHADOW) return 1 ;
00335
00336
00337 valcenter = cpl_apertures_get_median(aperts, objnum) ;
00338
00339
00340 if (cpl_apertures_get_bottom(aperts, objnum) - offset < 1) valunder = 0.0 ;
00341 else valunder = cpl_image_get_median_window(in, 1,
00342 cpl_apertures_get_bottom(aperts, objnum) - offset, 1,
00343 cpl_apertures_get_top(aperts, objnum) - offset) ;
00344
00345 if (cpl_apertures_get_top(aperts, objnum) + offset > 1024) valover = 0.0 ;
00346 else valover = cpl_image_get_median_window(in, 1,
00347 cpl_apertures_get_bottom(aperts, objnum) + offset, 1,
00348 cpl_apertures_get_top(aperts, objnum) + offset) ;
00349
00350 switch (shadows) {
00351 case TWO_SHADOWS:
00352 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
00353 (valover < -fabs(valcenter/SPEC_SHADOW_FACT)) &&
00354 (valunder/valover > 0.5) &&
00355 (valunder/valover < 2.0)) return 1 ;
00356 else return 0 ;
00357
00358 case ONE_SHADOW:
00359 if ((valunder < -fabs(valcenter/SPEC_SHADOW_FACT)) ||
00360 (valover < -fabs(valcenter/SPEC_SHADOW_FACT))) return 1 ;
00361 else return 0 ;
00362
00363 case NO_SHADOW:
00364 return 1 ;
00365
00366 default:
00367 cpl_msg_error(cpl_func, "unknown spec_detect_mode") ;
00368 break ;
00369 }
00370
00371 return 0 ;
00372 }