/* * This file is part of the ESO SINFONI Pipeline * Copyright (C) 2004,2005 European Southern Observatory * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ /*---------------------------------------------------------------------------- File name : function_1d.c Author : Nicolas Devillard Created on : Tue, Sept 23 1997 Description : 1d signal processing related routines ---------------------------------------------------------------------------*/ /* $Id: sinfo_function_1d.c,v 1.7 2012-03-02 08:42:20 amodigli Exp $ $Author: amodigli $ $Date: 2012-03-02 08:42:20 $ $Revision: 1.7 $ */ #ifdef HAVE_CONFIG_H # include #endif /*---------------------------------------------------------------------------- Includes ---------------------------------------------------------------------------*/ #include #include #include "sinfo_function_1d.h" #include "sinfo_fit_curve.h" #include "sinfo_median.h" /**@{*/ /** * @defgroup sinfo_function_1d 1d functions * * TBD */ /*---------------------------------------------------------------------------- Defines ---------------------------------------------------------------------------*/ /* * This parameter sets up the half size of the domain around which a * centroid position will be computed. */ #define HALF_CENTROID_DOMAIN 5 /*---------------------------------------------------------------------------- Private function prototypes ---------------------------------------------------------------------------*/ static double * function1d_generate_smooth_kernel(int filt_type, int hw); static int function1d_search_value( pixelvalue * x, int len, pixelvalue key, int * foundPtr ) ; /*---------------------------------------------------------------------------- Function codes ---------------------------------------------------------------------------*/ /** @name sinfo_function1d_new @memo Allocates a new array of pixelvalues. @param nsamples Number of values to store in the array. @return Pointer to newly allocated array of pixelvalues. @doc The returned array must be freed using sinfo_function1d_del(), not cpl_free(). This is in case some further housekeeping attributes are allocated together with the object in the future. Returns NULL in case of error. */ pixelvalue * sinfo_function1d_new(int nsamples) { if (nsamples<1) return NULL ; return cpl_calloc(nsamples, sizeof(pixelvalue)) ; } /** @name sinfo_function1d_del @memo Deallocate an array of pixelvalues. @param s Array to deallocate. @return void @doc Deallocates an array allocated by sinfo_function1d_new(). */ void sinfo_function1d_del(pixelvalue * s) { if (s) cpl_free(s); return ; } /** @name sinfo_function1d_dup @memo Copy an array of pixelvalues to a new array. @param arr Array to copy. @param ns Number of samples in the array. @return Pointer to newly allocated array of pixelvalues. @doc Creates a new array using sinfo_function1d_new(), with the same number of samples as the input signal, then copies over all values from source to destination array using memcpy(). The returned array must be freed using sinfo_function1d_del(), not cpl_free(). */ pixelvalue * sinfo_function1d_dup(pixelvalue * arr, int ns) { pixelvalue * n_arr ; n_arr = sinfo_function1d_new(ns); memcpy(n_arr, arr, ns * sizeof(pixelvalue)); return n_arr ; } /** @name sinfo_function1d_find_centroid @memo Find out a line centroid to subpixel precision. @param line Array of pixels. @param npix Number of pixels in the array. @return Centroid position as a double. @doc The input signal is assumed to be flat almost everywhere, with a single peak somewhere around the middle. Other kinds of signals are not handled correctly. The position of the peak is located to subpixel precision by simply weighting positions with pixelvalues. TODO: not used */ double sinfo_function1d_find_centroid( pixelvalue * line, /* the input line */ int npix /* number of pixels in this line */ ) { pixelvalue max ; double centroid ; double weights ; int i, maxpos ; /* * Search for the maximum pixel value on the line */ max = line[0] ; maxpos = 0 ; for (i=1 ; imax) { max = line[i] ; maxpos = i ; } } /* * The centroid position is the weighted average over the maximum * pixel neighborhood. */ centroid = 0.0 ; weights = 0.0 ; for (i=maxpos-HALF_CENTROID_DOMAIN; i<=maxpos+HALF_CENTROID_DOMAIN; i++) { centroid += (double)line[i] * (double)i ; weights += (double)line[i] ; } centroid /= weights ; return centroid ; } /** @name sinfo_function1d_find_locmax @memo Find out a local maximum in a 1d signal around a position. @param line Array of pixels. @param npix Number of pixels in the array. @param where Where to look around. @param hs Half-size of the search domain. @return Local maximum position as a double. @doc The closest local maximum to the given position is located to subpixel precision. This precision is achieved by simply weighting positions with pixelvalues. The 'where' parameter indicates where to look for a maximum as an index in the array, i.e. it must lie between 0 and npix-1 (inclusive). The 'hs' parameter indicates the half-size of the search domain, i.e. if hs=5 a local maximum will be searched +/-5 pixels around the requested position. Returns a negative value if an error occurred. TODO: not used */ double sinfo_function1d_find_locmax( pixelvalue * line, int npix, int where, int hs ) { pixelvalue max ; double centroid ; double weights ; int i, maxpos ; if ((where(npix-hs-1))) { return (double)-1.0 ; } /* * Search for the closest local maximal around the requested range. */ max = line[where] ; maxpos = where ; for (i=-hs ; i<=hs ; i++) { if (line[where+i]>max) { max = line[where+i] ; maxpos = where+i ; } } /* * The centroid position is the weighted average over the maximum * pixel neighborhood. */ centroid = 0.0 ; weights = 0.0 ; for (i=maxpos-hs; i<=maxpos+hs; i++) { centroid += (double)line[i] * (double)i ; weights += (double)line[i] ; } if (fabs(weights)>1e-6) { centroid /= weights ; } else { centroid = -1.0 ; } return centroid ; } /** @name sinfo_function1d_filter_lowpass @memo Apply a low-pass filter to a 1d signal. @param input_sig Input signal @param samples Number of samples in the signal @param filter_type Type of filter to use. @param hw Filter half-width. @return Pointer to newly allocated array of pixels. @doc This kind of low-pass filtering consists in a convolution with a given kernel. The chosen filter type determines the kind of kernel to apply for convolution. Possible kernels and associated symbols can be found in function_1d.h. Smoothing the signal is done by applying this kind of low-pass filter several times. The returned smooth signal has been allocated using sinfo_function1d_new(), it must be freed using sinfo_function1d_del(). The returned signal has exactly as many samples as the input signal. */ pixelvalue * sinfo_function1d_filter_lowpass( pixelvalue * input_sig, int samples, int filter_type, int hw ) { pixelvalue * out_sig ; int i, j ; double replace ; double * kernel ; /* allocate output signal */ out_sig = sinfo_function1d_new(samples); /* generate low-pass filter kernel */ kernel = function1d_generate_smooth_kernel(filter_type, hw) ; /* compute sinfo_edge effects for the first hw elements */ for (i=0 ; isamples-1) { replace += kernel[hw+j] * (double)input_sig[samples-1] ; } else { replace += kernel[hw+j] * (double)input_sig[i+j] ; } } out_sig[i] = (pixelvalue)replace ; } /* compute all other elements */ for (i=hw ; i (2.0*avg2med)) { spl_y[i] = (pixelvalue)0 ; } } smooth = sinfo_function1d_new(ns); for (i=0 ; i1e-4) { smooth[i] = pixel_signal[i] - spl_y[i]; } else { smooth[i] = 0.0 ; } } sinfo_function1d_del(spl_y); return smooth ; } #undef LOWFREQ_PASSES /** @name sinfo_function1d_interpolate_linear @memo Linear signal interpolation. @param x Input list of x positions. @param y Input list of y positions. @param len Number of samples in x and y. @param spl_x List of abscissas where the signal must be computed. @param spl_y Output list of computed signal values. @param spl_len Number of samples in spl_x and spl_y. @return void @doc To apply this interpolation, you need to provide a list of x and y positions, and a list of x positions where you want y to be computed (with linear interpolation). The returned signal has spl_len samples. It has been allocated using sinfo_function1d_new() and must be deallocated using sinfo_function1d_del(). */ void sinfo_function1d_interpolate_linear( pixelvalue * x, pixelvalue * y, int len, pixelvalue * spl_x, pixelvalue * spl_y, int spl_len ) { double a, b ; int i, j ; for (i=0 ; i=x[j]) && (spl_x[i]<=x[j+1])) { found++ ; break ; } } if (!found) { spl_y[i] = 0.0; } else { a = ((double)y[j+1]-(double)y[j]) / ((double)x[j+1]-(double)x[j]); b = (double)y[j] - a * (double)x[j] ; spl_y[i] = (pixelvalue)(a * (double)spl_x[i] + b) ; } } return ; } /** @name function1d_search_value @memo Conducts a binary search for a value. @param x Contains the abscissas of interpolation. @param len Length of the x array. @param key The value to locate in x. @param foundPtr Output flag, 1 if value was found, else 0. @return The index of the largest value in x for which x[i]= low) { int middle = (high + low) / 2; if (key > x[middle]) { low = middle + 1; } else if (key < x[middle]) { high = middle - 1; } else { *foundPtr = 1; return (middle); } } *foundPtr = 0; return (low); } /** @name sinfo_function1d_natural_spline @memo Interpolate a sinfo_vector along new abscissas. @param x List of x positions. @param y List of y positions. @param len Number of samples in x and y. @param splX Input new list of x positions. @param splY Output list of interpolated y positions. @param splLen Number of samples in splX and splY. @return Int 0 if Ok, -1 if error. @doc Reference: \begin{verbatim} Numerical Analysis, R. Burden, J. Faires and A. Reynolds. Prindle, Weber & Schmidt 1981 pp 112 \end{verbatim} Provide in input a known list of x and y values, and a list where you want the signal to be interpolated. The returned signal is written into splY. */ int sinfo_function1d_natural_spline( pixelvalue * x, pixelvalue * y, int len, pixelvalue * splX, pixelvalue * splY, int splLen ) { int end; int loc, found; register int i, j, n; double * h; /* sinfo_vector of deltas in x */ double * alpha; double * l, * mu, * z, * a, * b, * c, * d; end = len - 1; a = cpl_malloc(sizeof(double) * splLen * 9) ; b = a + len; c = b + len; d = c + len; h = d + len; l = h + len; z = l + len; mu = z + len; alpha = mu + len; for (i = 0; i < len; i++) { a[i] = (double)y[i]; } /* Calculate sinfo_vector of differences */ for (i = 0; i < end; i++) { h[i] = (double)x[i + 1] - (double)x[i]; if (h[i] < 0.0) { cpl_free(a) ; return -1; } } /* Calculate alpha sinfo_vector */ for (n = 0, i = 1; i < end; i++, n++) { /* n = i - 1 */ alpha[i] = 3.0 * ((a[i+1] / h[i]) - (a[i] / h[n]) - (a[i] / h[i]) + (a[n] / h[n])); } /* Vectors to solve the tridiagonal sinfo_matrix */ l[0] = l[end] = 1.0; mu[0] = mu[end] = 0.0; z[0] = z[end] = 0.0; c[0] = c[end] = 0.0; /* Calculate the intermediate results */ for (n = 0, i = 1; i < end; i++, n++) { /* n = i-1 */ l[i] = 2 * (h[i] + h[n]) - h[n] * mu[n]; mu[i] = h[i] / l[i]; z[i] = (alpha[i] - h[n] * z[n]) / l[i]; } for (n = end, j = end - 1; j >= 0; j--, n--) { /* n = j + 1 */ c[j] = z[j] - mu[j] * c[n]; b[j] = (a[n] - a[j]) / h[j] - h[j] * (c[n] + 2.0 * c[j]) / 3.0; d[j] = (c[n] - c[j]) / (3.0 * h[j]); } /* Now calculate the new values */ for (j = 0; j < splLen; j++) { double v = (double)splX[j]; splY[j] = (pixelvalue)0; /* Is it outside the interval? */ if ((v < (double)x[0]) || (v > (double)x[end])) { continue; } /* Search for the interval containing v in the x sinfo_vector */ loc = function1d_search_value(x, len, (pixelvalue)v, &found); if (found) { splY[j] = y[loc]; } else { loc--; v -= (double)x[loc]; splY[j] = (pixelvalue)( a[loc] + v * (b[loc] + v * (c[loc] + v * d[loc]))); } } cpl_free(a) ; return 0; } /** @name sinfo_function1d_average_reject @memo Sorts the input signal, takes out highest and lowest values, and returns the average of the remaining pixels. @param line Input signal. @param npix Number of samples in the input signal. @param pix_low Number of lowest pixels to reject. @param pix_high Number of highest pixels to reject. @return The filtered average of the input signal. @doc No input parameter is modified. The input signal is first copied. This copy is then sorted, and the highest and lowest pixels are taken out of the list. Remaining pixelvalues are averaged and the result is returned. TODO: not used */ pixelvalue sinfo_function1d_average_reject( pixelvalue * line, int npix, int pix_low, int pix_high) { pixelvalue * sorted ; int i ; double avg ; /* Sanity tests */ if ((line==NULL) || (npix<1)) return (pixelvalue)0 ; if ((pix_low+pix_high)>=npix) return (pixelvalue)0 ; /* Copy input line and sort it */ sorted = cpl_malloc(npix * sizeof(pixelvalue)) ; memcpy(sorted, line, npix * sizeof(pixelvalue)) ; sinfo_pixel_qsort(sorted, npix); /* Find out average of remaining values */ avg = 0.00 ; for (i=pix_low+1 ; i<(npix-pix_high) ; i++) { avg += (double)sorted[i] ; } cpl_free(sorted); avg /= (double)(npix - pix_high - pix_low) ; return (pixelvalue)avg ; } /** @name sinfo_function1d_xcorrelate @memo Cross-sinfo_correlation of two 1d signals. @param line_i The reference signal. @param width_i Number of samples in reference signal. @param line_t Candidate signal to compare. @param width_t Number of samples in candidate signal. @param half_search Half-size of the search domain. @param delta Output sinfo_correlation offset. @return Maximum cross-sinfo_correlation value as a double. @doc Two signals are expected in input of this function: a reference signal and a candidate signal. They are expected to be roughly the same signal up to an offset. A cross-sinfo_correlation is computed on 2*half_search+1 values. The maximum of likelihood is the maximum cross-sinfo_correlation value between signals. The offset corresponding to this position is returned. Returns -100.0 in case of error. Normally, the cross-sinfo_correlation coefficient is normalized so it should stay between -1 and +1. TODO: not used */ #define STEP_MIN (-half_search) #define STEP_MAX (half_search) double sinfo_function1d_xcorrelate( pixelvalue * line_i, int width_i, pixelvalue * line_t, int width_t, int half_search, double * delta ) { double * xcorr ; double xcorr_max ; double mean_i, mean_t ; double rms_i, rms_t ; double sum, sqsum ; double norm ; int maxpos ; int nsteps ; int i ; int step ; /* Compute normalization factors */ sum = sqsum = 0.00 ; for (i=0 ; i 0) && (i+step < width_i)) { xcorr[step-STEP_MIN] += ((double)line_t[i] - mean_t) * ((double)line_i[i+step] - mean_i) * norm ; nval++ ; } } xcorr[step-STEP_MIN] /= (double)nval ; } xcorr_max = xcorr[0] ; maxpos = 0 ; for (i=0 ; ixcorr_max) { maxpos = i ; xcorr_max = xcorr[i]; } } cpl_free(xcorr); (*delta) = + ((double)STEP_MIN + (double)maxpos); return xcorr_max ; } /**@}*/