SINFONI Pipeline Reference Manual  2.5.2
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  return (double*)NULL ;
405  break ;
406  }
407 
408  return kernel ;
409 }
410 
429 pixelvalue *
430 sinfo_function1d_median_smooth(
431  pixelvalue * list,
432  int np,
433  int hw)
434 {
435  int i,j ;
436  pixelvalue * row ;
437  pixelvalue * smoothed ;
438 
439  /* simply copy first 3 and last 3 items */
440  smoothed = sinfo_function1d_new(np);
441  for (i=0 ; i<hw ; i++) {
442  smoothed[i] = list[i] ;
443  }
444  for (i=np-hw ; i<np ; i++) {
445  smoothed[i] = list[i] ;
446  }
447 
448  /* sinfo_median filter on all central items */
449  row = sinfo_function1d_new(2*hw+1);
450  for (i=hw ; i<np-hw ; i++) {
451  for (j=-hw ; j<=hw ; j++) {
452  row[j+hw] = list[i+j] ;
453  }
454  smoothed[i] = sinfo_median_pixelvalue(row, 2*hw+1) ;
455  }
456  sinfo_function1d_del(row) ;
457  return smoothed ;
458 }
459 
476 #define LOWFREQ_PASSES 5
477 
478 pixelvalue *
479 sinfo_function1d_remove_lowfreq(
480  pixelvalue * pixel_signal,
481  int ns)
482 {
483  pixelvalue * sig_in ;
484  pixelvalue * smooth ;
485  int i ;
486 
487 
488  /* Apply severe low-pass filter several times */
489  sig_in = sinfo_function1d_dup(pixel_signal, ns);
490  for (i=0 ; i<LOWFREQ_PASSES ; i++) {
491  smooth = sinfo_function1d_filter_lowpass( sig_in, ns,
492  LOW_PASS_LINEAR, 5);
493  cpl_free(sig_in);
494  sig_in = smooth ;
495  }
496 
497  /* Subtract smoothed signal from input signal */
498  for (i=0 ; i<ns ; i++) {
499  smooth[i] = pixel_signal[i] - smooth[i];
500  }
501  return smooth ;
502 }
503 
504 #undef LOWFREQ_PASSES
505 
527 #define SAMPLE_BORDER 10
528 
529 pixelvalue *
530 sinfo_function1d_remove_thermalbg(
531  pixelvalue * pixel_signal,
532  int ns)
533 {
534  pixelvalue * smooth ;
535  int i ;
536 
537  int nmin ;
538  pixelvalue lef[2], rig[2];
539 
540  pixelvalue * x,
541  * y,
542  * spl_x,
543  * spl_y ;
544  double med_y ;
545  double avg2med ;
546  double dist ;
547 
548 
549  /* Detect all local minima */
550  nmin = 0 ;
551  x = sinfo_function1d_new(ns);
552  y = sinfo_function1d_new(ns);
553 
554  for (i=SAMPLE_BORDER ; i<(ns-SAMPLE_BORDER) ; i++) {
555  lef[0] = pixel_signal[i-2];
556  lef[1] = pixel_signal[i-1];
557  rig[0] = pixel_signal[i+1];
558  rig[1] = pixel_signal[i+2];
559 
560  if ( (pixel_signal[i] < lef[0]) &&
561  (pixel_signal[i] < lef[1]) &&
562  (pixel_signal[i] < rig[0]) &&
563  (pixel_signal[i] < rig[1])) {
564  x[nmin] = (pixelvalue)i ;
565  y[nmin] = pixel_signal[i];
566  nmin ++ ;
567  }
568  }
569 
570 
571  /* Interpolate linearly missing values */
572  spl_x = sinfo_function1d_new(ns);
573  spl_y = sinfo_function1d_new(ns);
574  for (i=0 ; i<ns ; i++) {
575  spl_x[i] = (pixelvalue)i ;
576  }
577  sinfo_function1d_interpolate_linear(x, y, nmin, spl_x, spl_y, ns);
578 
579  sinfo_function1d_del(x) ;
580  sinfo_function1d_del(y) ;
581  sinfo_function1d_del(spl_x);
582 
583  /* Compute sinfo_median and average distance to the sinfo_median */
584  med_y = (double)sinfo_median_pixelvalue(pixel_signal, ns);
585  avg2med = 0.0 ;
586  for (i=0 ; i<ns ; i++) {
587  avg2med += fabs((double)pixel_signal[i] - med_y) ;
588  }
589  avg2med /= (double)ns ;
590 
591  /* Reset all pixels out of sinfo_median + 2 * avg2med to zero. */
592  for (i=0 ; i<ns ; i++) {
593  dist = fabs((double)pixel_signal[i] - med_y);
594  if (dist > (2.0*avg2med)) {
595  spl_y[i] = (pixelvalue)0 ;
596  }
597  }
598 
599 
600  smooth = sinfo_function1d_new(ns);
601  for (i=0 ; i<ns ; i++) {
602  if (spl_y[i]>1e-4) {
603  smooth[i] = pixel_signal[i] - spl_y[i];
604  } else {
605  smooth[i] = 0.0 ;
606  }
607  }
608  sinfo_function1d_del(spl_y);
609  return smooth ;
610 }
611 
612 #undef LOWFREQ_PASSES
613 
635 void
636 sinfo_function1d_interpolate_linear(
637  pixelvalue * x,
638  pixelvalue * y,
639  int len,
640  pixelvalue * spl_x,
641  pixelvalue * spl_y,
642  int spl_len
643 )
644 {
645  double a, b ;
646  int i, j ;
647  int found ;
648 
649  for (i=0 ; i<spl_len ; i++) {
650  /* Find (x1,y1) on the left of the current point */
651  found = 0 ;
652  for (j=0 ; j<(len-1) ; j++) {
653  if ((spl_x[i]>=x[j]) && (spl_x[i]<=x[j+1])) {
654  found++ ;
655  break ;
656  }
657  }
658  if (!found) {
659  spl_y[i] = 0.0;
660  } else {
661  a = ((double)y[j+1]-(double)y[j]) /
662  ((double)x[j+1]-(double)x[j]);
663  b = (double)y[j] - a * (double)x[j] ;
664  spl_y[i] = (pixelvalue)(a * (double)spl_x[i] + b) ;
665  }
666  }
667  return ;
668 }
669 
686 static int
687 function1d_search_value(
688  pixelvalue * x,
689  int len,
690  pixelvalue key,
691  int * foundPtr
692 )
693 {
694  int high,
695  low,
696  middle;
697 
698  low = 0;
699  high = len - 1;
700 
701  while (high >= low) {
702  middle = (high + low) / 2;
703  if (key > x[middle]) {
704  low = middle + 1;
705  } else if (key < x[middle]) {
706  high = middle - 1;
707  } else {
708  *foundPtr = 1;
709  return (middle);
710  }
711  }
712  *foundPtr = 0;
713  return (low);
714 }
715 
741 int
742 sinfo_function1d_natural_spline(
743  pixelvalue * x,
744  pixelvalue * y,
745  int len,
746  pixelvalue * splX,
747  pixelvalue * splY,
748  int splLen
749 )
750 {
751  int end;
752  int loc,
753  found;
754  register int i,
755  j,
756  n;
757  double * h; /* sinfo_vector of deltas in x */
758  double * alpha;
759  double * l,
760  * mu,
761  * z,
762  * a,
763  * b,
764  * c,
765  * d,
766  v;
767 
768  end = len - 1;
769 
770  a = cpl_malloc(sizeof(double) * splLen * 9) ;
771  b = a + len;
772  c = b + len;
773  d = c + len;
774  h = d + len;
775  l = h + len;
776  z = l + len;
777  mu = z + len;
778  alpha = mu + len;
779 
780  for (i = 0; i < len; i++) {
781  a[i] = (double)y[i];
782  }
783 
784  /* Calculate sinfo_vector of differences */
785  for (i = 0; i < end; i++) {
786  h[i] = (double)x[i + 1] - (double)x[i];
787  if (h[i] < 0.0) {
788  cpl_free(a) ;
789  return -1;
790  }
791  }
792 
793  /* Calculate alpha sinfo_vector */
794  for (n = 0, i = 1; i < end; i++, n++) {
795  /* n = i - 1 */
796  alpha[i] = 3.0 * ((a[i+1] / h[i]) - (a[i] / h[n]) - (a[i] / h[i]) +
797  (a[n] / h[n]));
798  }
799 
800  /* Vectors to solve the tridiagonal sinfo_matrix */
801  l[0] = l[end] = 1.0;
802  mu[0] = mu[end] = 0.0;
803  z[0] = z[end] = 0.0;
804  c[0] = c[end] = 0.0;
805 
806  /* Calculate the intermediate results */
807  for (n = 0, i = 1; i < end; i++, n++) {
808  /* n = i-1 */
809  l[i] = 2 * (h[i] + h[n]) - h[n] * mu[n];
810  mu[i] = h[i] / l[i];
811  z[i] = (alpha[i] - h[n] * z[n]) / l[i];
812  }
813  for (n = end, j = end - 1; j >= 0; j--, n--) {
814  /* n = j + 1 */
815  c[j] = z[j] - mu[j] * c[n];
816  b[j] = (a[n] - a[j]) / h[j] - h[j] * (c[n] + 2.0 * c[j]) / 3.0;
817  d[j] = (c[n] - c[j]) / (3.0 * h[j]);
818  }
819 
820  /* Now calculate the new values */
821  for (j = 0; j < splLen; j++) {
822  v = (double)splX[j];
823  splY[j] = (pixelvalue)0;
824 
825  /* Is it outside the interval? */
826  if ((v < (double)x[0]) || (v > (double)x[end])) {
827  continue;
828  }
829  /* Search for the interval containing v in the x sinfo_vector */
830  loc = function1d_search_value(x, len, (pixelvalue)v, &found);
831  if (found) {
832  splY[j] = y[loc];
833  } else {
834  loc--;
835  v -= (double)x[loc];
836  splY[j] = (pixelvalue)( a[loc] +
837  v * (b[loc] +
838  v * (c[loc] +
839  v * d[loc])));
840  }
841  }
842  cpl_free(a) ;
843  return 0;
844 }
845 
864 pixelvalue
865 sinfo_function1d_average_reject(
866  pixelvalue * line,
867  int npix,
868  int pix_low,
869  int pix_high)
870 {
871  pixelvalue * sorted ;
872  int i ;
873  double avg ;
874 
875  /* Sanity tests */
876  if ((line==NULL) || (npix<1)) return (pixelvalue)0 ;
877  if ((pix_low+pix_high)>=npix) return (pixelvalue)0 ;
878 
879  /* Copy input line and sort it */
880  sorted = cpl_malloc(npix * sizeof(pixelvalue)) ;
881  memcpy(sorted, line, npix * sizeof(pixelvalue)) ;
882  sinfo_pixel_qsort(sorted, npix);
883 
884  /* Find out average of remaining values */
885  avg = 0.00 ;
886  for (i=pix_low+1 ; i<(npix-pix_high) ; i++) {
887  avg += (double)sorted[i] ;
888  }
889  cpl_free(sorted);
890  avg /= (double)(npix - pix_high - pix_low) ;
891 
892  return (pixelvalue)avg ;
893 }
894 
920 #define STEP_MIN (-half_search)
921 #define STEP_MAX (half_search)
922 
923 double
924 sinfo_function1d_xcorrelate(
925  pixelvalue * line_i,
926  int width_i,
927  pixelvalue * line_t,
928  int width_t,
929  int half_search,
930  double * delta
931 )
932 {
933  double * xcorr ;
934  double xcorr_max ;
935  double mean_i, mean_t ;
936  double rms_i, rms_t ;
937  double sum, sqsum ;
938  double norm ;
939  int maxpos ;
940  int nsteps ;
941  int i ;
942  int step ;
943  int nval ;
944 
945 
946  /* Compute normalization factors */
947  sum = sqsum = 0.00 ;
948  for (i=0 ; i<width_i ; i++) {
949  sum += (double)line_i[i] ;
950  sqsum += (double)line_i[i] * (double)line_i[i];
951  }
952  mean_i = sum / (double)width_i ;
953  sqsum /= (double)width_i ;
954  rms_i = sqsum - mean_i*mean_i ;
955 
956  sum = sqsum = 0.00 ;
957  for (i=0 ; i<width_t ; i++) {
958  sum += (double)line_t[i] ;
959  sqsum += (double)line_t[i] * (double)line_t[i];
960  }
961  mean_t = sum / (double)width_t ;
962  sqsum /= (double)width_t ;
963  rms_t = sqsum - mean_t*mean_t ;
964 
965  norm = 1.00 / sqrt(rms_i * rms_t);
966 
967  nsteps = (STEP_MAX - STEP_MIN) +1 ;
968  xcorr = cpl_malloc(nsteps * sizeof(double));
969  for (step=STEP_MIN ; step<=STEP_MAX ; step++) {
970  xcorr[step-STEP_MIN] = 0.00 ;
971  nval = 0 ;
972  for (i=0 ; i<width_t ; i++) {
973  if ((i+step > 0) &&
974  (i+step < width_i)) {
975  xcorr[step-STEP_MIN] += ((double)line_t[i] - mean_t) *
976  ((double)line_i[i+step] - mean_i) *
977  norm ;
978  nval++ ;
979  }
980  }
981  xcorr[step-STEP_MIN] /= (double)nval ;
982  }
983  xcorr_max = xcorr[0] ;
984  maxpos = 0 ;
985  for (i=0 ; i<nsteps ; i++) {
986  if (xcorr[i]>xcorr_max) {
987  maxpos = i ;
988  xcorr_max = xcorr[i];
989  }
990  }
991  cpl_free(xcorr);
992  (*delta) = + ((double)STEP_MIN + (double)maxpos);
993  return xcorr_max ;
994 }