SINFONI Pipeline Reference Manual  2.6.0
sinfo_function_1d.c
1 /*
2  * This file is part of the ESO SINFONI Pipeline
3  * Copyright (C) 2004,2005 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, 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
18  */
19 /*----------------------------------------------------------------------------
20 
21  File name : function_1d.c
22  Author : Nicolas Devillard
23  Created on : Tue, Sept 23 1997
24  Description : 1d signal processing related routines
25 
26  ---------------------------------------------------------------------------*/
27 /*
28  $Id: sinfo_function_1d.c,v 1.7 2012-03-02 08:42:20 amodigli Exp $
29  $Author: amodigli $
30  $Date: 2012-03-02 08:42:20 $
31  $Revision: 1.7 $
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 # include <config.h>
36 #endif
37 /*----------------------------------------------------------------------------
38  Includes
39  ---------------------------------------------------------------------------*/
40 #include <math.h>
41 #include <string.h>
42 #include "sinfo_function_1d.h"
43 #include "sinfo_fit_curve.h"
44 #include "sinfo_median.h"
45 
54 /*----------------------------------------------------------------------------
55  Defines
56  ---------------------------------------------------------------------------*/
57 /*
58  * This parameter sets up the half size of the domain around which a
59  * centroid position will be computed.
60  */
61 #define HALF_CENTROID_DOMAIN 5
62 /*----------------------------------------------------------------------------
63  Private function prototypes
64  ---------------------------------------------------------------------------*/
65 static double * function1d_generate_smooth_kernel(int filt_type, int hw);
66 
67 static int
68 function1d_search_value(
69  pixelvalue * x,
70  int len,
71  pixelvalue key,
72  int * foundPtr
73 ) ;
74 
75 /*----------------------------------------------------------------------------
76  Function codes
77  ---------------------------------------------------------------------------*/
94 pixelvalue *
95 sinfo_function1d_new(int nsamples)
96 {
97  if (nsamples<1) return NULL ;
98  return cpl_calloc(nsamples, sizeof(pixelvalue)) ;
99 }
100 
101 
111 void
112 sinfo_function1d_del(pixelvalue * s)
113 {
114  if (s)
115  cpl_free(s);
116  return ;
117 }
118 
135 pixelvalue *
136 sinfo_function1d_dup(pixelvalue * arr, int ns)
137 {
138  pixelvalue * n_arr ;
139 
140  n_arr = sinfo_function1d_new(ns);
141  memcpy(n_arr, arr, ns * sizeof(pixelvalue));
142  return n_arr ;
143 }
144 
160 double
161 sinfo_function1d_find_centroid(
162  pixelvalue * line, /* the input line */
163  int npix /* number of pixels in this line */
164 )
165 {
166  pixelvalue max ;
167  double centroid ;
168  double weights ;
169  int i, maxpos ;
170 
171  /*
172  * Search for the maximum pixel value on the line
173  */
174 
175  max = line[0] ;
176  maxpos = 0 ;
177  for (i=1 ; i<npix ; i++) {
178  if (line[i]>max) {
179  max = line[i] ;
180  maxpos = i ;
181  }
182  }
183 
184  /*
185  * The centroid position is the weighted average over the maximum
186  * pixel neighborhood.
187  */
188 
189  centroid = 0.0 ;
190  weights = 0.0 ;
191  for (i=maxpos-HALF_CENTROID_DOMAIN;
192  i<=maxpos+HALF_CENTROID_DOMAIN; i++) {
193  centroid += (double)line[i] * (double)i ;
194  weights += (double)line[i] ;
195  }
196 
197  centroid /= weights ;
198  return centroid ;
199 }
200 
223 double
224 sinfo_function1d_find_locmax(
225  pixelvalue * line,
226  int npix,
227  int where,
228  int hs
229 )
230 {
231  pixelvalue max ;
232  double centroid ;
233  double weights ;
234  int i, maxpos ;
235 
236 
237  if ((where<hs) || (where>(npix-hs-1))) {
238  return (double)-1.0 ;
239  }
240 
241  /*
242  * Search for the closest local maximal around the requested range.
243  */
244  max = line[where] ;
245  maxpos = where ;
246  for (i=-hs ; i<=hs ; i++) {
247  if (line[where+i]>max) {
248  max = line[where+i] ;
249  maxpos = where+i ;
250  }
251  }
252 
253  /*
254  * The centroid position is the weighted average over the maximum
255  * pixel neighborhood.
256  */
257 
258  centroid = 0.0 ;
259  weights = 0.0 ;
260  for (i=maxpos-hs; i<=maxpos+hs; i++) {
261  centroid += (double)line[i] * (double)i ;
262  weights += (double)line[i] ;
263  }
264  if (fabs(weights)>1e-6) {
265  centroid /= weights ;
266  } else {
267  centroid = -1.0 ;
268  }
269  return centroid ;
270 }
271 
296 pixelvalue *
297 sinfo_function1d_filter_lowpass(
298  pixelvalue * input_sig,
299  int samples,
300  int filter_type,
301  int hw
302 )
303 {
304  pixelvalue * out_sig ;
305  int i, j ;
306  double replace ;
307  double * kernel ;
308 
309  /* allocate output signal */
310  out_sig = sinfo_function1d_new(samples);
311 
312  /* generate low-pass filter kernel */
313  kernel = function1d_generate_smooth_kernel(filter_type, hw) ;
314 
315  /* compute sinfo_edge effects for the first hw elements */
316  for (i=0 ; i<hw ; i++) {
317  replace = 0.0 ;
318  for (j=-hw ; j<=hw ; j++) {
319  if (i+j<0) {
320  replace += kernel[hw+j] * (double)input_sig[0] ;
321  } else {
322  replace += kernel[hw+j] * (double)input_sig[i+j] ;
323  }
324  }
325  out_sig[i] = (pixelvalue)replace ;
326  }
327 
328  /* compute sinfo_edge effects for the last hw elements */
329  for (i=samples-hw ; i<samples ; i++) {
330  replace = 0.0 ;
331  for (j=-hw ; j<=hw ; j++) {
332  if (i+j>samples-1) {
333  replace += kernel[hw+j] * (double)input_sig[samples-1] ;
334  } else {
335  replace += kernel[hw+j] * (double)input_sig[i+j] ;
336  }
337  }
338  out_sig[i] = (pixelvalue)replace ;
339  }
340 
341  /* compute all other elements */
342  for (i=hw ; i<samples-hw ; i++) {
343  replace = 0.0 ;
344  for (j=-hw ; j<=hw ; j++) {
345  replace += kernel[hw+j] * (double)input_sig[i+j] ;
346  }
347  out_sig[i] = (pixelvalue)replace ;
348  }
349 
350  cpl_free(kernel) ;
351  return out_sig ;
352 
353 }
354 
371 static double *
372 function1d_generate_smooth_kernel(int filt_type, int hw)
373 {
374  double * kernel ;
375  double norm ;
376  int i ;
377 
378  kernel = (double*)cpl_calloc(2*hw+1, sizeof(double)) ;
379 
380  switch(filt_type) {
381 
382  case LOW_PASS_LINEAR:
383  for (i=-hw ; i<=hw ; i++) {
384  /* flat kernel */
385  kernel[hw+i] = 1.0 / (double)(2*hw+1) ;
386  }
387  break ;
388 
389  case LOW_PASS_GAUSSIAN:
390  norm = 0.00 ;
391  for (i=-hw ; i<=hw ; i++) {
392  /* sinfo_gaussian kernel */
393  kernel[hw+i] = exp(-(double)(i*i)) ;
394  norm += kernel[hw+i] ;
395  }
396  for (i=0 ; i<2*hw+1 ; i++) {
397  kernel[i] /= norm ;
398  }
399  break ;
400 
401  default:
402  sinfo_msg_error("unrecognized low pass filter: "
403  "cannot generate kernel") ;
404  cpl_free( kernel );
405  return (double*)NULL ;
406  break ;
407  }
408 
409  return kernel ;
410 }
411 
430 pixelvalue *
431 sinfo_function1d_median_smooth(
432  pixelvalue * list,
433  int np,
434  int hw)
435 {
436  int i,j ;
437  pixelvalue * row ;
438  pixelvalue * smoothed ;
439 
440  /* simply copy first 3 and last 3 items */
441  smoothed = sinfo_function1d_new(np);
442  for (i=0 ; i<hw ; i++) {
443  smoothed[i] = list[i] ;
444  }
445  for (i=np-hw ; i<np ; i++) {
446  smoothed[i] = list[i] ;
447  }
448 
449  /* sinfo_median filter on all central items */
450  row = sinfo_function1d_new(2*hw+1);
451  for (i=hw ; i<np-hw ; i++) {
452  for (j=-hw ; j<=hw ; j++) {
453  row[j+hw] = list[i+j] ;
454  }
455  smoothed[i] = sinfo_median_pixelvalue(row, 2*hw+1) ;
456  }
457  sinfo_function1d_del(row) ;
458  return smoothed ;
459 }
460 
477 #define LOWFREQ_PASSES 5
478 
479 pixelvalue *
480 sinfo_function1d_remove_lowfreq(
481  pixelvalue * pixel_signal,
482  int ns)
483 {
484  pixelvalue * sig_in ;
485  pixelvalue * smooth ;
486  int i ;
487 
488 
489  /* Apply severe low-pass filter several times */
490  sig_in = sinfo_function1d_dup(pixel_signal, ns);
491  for (i=0 ; i<LOWFREQ_PASSES ; i++) {
492  smooth = sinfo_function1d_filter_lowpass( sig_in, ns,
493  LOW_PASS_LINEAR, 5);
494  cpl_free(sig_in);
495  sig_in = smooth ;
496  }
497 
498  /* Subtract smoothed signal from input signal */
499  for (i=0 ; i<ns ; i++) {
500  smooth[i] = pixel_signal[i] - smooth[i];
501  }
502  return smooth ;
503 }
504 
505 #undef LOWFREQ_PASSES
506 
528 #define SAMPLE_BORDER 10
529 
530 pixelvalue *
531 sinfo_function1d_remove_thermalbg(
532  pixelvalue * pixel_signal,
533  int ns)
534 {
535  pixelvalue * smooth ;
536  int i ;
537 
538  int nmin ;
539  pixelvalue lef[2], rig[2];
540 
541  pixelvalue * x,
542  * y,
543  * spl_x,
544  * spl_y ;
545  double med_y ;
546  double avg2med ;
547 
548 
549 
550  /* Detect all local minima */
551  nmin = 0 ;
552  x = sinfo_function1d_new(ns);
553  y = sinfo_function1d_new(ns);
554 
555  for (i=SAMPLE_BORDER ; i<(ns-SAMPLE_BORDER) ; i++) {
556  lef[0] = pixel_signal[i-2];
557  lef[1] = pixel_signal[i-1];
558  rig[0] = pixel_signal[i+1];
559  rig[1] = pixel_signal[i+2];
560 
561  if ( (pixel_signal[i] < lef[0]) &&
562  (pixel_signal[i] < lef[1]) &&
563  (pixel_signal[i] < rig[0]) &&
564  (pixel_signal[i] < rig[1])) {
565  x[nmin] = (pixelvalue)i ;
566  y[nmin] = pixel_signal[i];
567  nmin ++ ;
568  }
569  }
570 
571 
572  /* Interpolate linearly missing values */
573  spl_x = sinfo_function1d_new(ns);
574  spl_y = sinfo_function1d_new(ns);
575  for (i=0 ; i<ns ; i++) {
576  spl_x[i] = (pixelvalue)i ;
577  }
578  sinfo_function1d_interpolate_linear(x, y, nmin, spl_x, spl_y, ns);
579 
580  sinfo_function1d_del(x) ;
581  sinfo_function1d_del(y) ;
582  sinfo_function1d_del(spl_x);
583 
584  /* Compute sinfo_median and average distance to the sinfo_median */
585  med_y = (double)sinfo_median_pixelvalue(pixel_signal, ns);
586  avg2med = 0.0 ;
587  for (i=0 ; i<ns ; i++) {
588  avg2med += fabs((double)pixel_signal[i] - med_y) ;
589  }
590  avg2med /= (double)ns ;
591 
592  /* Reset all pixels out of sinfo_median + 2 * avg2med to zero. */
593  for (i=0 ; i<ns ; i++) {
594  double dist = fabs((double)pixel_signal[i] - med_y);
595  if (dist > (2.0*avg2med)) {
596  spl_y[i] = (pixelvalue)0 ;
597  }
598  }
599 
600 
601  smooth = sinfo_function1d_new(ns);
602  for (i=0 ; i<ns ; i++) {
603  if (spl_y[i]>1e-4) {
604  smooth[i] = pixel_signal[i] - spl_y[i];
605  } else {
606  smooth[i] = 0.0 ;
607  }
608  }
609  sinfo_function1d_del(spl_y);
610  return smooth ;
611 }
612 
613 #undef LOWFREQ_PASSES
614 
636 void
637 sinfo_function1d_interpolate_linear(
638  pixelvalue * x,
639  pixelvalue * y,
640  int len,
641  pixelvalue * spl_x,
642  pixelvalue * spl_y,
643  int spl_len
644 )
645 {
646  double a, b ;
647  int i, j ;
648 
649 
650  for (i=0 ; i<spl_len ; i++) {
651  /* Find (x1,y1) on the left of the current point */
652  int found = 0 ;
653  for (j=0 ; j<(len-1) ; j++) {
654  if ((spl_x[i]>=x[j]) && (spl_x[i]<=x[j+1])) {
655  found++ ;
656  break ;
657  }
658  }
659  if (!found) {
660  spl_y[i] = 0.0;
661  } else {
662  a = ((double)y[j+1]-(double)y[j]) /
663  ((double)x[j+1]-(double)x[j]);
664  b = (double)y[j] - a * (double)x[j] ;
665  spl_y[i] = (pixelvalue)(a * (double)spl_x[i] + b) ;
666  }
667  }
668  return ;
669 }
670 
687 static int
688 function1d_search_value(
689  pixelvalue * x,
690  int len,
691  pixelvalue key,
692  int * foundPtr
693 )
694 {
695 
696 
697  int low = 0;
698  int high = len - 1;
699 
700  while (high >= low) {
701  int middle = (high + low) / 2;
702  if (key > x[middle]) {
703  low = middle + 1;
704  } else if (key < x[middle]) {
705  high = middle - 1;
706  } else {
707  *foundPtr = 1;
708  return (middle);
709  }
710  }
711  *foundPtr = 0;
712  return (low);
713 }
714 
740 int
741 sinfo_function1d_natural_spline(
742  pixelvalue * x,
743  pixelvalue * y,
744  int len,
745  pixelvalue * splX,
746  pixelvalue * splY,
747  int splLen
748 )
749 {
750  int end;
751  int loc,
752  found;
753  register int i,
754  j,
755  n;
756  double * h; /* sinfo_vector of deltas in x */
757  double * alpha;
758  double * l,
759  * mu,
760  * z,
761  * a,
762  * b,
763  * c,
764  * d;
765 
766  end = len - 1;
767 
768  a = cpl_malloc(sizeof(double) * splLen * 9) ;
769  b = a + len;
770  c = b + len;
771  d = c + len;
772  h = d + len;
773  l = h + len;
774  z = l + len;
775  mu = z + len;
776  alpha = mu + len;
777 
778  for (i = 0; i < len; i++) {
779  a[i] = (double)y[i];
780  }
781 
782  /* Calculate sinfo_vector of differences */
783  for (i = 0; i < end; i++) {
784  h[i] = (double)x[i + 1] - (double)x[i];
785  if (h[i] < 0.0) {
786  cpl_free(a) ;
787  return -1;
788  }
789  }
790 
791  /* Calculate alpha sinfo_vector */
792  for (n = 0, i = 1; i < end; i++, n++) {
793  /* n = i - 1 */
794  alpha[i] = 3.0 * ((a[i+1] / h[i]) - (a[i] / h[n]) - (a[i] / h[i]) +
795  (a[n] / h[n]));
796  }
797 
798  /* Vectors to solve the tridiagonal sinfo_matrix */
799  l[0] = l[end] = 1.0;
800  mu[0] = mu[end] = 0.0;
801  z[0] = z[end] = 0.0;
802  c[0] = c[end] = 0.0;
803 
804  /* Calculate the intermediate results */
805  for (n = 0, i = 1; i < end; i++, n++) {
806  /* n = i-1 */
807  l[i] = 2 * (h[i] + h[n]) - h[n] * mu[n];
808  mu[i] = h[i] / l[i];
809  z[i] = (alpha[i] - h[n] * z[n]) / l[i];
810  }
811  for (n = end, j = end - 1; j >= 0; j--, n--) {
812  /* n = j + 1 */
813  c[j] = z[j] - mu[j] * c[n];
814  b[j] = (a[n] - a[j]) / h[j] - h[j] * (c[n] + 2.0 * c[j]) / 3.0;
815  d[j] = (c[n] - c[j]) / (3.0 * h[j]);
816  }
817 
818  /* Now calculate the new values */
819  for (j = 0; j < splLen; j++) {
820  double v = (double)splX[j];
821  splY[j] = (pixelvalue)0;
822 
823  /* Is it outside the interval? */
824  if ((v < (double)x[0]) || (v > (double)x[end])) {
825  continue;
826  }
827  /* Search for the interval containing v in the x sinfo_vector */
828  loc = function1d_search_value(x, len, (pixelvalue)v, &found);
829  if (found) {
830  splY[j] = y[loc];
831  } else {
832  loc--;
833  v -= (double)x[loc];
834  splY[j] = (pixelvalue)( a[loc] +
835  v * (b[loc] +
836  v * (c[loc] +
837  v * d[loc])));
838  }
839  }
840  cpl_free(a) ;
841  return 0;
842 }
843 
862 pixelvalue
863 sinfo_function1d_average_reject(
864  pixelvalue * line,
865  int npix,
866  int pix_low,
867  int pix_high)
868 {
869  pixelvalue * sorted ;
870  int i ;
871  double avg ;
872 
873  /* Sanity tests */
874  if ((line==NULL) || (npix<1)) return (pixelvalue)0 ;
875  if ((pix_low+pix_high)>=npix) return (pixelvalue)0 ;
876 
877  /* Copy input line and sort it */
878  sorted = cpl_malloc(npix * sizeof(pixelvalue)) ;
879  memcpy(sorted, line, npix * sizeof(pixelvalue)) ;
880  sinfo_pixel_qsort(sorted, npix);
881 
882  /* Find out average of remaining values */
883  avg = 0.00 ;
884  for (i=pix_low+1 ; i<(npix-pix_high) ; i++) {
885  avg += (double)sorted[i] ;
886  }
887  cpl_free(sorted);
888  avg /= (double)(npix - pix_high - pix_low) ;
889 
890  return (pixelvalue)avg ;
891 }
892 
918 #define STEP_MIN (-half_search)
919 #define STEP_MAX (half_search)
920 
921 double
922 sinfo_function1d_xcorrelate(
923  pixelvalue * line_i,
924  int width_i,
925  pixelvalue * line_t,
926  int width_t,
927  int half_search,
928  double * delta
929 )
930 {
931  double * xcorr ;
932  double xcorr_max ;
933  double mean_i, mean_t ;
934  double rms_i, rms_t ;
935  double sum, sqsum ;
936  double norm ;
937  int maxpos ;
938  int nsteps ;
939  int i ;
940  int step ;
941 
942 
943 
944  /* Compute normalization factors */
945  sum = sqsum = 0.00 ;
946  for (i=0 ; i<width_i ; i++) {
947  sum += (double)line_i[i] ;
948  sqsum += (double)line_i[i] * (double)line_i[i];
949  }
950  mean_i = sum / (double)width_i ;
951  sqsum /= (double)width_i ;
952  rms_i = sqsum - mean_i*mean_i ;
953 
954  sum = sqsum = 0.00 ;
955  for (i=0 ; i<width_t ; i++) {
956  sum += (double)line_t[i] ;
957  sqsum += (double)line_t[i] * (double)line_t[i];
958  }
959  mean_t = sum / (double)width_t ;
960  sqsum /= (double)width_t ;
961  rms_t = sqsum - mean_t*mean_t ;
962 
963  norm = 1.00 / sqrt(rms_i * rms_t);
964 
965  nsteps = (STEP_MAX - STEP_MIN) +1 ;
966  xcorr = cpl_malloc(nsteps * sizeof(double));
967  for (step=STEP_MIN ; step<=STEP_MAX ; step++) {
968  xcorr[step-STEP_MIN] = 0.00 ;
969  int nval = 0 ;
970  for (i=0 ; i<width_t ; i++) {
971  if ((i+step > 0) &&
972  (i+step < width_i)) {
973  xcorr[step-STEP_MIN] += ((double)line_t[i] - mean_t) *
974  ((double)line_i[i+step] - mean_i) *
975  norm ;
976  nval++ ;
977  }
978  }
979  xcorr[step-STEP_MIN] /= (double)nval ;
980  }
981  xcorr_max = xcorr[0] ;
982  maxpos = 0 ;
983  for (i=0 ; i<nsteps ; i++) {
984  if (xcorr[i]>xcorr_max) {
985  maxpos = i ;
986  xcorr_max = xcorr[i];
987  }
988  }
989  cpl_free(xcorr);
990  (*delta) = + ((double)STEP_MIN + (double)maxpos);
991  return xcorr_max ;
992 }
#define sinfo_msg_error(...)
Print an error message.
Definition: sinfo_msg.h:69