/*
 * 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
 */
/*************************************************************************
 * M.P.E. - SPIFFI project
 *
 *
 *
 * who       when      what
 * --------  --------  ----------------------------------------------
 * rabuter 2004-12-03 support one dimensional image in sinfo_shiftImage
 * schreib  23/05/00  created
 */

/************************************************************************
 *   NAME
 *        sinfo_image_ops.c -
 *
 *------------------------------------------------------------------------
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include "sinfo_vltPort.h"

/*
 * System Headers
 */
#include <errno.h>
/*
 * Local Headers
 */

#include "sinfo_image_ops.h"
#include "sinfo_error.h"
#include "sinfo_resampling.h"
#include "sinfo_local_types.h"
#include "sinfo_utils_wrappers.h"
/**@{*/
/**
 * @defgroup sinfo_image_ops Image operations
 *
 * image arithmetic routines
 */


static cpl_image *
sinfo_gen_lowpass(const int xs,
                  const int ys,
                  const double sigma_x,
                  const double sigma_y);


static void quicksort_int(int* data, int left, int right);
/**@{*/

/*----------------------------------------------------------------------------
 *                            Function codes
 *--------------------------------------------------------------------------*/



/**
@name sinfo_image_line_cor
@brief Corrects for line artifacts in long exposure images
@param width
@param filt_rad
@param kappa
@param ima
@param ima_out
@return cpl error code
 */

cpl_error_code
sinfo_image_line_corr(const int width,
                      const int filt_rad,
                      const int kappa,
                      cpl_image* ima_in,
                      cpl_image** ima_out)
{

    cpl_image* mask=NULL;

    cpl_image* ima_backpix=NULL;
    cpl_image* ima_backpos=NULL;
    cpl_image* ima_ybackpix=NULL;
    cpl_image* ima_diffbackpix=NULL;
    cpl_image* ima_filt=NULL;
    cpl_image* ima = NULL;

    cpl_matrix* filter=NULL;
    //cpl_mask* bpm_good=NULL; //Is this really useful?
    cpl_mask* bpm_bad=NULL;

    int sx=0;
    int sy=0;
    int i=0;
    int j=0;
    int k=0;
    double med_back=0;
    double sigma_back=0;
    double medvalue=0;

    float* pima=NULL;
    float* ppix=NULL;
    float* pmsk=NULL;
    int* ppos=NULL;
    int* pbackpix=NULL;
    cpl_binary* pbin=NULL;
    double tot=0;
    double mean=0;
    int* ybad=NULL;

    int nrow=0;
    int nbad=0;
    int yval=0;
    int yprev=0;


    check_nomsg(sx=cpl_image_get_size_x(ima_in));
    check_nomsg(sy=cpl_image_get_size_y(ima_in));
    check_nomsg(*ima_out=cpl_image_duplicate(ima_in));

    check_nomsg(mask=cpl_image_new(sx,sy,CPL_TYPE_FLOAT));
    check_nomsg(pmsk=cpl_image_get_data_float(mask));

    for(i=0;i<width;i++) {
        for(j=width;j<sy-width;j++) {
            pmsk[j*sx+i]=1;
        }
    }

    for(i=sx-width;i<sx;i++) {
        for(j=width;j<sy-width;j++) {
            pmsk[j*sx+i]=1;
        }
    }
    sinfo_free_image(&mask); //is mask needed?


    nrow=2*width*(sy-2*width);
    check_nomsg(ima_backpix=cpl_image_new(nrow,1,CPL_TYPE_FLOAT));
    check_nomsg(ima_backpos=cpl_image_new(nrow,1,CPL_TYPE_INT));

    check_nomsg(pima=cpl_image_get_data_float(ima_in));
    check_nomsg(ppix=cpl_image_get_data_float(ima_backpix));
    check_nomsg(ppos=cpl_image_get_data_int(ima_backpos));

    k=0;
    for(i=0;i<width;i++) {
        for(j=width;j<sy-width;j++) {
            ppix[k]=pima[j*sx+i];
            ppos[k]=j*sx+i;
            k++;
        }
    }

    for(i=sx-width;i<sx;i++) {
        for(j=width;j<sy-width;j++) {
            ppix[k]=pima[j*sx+i];
            ppos[k]=j*sx+i;
            k++;
        }
    }

    check_nomsg(ima_ybackpix=cpl_image_duplicate(ima_backpos));
    sinfo_free_image(&ima_backpos);

    check_nomsg(pbackpix=cpl_image_get_data_int(ima_ybackpix));

    check_nomsg(cpl_image_divide_scalar(ima_ybackpix,sx));
    check_nomsg(pbackpix=cpl_image_get_data_int(ima_ybackpix));


    check_nomsg(med_back=cpl_image_get_median(ima_backpix));
    check_nomsg(ima_diffbackpix=cpl_image_duplicate(ima_backpix));
    //sinfo_msg("med_back=%g",med_back);
    check_nomsg(cpl_image_subtract_scalar(ima_diffbackpix,med_back));
    //check_nomsg(cpl_image_save(ima_diffbackpix,"ima_diff.fits",
    //                           CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT));

    check_nomsg(filter=cpl_matrix_new(1,filt_rad));
    check_nomsg(cpl_matrix_fill(filter,1.));
    check_nomsg(ima_filt=sinfo_image_filter_median(ima_diffbackpix,filter));
    sinfoni_free_matrix(&filter);
    //check_nomsg(cpl_image_save(ima_filt,"ima_filt.fits",CPL_BPP_IEEE_FLOAT,
    //		     NULL,CPL_IO_DEFAULT));


    check_nomsg(sigma_back=cpl_image_get_stdev(ima_filt));
    sinfo_free_image(&ima_filt);

    check_nomsg(ima=cpl_image_duplicate(ima_diffbackpix));
    sinfo_free_image(&ima_diffbackpix);

    check_nomsg(cpl_image_abs(ima));
    //sinfo_msg("sigma_back=%g",sigma_back);

    //find good pixels
    check_nomsg(bpm_bad=cpl_mask_threshold_image_create(ima,kappa*sigma_back,
                    SINFO_DBL_MAX));
    /*check_nomsg(bpm_good=cpl_mask_threshold_image_create(ima,SINFO_DBL_MIN,
						       kappa*sigma_back));
  sinfo_free_mask(&bpm_good);
     */
    check_nomsg(cpl_image_reject_from_mask(ima_backpix,bpm_bad));
    check_nomsg(medvalue=cpl_image_get_median(ima_backpix));
    //sinfo_msg("medvalue=%g",sigma_back);
    check_nomsg(nbad=cpl_mask_count(bpm_bad));
    //sinfo_msg("nbad=%d",nbad);
    check_nomsg(cpl_image_reject_from_mask(ima_backpix,bpm_bad));
    sinfo_free_image(&ima_backpix);

    yprev=-1;

    check_nomsg(pbin=cpl_mask_get_data(bpm_bad));
    check_nomsg(pbackpix=cpl_image_get_data_int(ima_ybackpix));
    cpl_msg_debug(cpl_func, "%d lines detected", nbad);
    if(nbad)
    {

        ybad = cpl_calloc(nbad,sizeof(int));
        k=0;

        for(i=0;i<nrow;i++) {
            if(pbin[i] == CPL_BINARY_1) {
                ybad[k]=pbackpix[i] + 1;
                k++;
            }
        }
        sinfo_free_mask(&bpm_bad);
        sinfo_free_image(&ima_ybackpix);

        quicksort_int(&(ybad[0]), 0, nbad-1);
        yprev=-1;
        for(k=0;k<nbad;k++) {
            yval=ybad[k];
            if(yval == yprev) {
                sinfo_msg_debug("skyp %d",yval);
            }
            else {
                yprev=yval;
                sinfo_msg_debug("correct raw %d",yval);
                check_nomsg(tot=cpl_image_get_flux_window(ima_in,1,yval,width,yval));
                check_nomsg(tot+=cpl_image_get_flux_window(ima_in,sx-width+1,
                                yval,sx,yval));
                mean=tot/(2. * width);
                check_nomsg(pima=cpl_image_get_data_float(*ima_out));
                for(i=width;i<sx-width;i++) {
                    pima[i+(yval-1)*sx]+=(float)(mean-medvalue);
                }

            }
        }
    }


    cleanup:

    sinfo_free_image(&mask); //is mask needed?
    sinfo_free_image(&ima_backpos);
    sinfoni_free_matrix(&filter);
    sinfo_free_image(&ima_filt);
    sinfo_free_image(&ima_diffbackpix);
    //  sinfo_free_mask(&bpm_good);
    sinfo_free_image(&ima_backpix);
    sinfo_free_mask(&bpm_bad);
    sinfo_free_image(&ima_ybackpix);
    cpl_image_delete(ima);
    cpl_free(ybad);
    return cpl_error_get_code();

}


/**
@name sinfo_new_my_median_image
@brief Computes the median of an image
@param im image
@return clean median
@note The median is computed rejecting NANs
 */

double sinfo_new_my_median_image(cpl_image* im)
{
    double m=0;
    register int i=0;
    int n=0;
    pixelvalue* pv=0;
    int ilx=0;
    int ily=0;
    float* pidata=NULL;


    if(im==NULL) sinfo_msg_error("Null Image");
    ilx=cpl_image_get_size_x(im);
    ily=cpl_image_get_size_y(im);
    pidata=cpl_image_get_data_float(im);

    for ( i = 0 ; i < (int) ilx*ily ; i++ )
    {
        if ( isnan(pidata[i]) )
        {

        } else {
            n++;
        }
    }
    pv = cpl_calloc(n,sizeof(pixelvalue));
    n=0;
    for ( i = 0 ; i < (int) ilx*ily ; i++ )
    {
        if ( isnan(pidata[i]) )
        {

        } else {
            pv[n]=pidata[i];
            n++;
        }
    }
    if(pv == NULL || n == 0) {
        m=0;
    } else {
        m=sinfo_new_median(pv,n);
    }
    cpl_free(pv);
    if(isnan(m)){
        m=0;
    }
    return m;
}

/**
@name sinfo_new_mean_of_columns
@brief   takes the average of each image column
 @param image
 @return resulting row array
 @note NANs are not considered

TODO: not used
 */

Vector * sinfo_new_mean_of_columns( cpl_image *im )
{
    Vector * row=NULL ;
    int i=0;
    int j=0;
    int ilx=0;
    int ily=0;
    float* pidata=NULL;

    if ( im == NULL )
    {
        sinfo_msg_error ("null image") ;
        return NullVector ;
    }
    ilx=cpl_image_get_size_x(im);
    ily=cpl_image_get_size_y(im);
    pidata=cpl_image_get_data_float(im);

    /* allocate memory for a row with the length of the image x-axis */
    if ( NULL == (row = sinfo_new_vector (ilx)) )
    {
        sinfo_msg_error ("not able to allocate a sinfo_vector" ) ;
        return NullVector ;
    }

    for ( i = 0 ; i < ilx ; i++ )
    {
        for ( j = 0 ; j < ily ; j++ )
        {
            if (!isnan(pidata[i+j*ilx]))
            {
                row->data[i] += pidata[i + j*(ilx)] ;
            }
        }

        row->data[i] /= ily ;
    }
    return row ;
}
/**
@name sinfo_new_clean_mean_of_columns
@brief computes the clean mean of image columns

  @param image , percentage of lowest and highest values to reject
  @return resulting row image
  @doc
  takes the average of each image column by sorting the column values and
  rejecting the given percentage of the highest and lowest values  [0...1]

TODO: not used
 */

cpl_image * sinfo_new_clean_mean_of_columns( cpl_image *im,
                                             float lo_reject,
                                             float hi_reject)
{
    cpl_image     * row=NULL ;
    pixelvalue*   buffer=NULL ;
    int          i=0;
    int          j=0;
    int          k=0;

    int          lo_n=0;
    int          hi_n=0;
    int ilx=0;
    int ily=0;
    float* pidata=NULL;
    float* podata=NULL;

    if ( im == NULL )
    {
        sinfo_msg_error ("null image") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(im);
    ily=cpl_image_get_size_y(im);
    pidata=cpl_image_get_data_float(im);

    if ((lo_reject + hi_reject) > 0.9)
    {
        sinfo_msg_error("illegal rejection thresholds: [%f] and [%f]",
                        lo_reject, hi_reject) ;
        sinfo_msg_error("threshold sum should not be over "
                        "0.90 aborting average") ;
        return NULL ;
    }

    lo_n = (int) (ily * lo_reject + 0.5) ;
    hi_n = (int) (ily * hi_reject + 0.5) ;
    if (lo_n + hi_n >= ily)
    {
        sinfo_msg_error ("everything would be rejected") ;
        return NULL ;
    }

    /* allocate memory for a row with the length of the image x-axis */
    if ( NULL == (row = cpl_image_new (ilx, 1,CPL_TYPE_FLOAT)) )
    {
        sinfo_msg_error ("cannot allocate new image") ;
        return NULL ;
    }
    podata=cpl_image_get_data_float(row);

    buffer=(pixelvalue*) cpl_calloc(ily,sizeof(pixelvalue)) ;

    for ( i = 0 ; i < ilx ; i++ )
    {
        for ( j = 0 ; j < ily ; j++ )
        {
            buffer[j] = pidata[i + j*(ilx)] ;
        }
        sinfo_pixel_qsort (buffer, ily) ;

        int nv = 0 ;
        for (k = lo_n ; k < ily - hi_n ; k ++)
        {
            if ( !isnan(buffer[k]) )
            {
                podata[i] += buffer[k] ;
                nv ++ ;
            }
        }
        podata[i] /= nv ;

    }
    cpl_free(buffer);
    return row ;
}


/**
@name sinfo_new_div_image_by_row
@brief divides each image column by a row value

   @param image, row array
   @return resulting image
   @note NANs values are not considered
TODO: not used
 */

cpl_image * sinfo_new_div_image_by_row( cpl_image *im, Vector *row )
{
    cpl_image *image=NULL ;
    int         i=0;
    int         j=0;
    int ilx=0;
    int ily=0;
    float* pidata=NULL;
    float* podata=NULL;

    if ( im == NULL || row == NULL )
    {
        sinfo_msg_error ("null image or null row") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(im);
    ily=cpl_image_get_size_y(im);
    pidata=cpl_image_get_data_float(im);

    if ( ilx != row -> n_elements )
    {
        sinfo_msg_error("image and row size not compatible") ;
        return NULL ;
    }

    if ( NULL == (image = cpl_image_duplicate (im)) )
    {
        sinfo_msg_error ("cannot copy image") ;
        return NULL ;
    }
    podata=cpl_image_get_data_float(image);

    for (i = 0 ; i < ilx ; i++ )
    {
        for (j = 0 ; j < ily ; j++)
        {
            if ( !isnan(pidata[i + j*(ilx)]) )
            {
                podata[i + j*(ilx)] = pidata[i + j*(ilx)] / row -> data[i] ;
            }
        }
    }
    return image ;
}


/**
@name sinfo_new_mult_row_to_image
@brief multiplies each image column with a row value

   @param image, row array
   @return resulting image
   @note NANs values are not considered
TODO: not used
 */

cpl_image * sinfo_new_mult_row_to_image( cpl_image *im, Vector *row )
{
    cpl_image *image=NULL;
    int         i=0;
    int         j=0;
    int ilx=0;
    int ily=0;
    float* pidata=NULL;
    float* podata=NULL;




    if ( im == NULL || row == NULL )
    {
        sinfo_msg_error ("null image or null row") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(im);
    ily=cpl_image_get_size_y(im);
    pidata=cpl_image_get_data_float(im);

    if ( ilx != row -> n_elements )
    {
        sinfo_msg_error("image and row size not compatible") ;
        return NULL ;
    }

    if ( NULL == (image = cpl_image_duplicate (im)) )
    {
        sinfo_msg_error ("cannot copy image") ;
        return NULL ;
    }
    podata=cpl_image_get_data_float(image);

    for (i = 0 ; i < ilx ; i++ )
    {
        for (j = 0 ; j < ily ; j++)
        {
            if ( !isnan(pidata[i + j*(ilx)]) )
            {
                podata[i + j*(ilx)] = pidata[i + j*(ilx)] * row -> data[i] ;
            }
        }
    }
    return image ;
}






/**
@brief
   @name sinfo_new_col_tilt

   @param image , factor of sigma noise limit to determine
                        pixels that are used for the fit.
   @return image


   @doc         : first calculates statistics for each column of an image.
                  median value and standard deviation of columns are determined,
                  blank values are excluded. Fits a straight
                  line through the pixel values of each column and subtracts
                  the fit in order to remove the tilt of each column.
                  Only those pixels are used for the fit that are within
                  a defined factor of sigma noise limit. The noise is
                  calculated from pixels between the 10percentil and
                  90 percentil points.
                  if the straight line could not be determined, the median
                  of the column is subtracted from the column

   @note       :  works only for raw or averaged raw images

 */

cpl_image * sinfo_new_col_tilt ( cpl_image * image, float sigmaFactor )
{
    cpl_image   * im=NULL;
    float      * column=NULL ;


    double  mean=0;
    float   sinfo_median=0;
    float   noise=0 ;


    float        a=0;
    float        b=0;
    float        siga=0;
    float        sigb=0;
    float        chi2=0;
    float        q=0;
    int          i=0;
    int          j=0;


    int         mwt=0 ;
    int lx=0;
    int ly=0;
    float* p_in_data=NULL;
    float* p_ou_data=NULL;


    if ( image == NULL )
    {
        sinfo_msg_error ("no image given" ) ;
        return NULL ;
    }

    if ( sigmaFactor <= 0. )
    {
        sinfo_msg_error ("no or negative sigma factor") ;
        return NULL ;
    }
    lx = cpl_image_get_size_x(image);
    ly = cpl_image_get_size_y(image);


    /* allocate memory */
    if ( NULL == (im = cpl_image_new (lx,ly,CPL_TYPE_FLOAT )) )
    {
        sinfo_msg_error ("cannot allocate new image" ) ;
        return NULL ;
    }

    /* go through the columns */
    p_in_data = cpl_image_get_data_float(image);
    p_ou_data = cpl_image_get_data_float(im);
    for ( i = 0 ; i < lx ; i ++ )
    {
        /* initialize the buffer variables for each column */
        int colnum = 0 ;
        column = (float *) cpl_calloc ( ly , sizeof (float *) ) ;
        float* sig    = (float *) cpl_calloc ( ly , sizeof (float *) ) ;
        float* dat    = (float *) cpl_calloc ( ly , sizeof (float *) ) ;

        /*select only non-ZERO values of one column*/
        for ( j = 0 ; j < ly ; j++ )
        {
            if ( !isnan(p_in_data[i + j*lx]) )
            {
                column[j] = p_in_data[i + j*lx] ;
                colnum ++ ;
            }
        }
        if ( colnum < 10 )
        {
            /*sinfo_msg_warning ("sinfo_new_col_tilt:",
          "column %d has almost only blank pixels and is set to blank", i+1) ;*/
            for ( j = 0 ; j < ly ; j++ )
            {
                p_ou_data[i + j*lx] = ZERO;
            }
            /*
            cpl_free (column) ;
            cpl_free (sig);
            cpl_free (dat) ;
            continue ;
             */
        }

        /*-------------------------------------------------------------------
         * sort the data, clip off the extremes, determine the noise
         * and get the range for the valid data. It is assumed here
         * that most pixels are o.k.
         */

        sinfo_pixel_qsort (column, colnum) ;

        double sum   = 0. ;
        double sum2  = 0. ;
        int npix  = 0  ;

        for ( j = 0.1*colnum + 1 ; j <= 0.9*colnum ; j++ )
        {
            sum  += column[j] ;
            sum2 += column[j] * column[j] ;
            npix ++ ;
        }

        if (npix <= 1)
        {
            noise = sigmaFactor * 1000.;
        }
        else
        {
            mean   = sum/(float)npix ;
            noise  = sqrt( (sum2 - sum*mean)/(double)(npix -1) ) ;
            noise *= sigmaFactor ;
        }

        /* -------------------------------------------------------------
         * determine sinfo_median if colnum is odd, sinfo_median will be the
           colnum/2 th value, otherwise
         * sinfo_median is the mean of colnum/2-1 th and colnum/2 th value.
         */

        if ( colnum % 2 == 1 )
        {
            sinfo_median = column[colnum/2] ;
        }
        else
        {
            sinfo_median = (column[colnum/2 - 1] + column[colnum/2])/2. ;
        }

        /* now select the pixels for the tilt calculation */

        colnum = 0 ;
        for ( j = 0; j < ly ; j ++ )
        {
            if ( !isnan(p_in_data[i+j*lx]) &&
                            fabs ( (p_in_data[i+j*lx]) - sinfo_median) <= noise )
            {
                column[colnum] = p_in_data[i+j*lx] ;
                dat[colnum] = (float) j ;
                sig[colnum] = 1. ;
                colnum ++ ;
            }
        }

        if ( colnum == 0 )
        {
            /*for ( j = 0; j < ly; j++ )
            {
                p_ou_data[i+j*lx] -= sinfo_median ;
            }
            cpl_free (column) ;
            cpl_free (sig)    ;
            cpl_free (dat)    ;
            continue ;*/
            a=0./0.;
            b=0./0.;
        }
        else
        {
            mwt = 0 ;
            sinfo_my_fit ( dat, column, colnum, sig, mwt, &a,
                           &b, &siga, &sigb, &chi2, &q ) ;
        }
        if ( fabs(b) >= SLOPE || fabs(a) >= SATURATION  ||
                        isnan(b) || isnan(a))
        {
            sinfo_msg_warning ("linear fit: slope is greater than limit: %f"
                            " saturation level is reached: %f in column"
                            " number %d ", b, a , i+1) ;
        }

        /* subtract fit or sinfo_median from data */
        for ( j = 0; j < ly; j++ )
        {
            if ( !isnan(p_in_data[i+j*lx]) &&
                            fabs(b) < SLOPE && fabs(a) < SATURATION )
            {
                p_ou_data[i+j*lx] = p_in_data[i+j*lx] - (a + b * (float)j) ;
            }
            else if ( isnan(p_in_data[i+j*lx]) )
            {
                p_ou_data[i+j*lx] = ZERO ;
            }
            else if ( (fabs(b) >= SLOPE ||
                            fabs(a) >= SATURATION || isnan(a) || isnan(b)) &&
                            !isnan(p_in_data[i+j*lx]) )
            {
                p_ou_data[i+j*lx] -= sinfo_median ;
            }
            else
            {
                sinfo_msg_error (" case is not possible! %f %f", b,a) ;
                /*cpl_free (column) ;
                cpl_free (sig)    ;
                cpl_free (dat)    ;
                cpl_image_delete(im) ;
                return NULL ;*/
            }
        }
        cpl_free (column) ;
        cpl_free (sig)    ;
        cpl_free (dat)    ;
    }

    return im     ;
}





/**
   @name sinfo_new_median_image
   @brief median filter
   @param image, a sinfo_median threshold parameter
   @return resulting image
   @doc
   median filter, calculates the sinfo_median for an image
   by using the 8 closest pixels of every pixel.
   The values in the output image are determined according
   to the values of the input parameter.
   If fmedian = 0: always replace by sinfo_median
   if fmedian < 0: replace by sinfo_median if |pixel - median| > -fmedian
   if fmedian > 0: replace by sinfo_median (fmedian as a factor of
                   the square root of the median itself)
   if |pixel - median| >= fmedian * sqrt ( median )
   This can be used to consider photon noise.
   This considers a dependence of the differences on the pixel values
   themselves.
   @note it is assumed that most of the 8 nearest neighbor pixels
                        are not bad pixels!
                        blank pixels are not replaced!

 */

cpl_image * sinfo_new_median_image( cpl_image * im, float fmedian )
{
    cpl_image *   image=NULL       ;
    pixelvalue * value=NULL       ;
    pixelvalue   sinfo_median=0      ;
    int        * position=NULL    ;
    int          nposition=0   ;
    int          n=0;
    int          i=0;
    int          j=0;
    int lx=0;
    int ly=0;
    float* p_in_data=NULL;
    float* p_ou_data=NULL;
    int im_size=0;
    if ( im == NULL )
    {
        sinfo_msg_error ("no image input") ;
        return NULL ;
    }

    image = cpl_image_duplicate ( im ) ;
    lx=cpl_image_get_size_x(im);
    ly=cpl_image_get_size_y(im);
    im_size=lx*ly;
    p_in_data=cpl_image_get_data_float(im);
    p_ou_data=cpl_image_get_data_float(image);

    /*----------------------------------------------------------------------
     * go through all pixels
     */

    for ( i = 0 ; i < im_size ; i++ )
    {
        /* blank pixels are not replaced */
        if ( isnan(p_in_data[i]) )
        {
            continue ;
        }

        /* initialize the buffer variables for the 8 nearest neighbors */
        value = (pixelvalue * )cpl_calloc ( 8, sizeof ( pixelvalue * ) ) ;
        position = ( int * ) cpl_calloc ( 8, sizeof ( int * ) ) ;

        /*--------------------------------------------------------------------
         * determine the pixel position of the 8 nearest neighbors
         */

        position[0] = i + lx - 1 ; /* upper left  */
        position[1] = i + lx     ; /* upper       */
        position[2] = i + lx + 1 ; /* upper right */
        position[3] = i + 1      ; /* right       */
        position[4] = i - lx + 1 ; /* lower right */
        position[5] = i - lx     ; /* lower       */
        position[6] = i - lx - 1 ; /* lower left  */
        position[7] = i - 1      ; /* left        */

        /*-------------------------------------------------------------------
         * determine the positions of the image margins, top positions are
           changed to low positions and vice versa. Right positions are
           changed to left positions and vice versa.
         */

        if ( i >= 0 && i < lx )    /* bottom line */
        {
            position[4] += 2 * lx ;
            position[5] += 2 * lx ;
            position[6] += 2 * lx ;
        }
        else if ( i >= ((int) lx*ly - lx ) && i < (int) lx*ly ) /* top line */
        {
            position[0] -= 2 * lx ;
            position[1] -= 2 * lx ;
            position[2] -= 2 * lx ;
        }
        else if ( i % lx == 0 )    /* left side */
        {
            position[0] += 2 ;
            position[6] += 2 ;
            position[7] += 2 ;
        }
        else if ( i % lx == lx - 1 )    /* right side */
        {
            position[2] -= 2 ;
            position[3] -= 2 ;
            position[4] -= 2 ;
        }

        /* --------------------------------------------------------------------
         * read the pixel values of the neighboring pixels,
         * blanks are not considered
         */

        nposition = 8 ;
        n = 0 ;
        for ( j = 0 ; j < nposition ; j ++ )
        {
            if((position[j] >-1 ) && (position[j]<im_size)) {
                if ( !isnan(p_in_data[position[j]]) )
                {
                    value[n] = p_in_data[position[j]] ;
                    n ++ ;
                }
            }
        }
        nposition = n ;

        if ( nposition <= 1 )  /* almost all neighbors are blank */
        {
            p_ou_data[i] = ZERO ;
            cpl_free(value) ;
            cpl_free(position) ;
            continue ;
        }

        /* sort the values and determine the sinfo_median */

        sinfo_pixel_qsort ( value, nposition ) ;
        if ( nposition % 2 == 1 )
        {
            sinfo_median = value [ nposition/2 ] ;
        }
        else
        {
            sinfo_median = ( value [nposition/2 - 1] +
                            value [nposition/2] ) / 2. ;
        }

        /* -----------------------------------------------------------------
         * replace the pixel value by the sinfo_median on conditions:
         * fmedian = 0: always replace with sinfo_median.
         * fmedian < 0: interpret as absolute condition:
         *              if |pixel - sinfo_median| > -fmedian
         *              replace with sinfo_median.
         * fmedian > 0: replace by sinfo_median (fmedian as a factor of
         *              the square root of the sinfo_median itself)
         *              if |pixel - sinfo_median| >= fmedian *
                                                     sqrt ( sinfo_median )
         *              considers a dependence on the pixel value.
         *              This can be used to consider photon noise.
         */

        if ( fmedian == 0 )
        {
            p_ou_data[i] = sinfo_median ;
        }
        else if ( fmedian < 0 &&
                        fabs ( sinfo_median - p_in_data[i] ) >= -fmedian )
        {
            p_ou_data[i] = sinfo_median ;
        }
        else if ( fmedian > 0 &&
                        fabs ( sinfo_median - p_in_data[i] ) >= fmedian *
                        sqrt(fabs(sinfo_median)) )
        {
            p_ou_data[i] = sinfo_median ;
        }
        else
        {
            cpl_free (value) ;
            cpl_free (position) ;
            continue ;
        }

        cpl_free (value) ;
        cpl_free (position) ;
    }
    return image ;
}




/**
   @name sinfo_new_compare_images
   @brief if a pixel value of one image (im1) equals the pixel value
          of the other image keep the pixel value of the original image
          otherwise replace it with ZEROs
   @param sinfo_new_compare_images()
   @param two images to be compared and the original image
   @return resulting image

 */

cpl_image *
sinfo_new_compare_images(cpl_image * im1,cpl_image * im2,cpl_image * origim )
{
    cpl_image * image=NULL ;
    int            i=0 ;
    int lx1=0;
    int ly1=0;
    int lx2=0;
    int ly2=0;
    float* p_in1_data=NULL;
    float* p_in2_data=NULL;
    float* p_ou_data=NULL;
    float* p_org_data=NULL;


    if ( im1 == NULL || im2 == NULL || origim == NULL )
    {
        sinfo_msg_error ("Null images as input" ) ;
        return NULL ;
    }
    lx1=cpl_image_get_size_x(im1);
    ly1=cpl_image_get_size_y(im1);

    lx2=cpl_image_get_size_x(im2);
    ly2=cpl_image_get_size_y(im2);

    p_in1_data=cpl_image_get_data_float(im1);
    p_in2_data=cpl_image_get_data_float(im2);
    p_org_data=cpl_image_get_data_float(origim);
    if ( lx1 != lx2 || ly1 != ly2 )
    {
        sinfo_msg_error ("incompatible image sizes" ) ;
        return NULL ;
    }

    /* allocate memory */
    if ( NULL == (image = cpl_image_new ( lx1, ly1, CPL_TYPE_FLOAT )) )
    {
        sinfo_msg_error ("cannot allocate new image" ) ;
        return NULL ;
    }
    p_ou_data=cpl_image_get_data_float(image);
    for ( i = 0 ; i < (int) lx1*ly1 ; i ++ )
    {
        if ( isnan(p_in1_data[i]) && isnan(p_in2_data[i]) )
        {
            p_ou_data[i] = ZERO ;
        }
        else
        {
            if ( p_in1_data[i] == p_in2_data[i] )
            {
                p_ou_data[i] = p_org_data[i] ;
            }
            else
            {
                p_ou_data[i] = ZERO ;
            }
        }
    }
    return image ;
}



/**
  @name info_new_promote_image_to_mask
  @brief changes an image with ZERO indicated bad pixels to a bad pixel mask
         image, that means the returned image has values 1 at positions of
         good pixels and ZEROs at positions of bad pixels.
  @name sinfo_new_promote_image_to_mask()
  @param image with ZERO indicating bad pixels
  @return resulting mask image that means 1 for good pixels and 0 for bad pixel
          positions n_badpixels: number of bad pixels

 */

cpl_image *
sinfo_new_promote_image_to_mask (cpl_image * im, int * n_badpixels )
{
    cpl_image * reImage=NULL ;
    int        i=0 ;
    int lx=0;
    int ly=0;
    float* p_in_data=NULL;
    float* p_ou_data=NULL;

    if ( NULL == im )
    {
        sinfo_msg_error("no input image given!") ;
        return NULL ;
    }
    lx=cpl_image_get_size_x(im);
    ly=cpl_image_get_size_y(im);
    p_in_data=cpl_image_get_data_float(im);

    /* allocate memory for the returned image */
    if ( NULL == (reImage = cpl_image_new (lx,ly,CPL_TYPE_FLOAT )) )
    {
        sinfo_msg_error ("cannot allocate new image!") ;
        return NULL ;
    }
    p_ou_data=cpl_image_get_data_float(reImage);

    *n_badpixels = 0 ;
    for ( i = 0 ; i < (int) lx*ly ; i ++ )
    {
        if ( isnan(p_in_data[i]) )
        {
            p_ou_data[i] = 0. ;
            (*n_badpixels)++ ;
        }
        else
        {
            p_ou_data[i] = 1. ;
        }
    }
    return reImage ;
}


/**
 @name sinfo_new_mult_image_by_mask
 @brief changes an image to an image that has ZERO indicated static bad pixels
 @name        sinfo_new_mult_image_by_mask()
 @param im:   input image
 @param mask: mask image
 @return resulting image that means the input image with marked
                        static bad pixels (ZERO values)

 */

cpl_image * sinfo_new_mult_image_by_mask (cpl_image * im,cpl_image * mask )
{
    cpl_image * reImage=NULL ;
    int        i=0 ;
    int ix=0;
    int iy=0;
    int mx=0;
    int my=0;


    float* pmdata=NULL;
    float* podata=NULL;

    if ( NULL == im )
    {
        sinfo_msg_error("no input image given!") ;
        return NULL ;
    }
    if ( NULL == mask )
    {
        sinfo_msg_error("no mask image given!") ;
        return NULL ;
    }
    ix=cpl_image_get_size_x(im);
    iy=cpl_image_get_size_y(im);
    mx=cpl_image_get_size_x(mask);
    my=cpl_image_get_size_y(mask);

    if ( ix != mx || iy != my)
    {
        sinfo_msg_error("image sizes are not correspondent!") ;
        return NULL ;
    }

    reImage = cpl_image_duplicate( im ) ;
    podata=cpl_image_get_data_float(reImage);
    pmdata=cpl_image_get_data_float(mask);

    for ( i = 0 ; i < (int) ix*iy ; i ++ )
    {
        if ( pmdata[i] == 0. )
        {
            podata[i] = ZERO ;
        }
    }

    return reImage ;
}



/**
   @name sinfo_new_thresh_image
   @brief simple search for static bad pixels for a flat field or dark frame,
          values below and above the threshold values are set to ZERO.

   @param image, low cut pixel value, high cut pixel value
   @return resulting image

 */

cpl_image *
sinfo_new_thresh_image (cpl_image * im, float lo_cut, float hi_cut )
{
    cpl_image * image=NULL ;
    float* p_inp_data=NULL;
    float* p_out_data=NULL;
    int lx=0;
    int ly=0;

    int            i=0 ;

    if (im == NULL)
    {
        sinfo_msg_error ("null image given") ;
        return NULL ;
    }
    lx=cpl_image_get_size_x(im);
    ly=cpl_image_get_size_y(im);

    image = cpl_image_duplicate(im) ;
    p_inp_data=cpl_image_get_data(im);
    p_out_data=cpl_image_get_data(image);
    for ( i = 0 ; i < (int) lx*ly ; i ++ )
    {
        if ( p_inp_data[i] > (pixelvalue) hi_cut ||
                        p_inp_data[i] < (pixelvalue) lo_cut )
        {
            p_out_data[i] = ZERO ;
        }
    }
    return image ;
}




/**
 @brief interpolates all bad pixels indicated by the bad pixel mask.
 @name   :       sinfo_new_interpol_image()
 @param im: raw image
 @param mask: bad pixel mask
 @param max_radius: maximum x and y distance in pixels from the
                    bad pixel within which valid pixels for
                    interpolation are searched.
 @param n_pixels:   minimal number of pixels with which the bad
                    pixel is interpolated if not enough
                    valid nearest neighbors are found.
 @return resulting interpolated image without any ZEROS
 @doc
 interpolates all bad pixels indicated by the bad pixel mask.
 Therefore, the mean of at least 2 valid values of the nearest 8 neighbours
 is taken. If too much neighbours are also bad pixels
 the neighbour radius is increased to a maximum of
 max_radius until n_pixels valid pixels are found.
 The valid neighbours are searched by going through
 the columns and rows around the central square that was already searched.
 The bad pixel is interpolated by the mean of these valid pixels (less than 9)
 or by the sinfo_median of them (more than 8).

TODO: not used
 */

cpl_image * sinfo_new_interpol_image ( cpl_image * im,
                                       cpl_image * mask,
                                       int        max_radius,
                                       int        n_pixels )
{
    cpl_image * returnImage=NULL ;
    float* neighbors=NULL ;
    float sum=0;
    float mean=0;
    int i=0;
    int j=0;
    int k=0;
    int row=0;
    int col=0;
    int n_valid=0;
    int agreed=0;

    int ilx=0;
    int ily=0;
    int mlx=0;
    int mly=0;
    float* pidata=NULL;
    float* podata=NULL;
    float* pmdata=NULL;

    if ( NULL == im )
    {
        sinfo_msg_error("sorry, no input image given!") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(im);
    ily=cpl_image_get_size_y(im);
    pidata=cpl_image_get_data_float(im);

    if ( NULL == mask )
    {
        sinfo_msg_error("sorry, no mask image given!") ;
        return NULL ;
    }

    mlx=cpl_image_get_size_x(mask);
    mly=cpl_image_get_size_y(mask);
    pmdata=cpl_image_get_data_float(mask);

    if ( mlx != ilx || mly != ily )
    {
        sinfo_msg_error("images not compatible !") ;
        return NULL ;
    }

    if ( max_radius <= 0 )
    {
        sinfo_msg_error("wrong number of pixels for maximal "
                        "search radius given!") ;
        return NULL ;
    }

    if ( n_pixels <= 2 )
    {
        sinfo_msg_error("wrong number of pixels used "
                        "for interpolation given!") ;
        return NULL ;
    }

    returnImage = cpl_image_duplicate ( im ) ;
    podata=cpl_image_get_data_float(returnImage);

    /* go through the columns and rows of the input and mask image */
    neighbors=cpl_calloc(4*max_radius*max_radius,sizeof(float)) ;

    for ( col = 0 ; col < ilx ; col++ )
    {
        for ( row = 0 ; row < ily ; row++ )
        {
            /* look for the ZEROS that means the detected bad pixels */
            if ( isnan(pmdata[col+row*ilx]) || pmdata[col+row*ilx] == 0. )
            {
                /* now the neighbors must be considered */
                n_valid = 0 ;
                agreed  = 0 ;
                for ( j = 1 ; j <= max_radius ; j++ )
                {

                    /* go through the left column */
                    for ( k = -j ; k < j ; k++ )
                    {
                        if ( col-j >= 0 && row+k < ily && row+k >= 0 )
                        {
                            if ( !isnan(pmdata[col-j+(row+k)*mlx]) ||
                                            pmdata[col-j+(row+k)*mlx] != 0 )
                            {
                                neighbors[n_valid]=pidata[col-j+(row+k)*ilx] ;
                                n_valid++ ;
                            }
                        }
                    }

                    /* go through the upper row */
                    for ( k = -j ; k < j ; k++ )
                    {
                        if ( col+k < ilx && col+k >= 0 && row+j < ily )
                        {
                            if ( !isnan(pmdata[col+k+(row+j)*mlx]) ||
                                            pmdata[col+k+(row+j)*mlx] != 0. )
                            {
                                neighbors[n_valid]=pidata[col+k+(row+j)*ilx] ;
                                n_valid++ ;
                            }
                        }
                    }

                    /* go through the right column */
                    for ( k = -j ; k < j ; k++ )
                    {
                        if ( col+j < ilx  && row-k >= 0 && row-k < ily )
                        {
                            if ( !isnan(pmdata[col+j+(row-k)*mlx]) ||
                                            pmdata[col+j+(row-k)*mlx] != 0. )
                            {
                                neighbors[n_valid]=pidata[col+j+(row-k)*ilx] ;
                                n_valid++ ;
                            }
                        }
                    }

                    /* go through the lower row */
                    for ( k = -j ; k < j ; k++ )
                    {
                        if ( col-k >= 0 && col-k < ilx && row-j < ily )
                        {
                            if ( !isnan(pmdata[col-k+(row-j)*mlx]) ||
                                            pmdata[col-k+(row-j)*mlx] != 0. )
                            {
                                neighbors[n_valid]=pidata[col-k+(row-j)*ilx] ;
                                n_valid++ ;
                            }
                        }
                    }

                    /* control if the breaking criteria is fullfilled */
                    if ( n_valid >= n_pixels )
                    {
                        agreed = 1 ;
                        break ;
                    }
                    /* do a break if more than 2 nearest neighbors are found */
                    if ( j == 1 && n_valid >= 2 )
                    {
                        agreed = 1 ;
                        break ;
                    }
                }
                if ( n_valid < n_pixels && agreed == 0 )
                {
                    sinfo_msg_error("not enough valid neighbors found to "
                                    "interpolate bad pixel in col: "
                                    "%d, row: %d", col, row ) ;
                    return NULL ;
                }
                else
                {
                    /* ------------------------------------------------------
                     * take the mean of the valid neighboring pixels if less
                     * than 9 valid pixels are available else take the
                       sinfo_median.
                     */
                    if ( n_valid <= 8 )
                    {
                        sum = 0. ;

                        for ( i = 0 ; i < n_valid ; i++ )
                        {
                            sum += neighbors[i] ;
                        }
                        mean = sum / n_valid ;

                        podata[col+row*ilx] = mean ;
                    }
                    else
                    {
                        podata[col+row*ilx]=sinfo_new_median(neighbors,n_valid);
                    }
                }
            }
        }
    }
    cpl_free(neighbors);
    return returnImage ;
}


/**
@brief
 @name     :       sinfo_interpol_source_image()
 @param im:         source raw image
 @param mask:       bad pixel mask
 @param max_rad:    maximum pixel distance from the bad pixel to
                    interpolate where
                    valid pixel values are searched for.
 @param slit_edges: array of the edges of the slitlets.
 @return resulting interpolated image hopefully without any ZEROS

 @doc
 interpolates all bad pixels indicated by the bad pixel mask.
 Therefore, the mean of the nearest 4 neighbours is taken,
 two in spectral direction and 2 in spatial direction.
 The routine cares about the image and slitlet edges.
 If there are no good pixel found within the nearest neighbours,
 the next 4 nearest neighbours in spatial and spectral direction
 are searched for valid pixels until a limit of max_rad.
 A maximum of 4 valid pixels are used for interpolation by their mean.

 */

cpl_image * sinfo_interpol_source_image ( cpl_image * im,
                                          cpl_image * mask,
                                          int        max_rad,
                                          float   ** slit_edges )
{
    cpl_image * returnImage=NULL ;
    float validpixel[6] ;
    float sum=0 ;
    int n=0;
    int row=0;
    int col=0;
    int i=0;
    int k=0;
    int slitlet=0;
    int n_slitlets=0;
    int ilx=0;
    int ily=0;
    int mlx=0;
    int mly=0;

    float* pidata=NULL;
    float* podata=NULL;
    float* pmdata=NULL;


    if ( NULL == im )
    {
        sinfo_msg_error("sorry, no input image given!") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(im);
    ily=cpl_image_get_size_y(im);
    pidata=cpl_image_get_data_float(im);

    if ( NULL == mask )
    {
        sinfo_msg_error("sorry, no bad pixel mask image given!") ;
        return NULL ;
    }
    mlx=cpl_image_get_size_x(mask);
    mly=cpl_image_get_size_y(mask);
    pmdata=cpl_image_get_data_float(mask);

    if ( mlx != ilx || mly != ily )
    {
        sinfo_msg_error("images not compatible in size!") ;
        return NULL ;
    }

    if ( max_rad < 1 )
    {
        sinfo_msg_error("sorry, wrong maximum distance given!") ;
        return NULL ;
    }

    if ( slit_edges == NULL )
    {
        sinfo_msg_error("sorry, array slit_edges is empty!") ;
        return NULL ;
    }

    /* determine the number of slitlets */
    n_slitlets = N_SLITLETS ;

    /* copy the original image in the image that will be returned */
    returnImage = cpl_image_duplicate( im ) ;
    podata=cpl_image_get_data_float(returnImage);

    /* go through the rows and columns of the image and search for
      the bad pixels */
    for ( row = 0 ; row < ily ; row++ )
    {
        for ( col = 0 ; col < ilx ; col++ )
        {
            n = 0 ;
            if ( isnan(pmdata[col + row*mlx]) ||
                            pmdata[col + row*mlx] == 0. ||
                            isnan(pidata[col + row*mlx]) )
            {
                /* look for the slitlet where the bad pixel is found */
                slitlet = -1000 ;
                for ( k = 0 ; k < n_slitlets ; k++ )
                {
                    if ( sinfo_new_nint(slit_edges[k][0]) <= col &&
                                    sinfo_new_nint(slit_edges[k][1]) >= col )
                    {
                        slitlet = k ;
                    }
                    /* The following else statement is wrong, because in the
     end slitlet will always be -1000
            else
                    {
                        slitlet = -1000 ;
                    }
                     */
                }
                for ( i = 0 ; i < 6 ; i++ )
                {
                    validpixel[i] = 0. ;
                }
                /* look for the valid nearest neighbors
                   and collect them but only a maximum of 4 */
                for ( i = 1 ; i <= max_rad ; i++ )
                {
                    if ( row + i < ily)
                    {
                        if ( !isnan(pmdata[col + (row+i) * mlx])
                                        && pmdata[col + (row+i) * mlx] != 0. &&
                                        !isnan(pidata[col + (row+i) * ilx]) )
                        {
                            validpixel[n] = pidata[col + (row+i) * ilx] ;
                            n++ ;
                        }
                    }
                    if ( row - i >= 0 )
                    {
                        if ( !isnan(pmdata[col + (row-i) * mlx])
                                        && pmdata[col + (row-i) * mlx] != 0. &&
                                        !isnan(pidata[col + (row-i) * ilx]) )
                        {
                            validpixel[n] = pidata[col + (row-i) * ilx] ;
                            n++ ;
                        }
                    }

                    /* be aware of the slitlet edges in the
                       spatial direction */
                    if ( col + i < ilx )
                    {
                        if (  slitlet != -1000 )
                        {
                            if (col+i <= sinfo_new_nint(slit_edges[slitlet][1]) &&
                                            !isnan(pmdata[col + i + row * mlx]) &&
                                            pmdata[col + i + row * mlx] != 0. &&
                                            !isnan(pidata[col + i + row * ilx]) )
                            {
                                validpixel[n] = pidata[col + i + row * ilx] ;
                                n++ ;
                            }
                        }
                    }
                    if ( col - i >= 0 )
                    {
                        if ( slitlet != -1000 )
                        {
                            if (col-i >= sinfo_new_nint(slit_edges[slitlet][0]) &&
                                            !isnan(pmdata[col - i + row * mlx]) &&
                                            pmdata[col - i + row * mlx] != 0. &&
                                            !isnan(pidata[col - i + row * ilx]) )
                            {
                                validpixel[n] = pidata[col - i + row * ilx] ;
                                n++ ;
                            }
                        }
                    }

                    if ( i == 1 && n > 1 )
                    {
                        break ;
                    }
                    if ( n > 2 )
                    {
                        break ;
                    }
                }

                if ( n == 0 )
                {
                    podata[col + row*ilx] = ZERO ;
                    /*sinfo_msg_warning("sinfo_interpolSourceImage:",
                                        "bad pixel in column: %d and row: %d"
                                        " could not be interpolated!",col,row);
                     */
                }
                else
                {
                    /* now compute the mean and replace
                       the bad pixel value by the mean */
                    sum = 0. ;
                    for ( i = 0 ; i < n ; i++ )
                    {
                        sum += validpixel[i] ;
                    }
                    podata[col + row*ilx] = sum/n ;
                }
            }
        }
    }

    return returnImage ;
}

/**
@brief stack a given image row to build a whole image
@name sinfo_new_stack_row_to_image()
@param row: one image row as sinfo_vector data structure
@param ly:  image length
@return  resulting image

TODO: not used
 */

cpl_image * sinfo_new_stack_row_to_image ( Vector * row, int ly )
{
    cpl_image * image=NULL;
    int        col=0;
    int        ro=0;
    float* podata=NULL;

    if ( row == NullVector )
    {
        sinfo_msg_error ("Null sinfo_vector as input" ) ;
        return NULL ;
    }
    if ( ly <= 1 )
    {
        sinfo_msg_error ("wrong image length given" ) ;
        return NULL ;
    }

    /* allocate memory */
    if (NULL == (image = cpl_image_new(row->n_elements ,ly,CPL_TYPE_FLOAT )) )
    {
        sinfo_msg_error ("cannot allocate new image" ) ;
        return NULL ;
    }
    podata=cpl_image_get_data_float(image);

    for ( col = 0 ; col < row -> n_elements ; col++ )
    {
        for ( ro = 0 ; ro < ly ; ro++ )
        {
            podata[col + ro*ly] = row -> data[col] ;
        }
    }
    return image ;
}

/**
@brief computes the mean and standard deviation of a given
       rectangle on an image by leaving the extreme intensity values.
   @name  sinfo_new_image_stats_on_rectangle()
   @param im: flatfield image to search for bad pix
   @param loReject,
   @param hiReject: percentage (0...100) of extrem values
                                            that should not be considered
   @param llx,
   @param lly: lower left pixel position of rectangle
   @param urx,
   @param ury: upper right pixel position of rectangle
   @return data structure giving the mean and standard deviation

 */

Stats * sinfo_new_image_stats_on_rectangle ( cpl_image * im,
                                             float      loReject,
                                             float      hiReject,
                                             int        llx,
                                             int        lly,
                                             int        urx,
                                             int        ury )
{
    Stats * retstats=NULL;
    int i=0 ;
    int row=0;
    int col=0;
    int n=0;
    int npix=0;
    int lo_n=0;
    int hi_n=0;
    double pix_sum=0;
    double sqr_sum=0;
    float * pix_array=NULL;
    int im_lx=0;
    int im_ly=0;
    float* pim=NULL;

    if ( NULL == im )
    {
        sinfo_msg_error("sorry, no input image given!") ;
        return NULL ;
    }
    if ( loReject+hiReject >= 100. )
    {
        sinfo_msg_error("sorry, too much pixels rejected!") ;
        return NULL ;
    }
    if ( loReject < 0. || loReject >= 100. ||
                    hiReject < 0. || hiReject >= 100. )
    {
        sinfo_msg_error("sorry, negative reject values!") ;
        return NULL ;
    }

    im_lx=cpl_image_get_size_x(im);
    im_ly=cpl_image_get_size_y(im);

    if ( llx < 0 || lly < 0 || urx < 0 || ury < 0 ||
                    llx >= im_lx || lly >= im_ly || urx >= im_lx ||
                    ury >= im_ly || ury <= lly || urx <= llx )
    {
        sinfo_msg_error("sorry, wrong pixel coordinates of rectangle!") ;
        return NULL ;
    }

    /* allocate memory */
    retstats = (Stats*) cpl_calloc(1, sizeof(Stats)) ;
    npix = (urx - llx + 1) * (ury - lly + 1) ;
    pix_array = (float*) cpl_calloc ( npix, sizeof(float) ) ;

    /*-------------------------------------------------------------------------
     * go through the rectangle and copy the pixel values into an array.
     */
    n = 0 ;
    pim = cpl_image_get_data_float(im);
    for ( row = lly ; row <= ury ; row++ )
    {
        for ( col = llx ; col <= urx ; col++ )
        {
            if ( !isnan(pim[col + row*im_lx]) )
            {
                pix_array[n] = pim[col + row*im_lx] ;
                n++ ;
            }
        }
    }

    npix = n;
    /*if (n != npix)
    {
        sinfo_msg_error("the computed number of pixel equals "
                        "not the counted number, impossible!") ;
        cpl_free(retstats) ;
        cpl_free(pix_array) ;
        return NULL ;
    }*/

    /* determining the clean mean is already done in the recipes */
    if ( FLT_MAX == (retstats->cleanmean = sinfo_new_clean_mean(pix_array,
                    npix, loReject, hiReject)) )
    {
        sinfo_msg_error("sinfo_new_clean_mean() did not work!") ;
        cpl_free(retstats) ;
        cpl_free(pix_array) ;
        return NULL ;
    }

    /* now the clean standard deviation must be calculated */
    /* initialize sums */
    lo_n = (int) (loReject / 100. * (float)npix) ;
    hi_n = (int) (hiReject / 100. * (float)npix) ;
    pix_sum = 0. ;
    sqr_sum = 0. ;
    n = 0 ;
    for ( i = lo_n ; i <= npix - hi_n ; i++ )
    {
        pix_sum += (double)pix_array[i] ;
        sqr_sum += ((double)pix_array[i] * (double)pix_array[i]) ;
        n++ ;
    }

    if ( n == 0 )
    {
        sinfo_msg_error("number of clean pixels is zero!") ;
        cpl_free(retstats) ;
        cpl_free(pix_array) ;
        return NULL ;
    }
    retstats -> npix = n ;
    pix_sum /= (double) n ;
    sqr_sum /= (double) n ;
    retstats -> cleanstdev = (float)sqrt(sqr_sum - pix_sum * pix_sum) ;
    cpl_free (pix_array) ;
    return retstats ;
}



/**
@brief normalizes a raw flatfield image by dividing by the median of the
       central spectral pixels to produce a master flatfield
@name     :       sinfo_new_normalize_to_central_pixel()
@param image: image to normalize
@return resulting image

TODO: not used
 */

cpl_image * sinfo_new_normalize_to_central_pixel ( cpl_image * image )
{
    int col=0;
    int row=0;
    int i=0;
    int n=0;
    float* array=NULL ;
    float divisor=0;
    cpl_image * retImage=NULL;
    int ilx=0;
    int ily=0;
    float* pidata=NULL;
    float* podata=NULL;

    if ( image == NULL )
    {
        sinfo_msg_error("no image given!") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(image);
    ily=cpl_image_get_size_y(image);
    pidata=cpl_image_get_data_float(image);

    retImage = cpl_image_duplicate(image) ;
    podata=cpl_image_get_data_float(retImage);

    n = 0 ;
    /* go through the central two image rows and store
       the values in an array */
    array=cpl_calloc(2*ilx,sizeof(float)) ;

    for ( row = ily/2 ; row < ily/2+1 ; row++ )
    {
        for ( col = 0 ; col < ilx ; col++ )
        {
            if ( !isnan(pidata[col+ilx*row]) )
            {
                array[n] = pidata[col+ilx*row] ;
                n++ ;
            }
        }
    }
    /* compute the sinfo_median of the central 2 spectral
       values of all spatial pixels*/
    if ( isnan(divisor = sinfo_new_median(array, n) ) )
    {
        sinfo_msg_error("no sinfo_median possible!") ;
        return NULL ;
    }
    if ( 0 == divisor )
    {
        sinfo_msg_error("cannot divide by 0") ;
        return NULL ;
    }

    for ( i = 0 ; i < (int) ilx*ily ; i++ )
    {
        if ( isnan(pidata[i]) )
        {
            podata[i] = ZERO ;
        }
        else
        {
            podata[i] = pidata[i]/divisor ;
        }
    }
    cpl_free(array);
    return retImage ;
}

/*-------------------------------------------------------------------------*/
/**
  @name         sinfo_new_mpe_shift_image
  @memo         Shift an image by a given (non-integer) 2d offset.
  @param        image_in                Image to shift.
  @param        shift_x                 Shift in x.
  @param        shift_y                 Shift in y.
  @param        interp_kernel   Interpolation kernel to use.
  @return       1 newly allocated image.
  @see          sinfo_generate_interpolation_kernel
  @doc

  This function is a conversion to CPL of the ECLIPSE function shift_image()
  but slightly changed. If a blank (ZERO) pixel appears the blank pixel
  is shifted but preserved as blank.
  If a blank (ZERO) pixel appears within the
  interpolation kernel the blank pixel is set to 0.

  This function shifts an image by a non-integer offset, using
  interpolation. You can either generate an interpolation kernel once and
  pass it to this function, or let it generate a default kernel. In the
  former case, use sinfo_generate_interpolation_kernel() to generate an
  appropriate kernel. In the latter case, pass NULL as last argument. A
  default interpolation kernel is then generated then discarded before this
  function returns.

  The returned image is a newly allocated object, it must be deallocated
  using cpl_image_delete().

  TODO: not used
 */
/*--------------------------------------------------------------------------*/

cpl_image *
sinfo_new_mpe_shift_image(
                cpl_image    *    image_in,
                double           shift_x,
                double           shift_y,
                double       *   interp_kernel)
{
    cpl_image    *       shifted=NULL ;
    pixelvalue  *       first_pass=NULL ;
    pixelvalue  *       second_pass=NULL ;
    int             samples = KERNEL_SAMPLES ;
    int          i=0, j=0 ;

    double           ry=0 ;
    int            py=0 ;
    int             taby=0 ;
    double           value=0 ;
    size_t          pos ;
    register pixelvalue     *   pix ;
    register pixelvalue     *   pixint ;
    int             mid=0;
    double          norm=0 ;
    double       *      ker=NULL ;
    int                         freeKernel = 1 ;

    int ilx=0;
    int ily=0;
    float* pidata=NULL;
    float* psdata=NULL;


    /* error handling: test entries */
    if (image_in==NULL) return NULL ;

    /* Shifting by a zero offset returns a copy of the input image */
    if ((fabs(shift_x)<1e-2) && (fabs(shift_y)<1e-2))
        return cpl_image_duplicate(image_in) ;
    ilx=cpl_image_get_size_x(image_in);
    ily=cpl_image_get_size_y(image_in);
    pidata=cpl_image_get_data_float(image_in);


    /* See if a kernel needs to be generated */
    if (interp_kernel == NULL) {
        ker = sinfo_generate_interpolation_kernel("default") ;
        if (ker == NULL) {
            sinfo_msg_error("kernel generation failure:aborting resampling") ;
            return NULL ;
        }
    } else {
        ker = interp_kernel ;
        freeKernel = 0 ;
    }

    mid = (int)samples/(int)2 ;
    first_pass = cpl_calloc(ilx, ily*sizeof(pixelvalue)) ;
    shifted = cpl_image_new(ilx, ily,CPL_TYPE_FLOAT) ;
    psdata=cpl_image_get_data_float(shifted);

    second_pass = psdata ;

    pix = pidata ;
    if ( ilx != 1 )
    {
        for (j=0 ; j<ily ; j++)
        {
            for (i=0 ; i<ilx ; i++) {
                double fx = (double)i-shift_x ;
                int px = (int)fx ;
                double rx = fx - (double)px ;
                pos = px + j * ilx ;

                if ((px>1) && (px<(ilx-2)))
                {
                    int tabx = (int)(fabs((double)mid * rx)) ;
                    /* exclude blank (ZERO) pixels from interpolation */
                    if (isnan(pix[pos]))
                    {
                        value = ZERO ;
                    }
                    else
                    {
                        if (isnan(pix[pos-1]))
                        {
                            pix[pos-1] = 0. ;
                        }
                        if (isnan(pix[pos+1]))
                        {
                            pix[pos+1] = 0. ;
                        }
                        if (isnan(pix[pos+2]))
                        {
                            pix[pos+2] = 0. ;
                        }

                        /*
                         * Sum up over 4 closest pixel values,
                         * weighted by interpolation kernel values
                         */
                        value =     (double)pix[pos-1] * ker[mid+tabx] +
                                        (double)pix[pos] * ker[tabx] +
                                        (double)pix[pos+1] * ker[mid-tabx] +
                                        (double)pix[pos+2] * ker[samples-tabx-1] ;
                        /*
                         * Also sum up interpolation kernel coefficients
                         * for further normalization
                         */
                        norm =      (double)ker[mid+tabx] +
                                        (double)ker[tabx] +
                                        (double)ker[mid-tabx] +
                                        (double)ker[samples-tabx-1] ;
                        if (fabs(norm) > 1e-4) {
                            value /= norm ;
                        }
                    }
                } else {
                    value = ZERO ;
                }
                /*
                 * There may be a problem of rounding here if pixelvalue
                 * has not enough bits to sustain the accuracy.
                 */
                if ( isnan(value) )
                {
                    first_pass[i+j*ilx] = ZERO ;
                }
                else
                {
                    first_pass[i+j*ilx] = (pixelvalue)value ;
                }
            }
        }
    }
    else
    {
        memcpy(first_pass,pix,ily*sizeof(pixelvalue));
    }

    pixint = first_pass ;
    for (i=0 ; i<ilx ; i++) {
        for (j=0 ; j<ily ; j++) {
            double fy = (double)j - shift_y ;
            py = (int)fy ;
            ry = fy - (double)py ;
            pos = i + py * ilx ;

            taby = (int)(fabs((double)mid * ry)) ;

            if ((py>(int)1) && (py<(ily-2))) {
                /* exclude blank (ZERO) pixels from interpolation */
                if (isnan(pixint[pos]) && ilx != 1 )
                {
                    value = ZERO ;
                }
                else
                {
                    if (isnan(pixint[pos-ilx]))
                    {
                        pixint[pos-ilx] = 0. ;
                    }
                    if (isnan(pixint[pos+ilx]))
                    {
                        pixint[pos+ilx] = 0. ;
                    }
                    if (isnan(pixint[pos+2*ilx]))
                    {
                        pixint[pos+2*ilx] = 0. ;
                    }
                    /*
                     * Sum up over 4 closest pixel values,
                     * weighted by interpolation kernel values
                     */
                    value = (double)pixint[pos-ilx] * ker[mid+taby] +
                                    (double)pixint[pos] * ker[taby] +
                                    (double)pixint[pos+ilx] * ker[mid-taby] +
                                    (double)pixint[pos+2*ilx]*ker[samples-taby-1];
                    /*
                     * Also sum up interpolation kernel coefficients
                     * for further normalization
                     */
                    norm =      (double)ker[mid+taby] +
                                    (double)ker[taby] +
                                    (double)ker[mid-taby] +
                                    (double)ker[samples-taby-1] ;

                    if (fabs(norm) > 1e-4) {
                        value /= norm ;
                    }
                }
            } else {
                value = ZERO ;
            }
            if (isnan(value))
            {
                second_pass[i+j*ilx] = ZERO ;
            }
            else
            {
                second_pass[i+j*ilx] = (pixelvalue)value ;
            }
        }
    }

    cpl_free(first_pass) ;
    if (freeKernel)
        cpl_free(ker) ;
    return shifted ;
}


/*-------------------------------------------------------------------------*/
/**
  @name         sinfo_new_shift_image_in_cube
  @memo         Shift a cube plane by a given (non-integer) 2d offset.
  @param        image_in                Image to shift.
  @param        shift_x                 Shift in x.
  @param        shift_y                 Shift in y.
  @param        interp_kernel           Interpolation kernel to use.
  @param        shifted                 final shifted image
  @param        first_pass              temporary data
  @return       void, shifted image is in shifted
  @see          sinfo_generate_interpolation_kernel
  @doc

  This function is a conversion to CPL of the ECLIPSE function shift_image()
  but slightly changed. If a blank (ZERO) pixel appears the blank pixel
  is shifted but preserved as blank.
  If a blank (ZERO) pixel appears within the
  interpolation kernel the blank pixel is set to 0.

  This function shifts an image by a non-integer offset, using
  interpolation. You can either generate an interpolation kernel once and
  pass it to this function, or let it generate a default kernel. In the
  former case, use sinfo_generate_interpolation_kernel() to generate an
  appropriate kernel. In the latter case, pass NULL as last argument. A
  default interpolation kernel is then generated then discarded before this
  function returns.

  The returned image is a newly allocated object, it must be deallocated
  using sinfo_image_delete().

  TODO: not used
 */
/*--------------------------------------------------------------------------*/

void
sinfo_new_shift_image_in_cube(
                cpl_image     *   image_in,
                double           shift_x,
                double           shift_y,
                double       *   interp_kernel,
                cpl_image     *   shifted,
                pixelvalue   *   first_pass)
{
    pixelvalue  *       second_pass=NULL ;
    int             samples = KERNEL_SAMPLES ;
    int          i=0, j=0 ;
    double           fx=0, fy=0 ;
    double           rx=0, ry=0 ;
    int             px=0, py=0 ;
    int             tabx=0, taby=0 ;
    double           value=0 ;
    size_t          pos ;
    register pixelvalue     *   pix ;
    int             mid=0;
    double          norm=0 ;
    double       *      ker=NULL ;
    int                         freeKernel = 1 ;

    int ilx=0;
    int ily=0;
    int slx=0;
    int sly=0;
    float* pidata=NULL;
    float* psdata=NULL;

    /* error handling: test entries */
    if (image_in==NULL) shifted = NULL ;
    pidata=cpl_image_get_data_float(image_in);
    ilx=cpl_image_get_size_x(image_in);
    ily=cpl_image_get_size_y(image_in);

    shifted=cpl_image_new(ilx,ily,CPL_TYPE_FLOAT);
    slx=ilx;
    sly=ily;

    psdata=cpl_image_get_data_float(shifted);

    /* Shifting by a zero offset returns a copy of the input image */
    if ((fabs(shift_x)<1e-2) && (fabs(shift_y)<1e-2))
        memcpy(psdata,pidata, (size_t) slx*sly * sizeof(pixelvalue)) ;

    /* See if a kernel needs to be generated */
    if (interp_kernel == NULL) {
        ker = sinfo_generate_interpolation_kernel("default") ;
        if (ker == NULL) {
            sinfo_msg_error("kernel generation failure:aborting resampling") ;
            shifted = NULL ;
        }
    } else {
        ker = interp_kernel ;
        freeKernel = 0 ;
    }

    mid = (int)samples/(int)2 ;
    second_pass = psdata ;

    pix = pidata ;
    for (j=0 ; j<ily ; j++) {
        for (i=1 ; i<ilx-2 ; i++) {
            fx = (double)i-shift_x ;
            px = (int)fx ;
            rx = fx - (double)px ;

            pos = px + j * ilx ;

            if ((px>1) && (px<(ilx-2))) {
                tabx = (int)(fabs((double)mid * rx)) ;
                /* exclude blank (ZERO) pixels from interpolation */
                if (isnan(pix[pos]))
                {
                    value = ZERO ;
                }
                else
                {
                    if (isnan(pix[pos-1]))
                    {
                        pix[pos-1] = 0. ;
                    }
                    if (isnan(pix[pos+1]))
                    {
                        pix[pos+1] = 0. ;
                    }
                    if (isnan(pix[pos+2]))
                    {
                        pix[pos+2] = 0. ;
                    }

                    /*
                     * Sum up over 4 closest pixel values,
                     * weighted by interpolation kernel values
                     */
                    value =     (double)pix[pos-1] * ker[mid+tabx] +
                                    (double)pix[pos] * ker[tabx] +
                                    (double)pix[pos+1] * ker[mid-tabx] +
                                    (double)pix[pos+2] * ker[samples-tabx-1] ;
                    /*
                     * Also sum up interpolation kernel coefficients
                     * for further normalization
                     */
                    norm =      (double)ker[mid+tabx] +
                                    (double)ker[tabx] +
                                    (double)ker[mid-tabx] +
                                    (double)ker[samples-tabx-1] ;
                    if (fabs(norm) > 1e-4) {
                        value /= norm ;
                    }
                }
            } else {
                value = 0.0 ;
            }
            /*
             * There may be a problem of rounding here if pixelvalue
             * has not enough bits to sustain the accuracy.
             */
            if ( isnan(value) )
            {
                first_pass[i+j*ilx] = ZERO ;
            }
            else
            {
                first_pass[i+j*ilx] = (pixelvalue)value ;
            }
        }
    }
    register pixelvalue* pixint = first_pass ;
    for (i=0 ; i< ilx ; i++) {
        for (j=1 ; j< ily-2 ; j++) {
            fy = (double)j - shift_y ;
            py = (int)fy ;
            ry = fy - (double)py ;
            pos = i + py * ilx ;

            taby = (int)(fabs((double)mid * ry)) ;

            if ((py>(int)1) && (py<(ily-2))) {
                /* exclude blank (ZERO) pixels from interpolation */
                if (isnan(pixint[pos]))
                {
                    value = ZERO ;
                }
                else
                {
                    if (isnan(pixint[pos-ilx]))
                    {
                        pixint[pos-ilx] = 0. ;
                    }
                    if (isnan(pixint[pos+ilx]))
                    {
                        pixint[pos+ilx] = 0. ;
                    }
                    if (isnan(pixint[pos+2*ilx]))
                    {
                        pixint[pos+2*ilx] = 0. ;
                    }
                    /*
                     * Sum up over 4 closest pixel values,
                     * weighted by interpolation kernel values
                     */
                    value = (double)pixint[pos-ilx] * ker[mid+taby] +
                                    (double)pixint[pos] * ker[taby] +
                                    (double)pixint[pos+ilx] * ker[mid-taby] +
                                    (double)pixint[pos+2*ilx]*ker[samples-taby-1];
                    /*
                     * Also sum up interpolation kernel coefficients
                     * for further normalization
                     */
                    norm =      (double)ker[mid+taby] +
                                    (double)ker[taby] +
                                    (double)ker[mid-taby] +
                                    (double)ker[samples-taby-1] ;

                    if (fabs(norm) > 1e-4) {
                        value /= norm ;
                    }
                }
            } else {
                /* value = 0.0 ; AMo: This affect slitlet #1 */
            }
            if (isnan(value))
            {
                second_pass[i+j*ilx] = ZERO ;
            }
            else
            {
                second_pass[i+j*ilx] = (pixelvalue)value ;
            }
        }
    }

    if (freeKernel)
        cpl_free(ker) ;
}

/* function to delete the image statistics within python */
void sinfo_new_del_Stats( Stats * st)
{
    cpl_free (st) ;
}

/**
@brief combines two bad pixel mask to one using an or relation
   @param firstMask, secondMask: bad pixel masks to combine
   @return resulting image

 */

cpl_image *
sinfo_new_combine_masks ( cpl_image * firstMask, cpl_image * secondMask )
{
    cpl_image * retMask=NULL ;
    int n=0 ;
    int olx=0;
    int oly=0;
    float* podata=NULL;
    //float* pm1data=NULL;
    float* pm2data=NULL;

    if ( firstMask == NULL || secondMask == NULL )
    {
        sinfo_msg_error ("no input mask image given!") ;
        return NULL ;
    }
    retMask = cpl_image_duplicate (firstMask) ;
    podata = cpl_image_get_data_float(retMask);
    //pm1data = cpl_image_get_data_float(firstMask);
    pm2data = cpl_image_get_data_float(secondMask);
    olx=cpl_image_get_size_x(retMask);
    oly=cpl_image_get_size_y(retMask);

    for ( n = 0 ; n < (int) olx*oly ; n++ )
    {
        if ( podata[n] == 0. || pm2data[n] == 0. )
        {
            podata[n] = 0. ;
        }
        else
        {
            podata[n] = 1. ;
        }
    }
    return retMask ;
}

/**
@brief slices a data cube in x or y direction
   @param cube: input reduced data cube
   @param x  x slice pixel value
   @param y  y slice pixel value
   @return resulting slice image

TODO: not used
 */

cpl_image * sinfo_new_slice_cube (cpl_imagelist * cube, int x, int y )
{
    cpl_image * retImage=NULL ;

    int inp=0;
    int ilx=0;
    int ily=0;
    cpl_image* img=NULL;
    float* podata=NULL;
    float* pidata=NULL;

    if ( cube == NULL )
    {
        sinfo_msg_error("no cube given!") ;
        return NULL ;
    }
    if ( x > 31 || y > 31 )
    {
        sinfo_msg_warning("wrong x or y values!") ;
    }

    img=cpl_imagelist_get(cube,0);
    ilx=cpl_image_get_size_x(img);
    ily=cpl_image_get_size_y(img);
    inp=cpl_imagelist_get_size(cube);
    int z=0;
    if ( x < 0 )
    {
        /* allocate memory */
        if ( NULL == (retImage = cpl_image_new(ilx, inp, CPL_TYPE_FLOAT)) )
        {
            sinfo_msg_error("could not allocate memory!") ;
            return NULL ;
        }
        podata=cpl_image_get_data_float(retImage);
        for ( z = 0 ; z < inp ; z++ )
        {

            pidata=cpl_image_get_data_float(cpl_imagelist_get(cube,z));
            int col;
            for ( col = 0 ; col < ilx ; col++ )
            {
                podata[col+z*ilx] = pidata[col+y*ilx] ;
            }
        }
    }
    else if ( y < 0 )
    {
        /* allocate memory */
        if ( NULL == (retImage = cpl_image_new(ily, inp,CPL_TYPE_FLOAT)) )
        {
            sinfo_msg_error("could not allocate memory!") ;
            return NULL ;
        }
        podata=cpl_image_get_data_float(retImage);

        for ( z = 0 ; z < inp ; z++ )
        {
            pidata=cpl_image_get_data_float(cpl_imagelist_get(cube,z));
            int row=0;
            for ( row = 0 ; row < ily ; row++ )
            {
                podata[row+z*ily] = pidata[x+row*ily] ;
            }
        }
    }
    else
    {
        sinfo_msg_error("wrong input!") ;
        return NULL ;
    }
    return retImage ;
}

/**
@brief divides two images by considering blanks and calculating
       first 1/im2 by cutting the very high values and setting to 1,
       then multiplying im1 * 1/im2.

   @param im1
   @param im2 input images im1/im2
   @return resulting divided image

 */

cpl_image * sinfo_new_div_images_robust ( cpl_image * im1, cpl_image * im2 )
{
    cpl_image * retIm=NULL ;
    float help=0 ;
    int i=0 ;
    int lx1=0;
    int ly1=0;
    int lx2=0;
    int ly2=0;

    float* p1data=NULL;
    float* p2data=NULL;
    float* podata=NULL;

    if ( im1 == NULL || im2 == NULL )
    {
        sinfo_msg_error("no input images given!") ;
        return NULL ;
    }
    lx1=cpl_image_get_size_x(im1);
    ly1=cpl_image_get_size_y(im1);
    lx2=cpl_image_get_size_x(im2);
    ly2=cpl_image_get_size_y(im2);
    p1data=cpl_image_get_data_float(im1);
    p2data=cpl_image_get_data_float(im2);

    if ( lx1 != lx2 || ly1 != ly2 )
    {
        sinfo_msg_error("images not compatible!") ;
        return NULL ;
    }
    if ( NULL == (retIm = cpl_image_new(lx1, ly1, CPL_TYPE_FLOAT)) )
    {
        sinfo_msg_error("could not allocate memory!") ;
        return NULL ;
    }
    podata=cpl_image_get_data_float(retIm);

    for ( i = 0 ; i < (int) lx2*ly2 ; i++ )
    {
        if ( !isnan(p2data[i]) )
        {
            help = 1./p2data[i] ;
            if (fabs( help )> THRESH )
            {
                help = 1. ;
            }
        }
        else
        {
            help = ZERO ;
        }
        if ( isnan(help) || isnan(p1data[i]) )
        {
            podata[i] = ZERO ;
        }
        else
        {
            podata[i] = p1data[i] * help ;
        }
    }
    return retIm ;
}

/* TODO: not used */
cpl_image * sinfo_new_null_edges ( cpl_image * image)
{
    cpl_image * new=NULL ;
    int i=0,j=0 ;
    /*
    int ilx=0;
    int ily=0;
     */
    int olx=0;
    int oly=0;

    //float* pidata=NULL;
    float* podata=NULL;

    if ( image == NULL )
    {
        sinfo_msg_error ("no input image given!\n") ;
        return NULL ;
    }


    new = cpl_image_duplicate (image) ;
    /*
    ilx=cpl_image_get_size_x(image);
    ily=cpl_image_get_size_y(image);
     */
    olx=cpl_image_get_size_x(new);
    oly=cpl_image_get_size_y(new);
    //pidata=cpl_image_get_data_float(image);
    podata=cpl_image_get_data_float(new);

    for ( i = 0 ; i < olx ; i++ )
    {
        for ( j = 0 ; j < 4 ; j++)
        {
            podata[i+j*olx]=0;
            podata[i+(oly-j-1)*olx]=0;
        }
    }
    for ( i = 0 ; i < oly ; i++ )
    {
        for ( j = 0 ; j < 4 ; j++)
        {
            podata[j+i*olx]=0;
            podata[(olx-j-1)+i*olx]=0;
        }
    }
    return new ;
}

/* TODO: not used */

void sinfo_new_used_cor_map( cpl_image *im, cpl_image *map)
{
    int i=0,j=0,loc_index=0;
    float temp_array[2048];
    int lx=cpl_image_get_size_x(im);
    int ly=cpl_image_get_size_y(im);
    float* pidata=cpl_image_get_data_float(im);
    float* pmdata=cpl_image_get_data_float(map);

    for( j=0; j<ly; j++)
    {
        for( i=0;i<lx;i++)
        {
            loc_index = (int)pmdata[i+j*lx];
            temp_array[i] = pidata[loc_index+j*lx];
        }
        for( i=0;i<lx;i++)
        {
            pidata[i+j*lx]= temp_array[i];
        }
    }
}




/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_new_shift_image
  @memo        Shift an image by a given (non-integer) 2d offset.
  @param    image_in        Image to shift.
  @param    shift_x            Shift in x.
  @param    shift_y            Shift in y.
  @param    interp_kernel    Interpolation kernel to use.
  @return    1 newly allocated image.
  @see        sinfo_generate_interpolation_kernel
  @doc

  This function is a conversion to CPL of the ECLIPSE function shift_image()
  but slightly changed. If a blank (ZERO) pixel appears the blank pixel is
  shifted but preserved as blank.
  If a blank (ZERO) pixel appears within the interpolation kernel the blank
  pixel is set to 0.

  This function shifts an image by a non-integer offset, using
  interpolation. You can either generate an interpolation kernel once and
  pass it to this function, or let it generate a default kernel. In the
  former case, use sinfo_generate_interpolation_kernel() to generate an
  appropriate kernel. In the latter case, pass NULL as last argument. A
  default interpolation kernel is then generated then discarded before this
  function returns.

  The returned image is a newly allocated object, it must be deallocated
  using sinfo_image_delete().

 */
/*--------------------------------------------------------------------------*/

cpl_image *
sinfo_new_shift_image(
                cpl_image    *    image_in,
                double           shift_x,
                double           shift_y,
                double       *    interp_kernel)
{
    cpl_image    *    shifted=NULL ;


    int             samples = KERNEL_SAMPLES ;
    double       *    ker=NULL ;
    int                freeKernel = 1 ;
    int ilx=0;
    int ily=0;

    /* error handling: test entries */
    if (image_in==NULL) return NULL ;

    /* Shifting by a zero offset returns a copy of the input image */
    if ((fabs(shift_x)<1e-2) && (fabs(shift_y)<1e-2))
        return cpl_image_duplicate(image_in) ;

    /* See if a kernel needs to be generated */
    if (interp_kernel == NULL) {
        ker = sinfo_generate_interpolation_kernel("default") ;
        if (ker == NULL) {
            sinfo_msg_error("kernel generation failure: aborting resampling") ;
            return NULL ;
        }
    } else {
        ker = interp_kernel ;
        freeKernel = 0 ;
    }

    ilx=cpl_image_get_size_x(image_in);
    ily=cpl_image_get_size_y(image_in);


    register float* pix = cpl_image_get_data_float(image_in);
    float* first_pass=NULL;

    if (pix)
    {
        double value = 0;
        int mid = (int)samples/(int)2 ;
        first_pass = cpl_calloc(ilx, ily*sizeof(float)) ;
        shifted = cpl_image_new(ilx, ily,CPL_TYPE_FLOAT) ;
        float* second_pass = cpl_image_get_data_float(shifted);
        int i=0,j=0;
        double norm;
        size_t pos;
        for (j=0 ; j<ily ; j++) {
            for (i=1 ; i<ilx-2 ; i++) {
                double fx = (double)i-shift_x ;
                int px = (int)fx ;
                double rx = fx - (double)px ;

                pos = px + j * ilx ;

                if ((px>1) && (px<(ilx-3))) {
                    int tabx = (int)(fabs((double)mid * rx)) ;
                    /*
                     * Sum up over 4 closest pixel values,
                     * weighted by interpolation kernel values
                     */
                    value =     (double)pix[pos-1] * ker[mid+tabx] +
                                    (double)pix[pos] * ker[tabx] +
                                    (double)pix[pos+1] * ker[mid-tabx] +
                                    (double)pix[pos+2] * ker[samples-tabx-1] ;
                    /*
                     * Also sum up interpolation kernel coefficients
                     * for further normalization
                     */
                    norm =      (double)ker[mid+tabx] +
                                    (double)ker[tabx] +
                                    (double)ker[mid-tabx] +
                                    (double)ker[samples-tabx-1] ;
                    if (fabs(norm) > 1e-4) {
                        value /= norm ;
                    }
                } else {
                    value = 0.0 ;
                }
                /*
                 * There may be a problem of rounding here if pixelvalue
                 * has not enough bits to sustain the accuracy.
                 */
                first_pass[i+j*ilx] = (float)value ;
            }
        }
        register float* pixint = first_pass ;
        for (i=0 ; i<ilx ; i++) {
            for (j=1 ; j<ily-3 ; j++) {
                double fy = (double)j - shift_y ;
                int py = (int)fy ;
                double ry = fy - (double)py ;
                pos = i + py * ilx ;

                int taby = (int)(fabs((double)mid * ry)) ;

                if ((py>(int)1) && (py<(ily-2))) {
                    /*
                     * Sum up over 4 closest pixel values,
                     * weighted by interpolation kernel values
                     */
                    value = (double)pixint[pos-ilx] * ker[mid+taby] +
                                    (double)pixint[pos] * ker[taby] +
                                    (double)pixint[pos+ilx] * ker[mid-taby] +
                                    (double)pixint[pos+2*ilx]*ker[samples-taby-1];
                    /*
                     * Also sum up interpolation kernel coefficients
                     * for further normalization
                     */
                    norm =      (double)ker[mid+taby] +
                                    (double)ker[taby] +
                                    (double)ker[mid-taby] +
                                    (double)ker[samples-taby-1] ;

                    if (fabs(norm) > 1e-4) {
                        value /= norm ;
                    }
                } else {
                    value = 0.0 ;
                }
                second_pass[i+j*ilx] = (float)value ;
            }
        }
    }
    else
    {
        cpl_msg_warning(cpl_func, "cannot get a data from an image");
    }
    cpl_free(first_pass) ;
    if (freeKernel)
        cpl_free(ker) ;
    return shifted ;
}


/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_image_hermite_interpol
  @memo        Smooth an image usinh Hermite splines.
  @param       inp        Image to shift.
  @return      1 newly allocated image.
  @see         sinfo_spline_hermite
  @doc

  This function interpolate an image using hermite splines.

  The returned image is a newly allocated object, it must be deallocated
  using sinfo_image_delete().

  TODO: not used
 */
/*--------------------------------------------------------------------------*/

cpl_image *
sinfo_image_hermite_interpol(cpl_image * inp)
{

    /*
   @param xp     x-value to interpolate
   @param x      x-values
   @param y      y-values
   @param n      array length
   @param istart    (input/output) initial row (set to 0 to search all row)

  sinfo_spline_hermite( double xp, const double *x,
                        const double *y, int n, int *istart );

     */
    float* pinp=NULL;
    float* pout=NULL;
    int sx=0;
    int sy=0;
    int i=0;
    int j=0;
    int r=5;
    int k=0;

    cpl_image* out=NULL;

    cknull(inp,"Null in put image, exit");
    check_nomsg(out=cpl_image_duplicate(inp));
    check_nomsg(sx=cpl_image_get_size_x(inp));
    check_nomsg(sy=cpl_image_get_size_y(inp));
    check_nomsg(pinp=cpl_image_get_data_float(inp));
    check_nomsg(pout=cpl_image_get_data_float(out));
    for(j=r;j<sy-r;j++) {
        for(i=0;i<sx;i++) {
            for(k=-r;k<r;k++) {
                pout[j*sx+i]+=pinp[(j+k)*sx+i];
            }
            pout[j*sx+i]/=2*r;
        }
    }

    cleanup:

    if(cpl_error_get_code() != CPL_ERROR_NONE) {
        return NULL;
    } else {
        return out;

    }

}



/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_image_smooth_y
  @memo        Smooth an image using a simple mean.
  @param       inp        Image to shift.
  @return      1 newly allocated image.
  @see         sinfo_spline_hermite
  @doc

  This function interpolate an image using hermite splines.

  The returned image is a newly allocated object, it must be deallocated
  using sinfo_image_delete().

  TODO: not used
 */
/*--------------------------------------------------------------------------*/

cpl_image *
sinfo_image_smooth_y(cpl_image * inp, const int r)
{

    /*
   @param xp     x-value to interpolate
   @param x      x-values
   @param y      y-values
   @param n      array length
   @param istart    (input/output) initial row (set to 0 to search all row)

     */
    float* pinp=NULL;
    float* pout=NULL;
    int sx=0;
    int sy=0;
    int i=0;
    int j=0;
    int k=0;

    cpl_image* out=NULL;

    cknull(inp,"Null in put image, exit");
    check_nomsg(out=cpl_image_duplicate(inp));
    check_nomsg(sx=cpl_image_get_size_x(inp));
    check_nomsg(sy=cpl_image_get_size_y(inp));
    check_nomsg(pinp=cpl_image_get_data_float(inp));
    check_nomsg(pout=cpl_image_get_data_float(out));
    for(j=r;j<sy-r;j++) {
        for(i=0;i<sx;i++) {
            for(k=-r;k<r;k++) {
                pout[j*sx+i]+=pinp[(j+k)*sx+i];
            }
            pout[j*sx+i]/=2*r;
        }
    }

    cleanup:

    if(cpl_error_get_code() != CPL_ERROR_NONE) {
        return NULL;
    } else {
        return out;

    }

}


/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_image_smooth_mean_y
  @memo        Smooth an image using a simple mean.
  @param       inp        Image to shift.
  @return      1 newly allocated image.
  @see         sinfo_spline_hermite
  @doc

  This function interpolate an image using hermite splines.

  The returned image is a newly allocated object, it must be deallocated
  using sinfo_image_delete().

  TODO: not used
 */
/*--------------------------------------------------------------------------*/

cpl_image *
sinfo_image_smooth_mean_y(cpl_image * inp, const int r)
{

    /*
   @param xp     x-value to interpolate
   @param x      x-values
   @param y      y-values
   @param n      array length
   @param istart    (input/output) initial row (set to 0 to search all row)

     */
    float* pinp=NULL;
    float* pout=NULL;
    int sx=0;
    int sy=0;
    int i=0;
    int j=0;
    int k=0;

    cpl_image* out=NULL;

    cknull(inp,"Null in put image, exit");
    check_nomsg(out=cpl_image_duplicate(inp));
    check_nomsg(sx=cpl_image_get_size_x(inp));
    check_nomsg(sy=cpl_image_get_size_y(inp));
    check_nomsg(pinp=cpl_image_get_data_float(inp));
    check_nomsg(pout=cpl_image_get_data_float(out));
    for(j=r;j<sy-r;j++) {
        for(i=0;i<sx;i++) {
            for(k=-r;k<r;k++) {
                pout[j*sx+i]+=pinp[(j+k)*sx+i];
            }
            pout[j*sx+i]/=2*r;
        }
    }

    cleanup:

    if(cpl_error_get_code() != CPL_ERROR_NONE) {
        return NULL;
    } else {
        return out;

    }

}


/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_image_smooth_median_y
  @memo        Smooth an image using a simple mean.
  @param       inp        Image to shift.
  @return      1 newly allocated image.
  @see         sinfo_spline_hermite
  @doc

  This function interpolate an image using hermite splines.

  The returned image is a newly allocated object, it must be deallocated
  using sinfo_image_delete().
 */
/*--------------------------------------------------------------------------*/

cpl_image *
sinfo_image_smooth_median_y(cpl_image * inp, const int r)
{

    /*
   @param xp     x-value to interpolate
   @param x      x-values
   @param y      y-values
   @param n      array length
   @param istart    (input/output) initial row (set to 0 to search all row)

     */
    float* pout=NULL;
    int sx=0;
    int sy=0;
    int i=0;
    int j=0;

    cpl_image* out=NULL;


    cknull(inp,"Null in put image, exit");
    check_nomsg(out=cpl_image_duplicate(inp));
    check_nomsg(sx=cpl_image_get_size_x(inp));
    check_nomsg(sy=cpl_image_get_size_y(inp));
    check_nomsg(pout=cpl_image_get_data_float(out));

    for(j=r+1;j<sy-r;j++) {
        for(i=1;i<sx;i++) {
            pout[j*sx+i]=(float)cpl_image_get_median_window(inp,i,j,i,j+r);
        }
    }

    cleanup:

    if(cpl_error_get_code() != CPL_ERROR_NONE) {
        return NULL;
    } else {
        return out;

    }

}

/*-------------------------------------------------------------------------*/
/**
  @name        sinfo_image_smooth_fft
  @memo        Smooth an image using a FFT.
  @param       inp  Image to filter
  @param       fy  Image to filter
  @return      1 newly allocated image.
  @doc
  This function applies a lowpass spatial filter of frequency fy along Y.

  The returned image is a newly allocated object, it must be deallocated
  using sinfo_free_image().
 */
/*--------------------------------------------------------------------------*/

cpl_image *
sinfo_image_smooth_fft(cpl_image * inp, const int fy)
{

    int sx=0;
    int sy=0;

    cpl_image* out=NULL;
    cpl_image* im_re=NULL;
    cpl_image* im_im=NULL;
    cpl_image* ifft_re=NULL;
    cpl_image* ifft_im=NULL;
    cpl_image* filter=NULL;

    int sigma_x=0;
    int sigma_y=fy;

    cknull(inp,"Null in put image, exit");
    check_nomsg(im_re = cpl_image_cast(inp, CPL_TYPE_DOUBLE));
    check_nomsg(im_im = cpl_image_cast(inp, CPL_TYPE_DOUBLE));

    // Compute FFT
    check_nomsg(cpl_image_fft(im_re,im_im,CPL_FFT_DEFAULT));

    check_nomsg(sx=cpl_image_get_size_x(inp));
    check_nomsg(sy=cpl_image_get_size_y(inp));
    sigma_x=sx;

    //Generates filter image
    check_nomsg(filter = sinfo_gen_lowpass(sx,sy,sigma_x,sigma_y));

    //Apply filter
    cpl_image_multiply(im_re,filter);
    cpl_image_multiply(im_im,filter);

    sinfo_free_image(&filter);

    check_nomsg(ifft_re = cpl_image_duplicate(im_re));
    check_nomsg(ifft_im = cpl_image_duplicate(im_im));

    sinfo_free_image(&im_re);
    sinfo_free_image(&im_im);

    //Computes FFT-INVERSE
    check_nomsg(cpl_image_fft(ifft_re,ifft_im,CPL_FFT_INVERSE));
    check_nomsg(out = cpl_image_cast(ifft_re, CPL_TYPE_FLOAT));

    cleanup:

    sinfo_free_image(&ifft_re);
    sinfo_free_image(&ifft_im);
    sinfo_free_image(&filter);
    sinfo_free_image(&im_re);
    sinfo_free_image(&im_im);

    if(cpl_error_get_code() != CPL_ERROR_NONE) {
        return NULL;
    } else {
        return out;
    }

}



/*-------------------------------------------------------------------------*/
/**
  @brief	Generate a low pass filter for FFT convolution .
  @param	xs	x size of the generated image.
  @param	ys	y size of the generated image.
  @param	sigma_x	Sigma for the gaussian distribution.
  @param	sigma_y      Sigma for the gaussian distribution.
  @return	1 newly allocated image.

  This function generates an image of a 2d gaussian, modified in such
  a way that the different quadrants have a quadrants of the gaussian
  in the corner. This image is suitable for FFT convolution.
  Copied from eclipse, src/iproc/generate.c

  The returned image must be deallocated.
 */
/*--------------------------------------------------------------------------*/
static cpl_image *
sinfo_gen_lowpass(const int xs,
                  const int ys,
                  const double sigma_x,
                  const double sigma_y)
{



    int hlx= 0.0;
    int hly = 0.0;
    double x= 0.0;

    double gaussval= 0.0;
    double inv_sigma_x=1./sigma_x;
    double inv_sigma_y=1./sigma_y;

    float *data;

    cpl_image 	*lowpass_image=NULL;


    lowpass_image = cpl_image_new (xs, ys, CPL_TYPE_FLOAT);
    if (lowpass_image == NULL) {
        sinfo_msg_error("Cannot generate lowpass filter <%s>",
                        cpl_error_get_message());
        return NULL;
    }

    hlx = xs/2;
    hly = ys/2;

    data = cpl_image_get_data_float(lowpass_image);

    /* Given an image with pixels 0<=i<N, 0<=j<M then the convolution image
   has the following properties:

   ima[0][0] = 1
   ima[i][0] = ima[N-i][0] = exp (-0.5 * (i/sig_i)^2)   1<=i<N/2
   ima[0][j] = ima[0][M-j] = exp (-0.5 * (j/sig_j)^2)   1<=j<M/2
   ima[i][j] = ima[N-i][j] = ima[i][M-j] = ima[N-i][M-j]
             = exp (-0.5 * ((i/sig_i)^2 + (j/sig_j)^2))
     */

    data[0] = 1.0;

    /* first row */
    int i=0;
    for (i=1 ; i<=hlx ; i++) {
        x = i * inv_sigma_x;
        gaussval = exp(-0.5*x*x);
        data[i] = gaussval;
        data[xs-i] = gaussval;
    }
    int j=0;
    for (j=1; j<=hly ; j++) {
        double y = j * inv_sigma_y;
        /* first column */
        data[j*xs] = exp(-0.5*y*y);
        data[(ys-j)*xs] = exp(-0.5*y*y);

        for (i=1 ; i<=hlx ; i++) {
            /* Use internal symetries */
            x = i * inv_sigma_x;
            gaussval = exp (-0.5*(x*x+y*y));
            data[j*xs+i] = gaussval;
            data[(j+1)*xs-i] = gaussval;
            data[(ys-j)*xs+i] = gaussval;
            data[(ys+1-j)*xs-i] = gaussval;

        }
    }

    /* FIXME: for the moment, reset errno which is coming from exp()
            in first for-loop at i=348. This is causing cfitsio to
            fail when loading an extension image (bug in cfitsio too).
     */
    if(errno != 0)
        errno = 0;

    return lowpass_image;
}

static void quicksort_int(int* data, int left, int right)
{
    int i = left;
    int j = right;
    int pivot = (i + j) / 2;
    double index_value = data[pivot];
    do
    {
        while(data[i] < index_value) i++;
        while(data[j] > index_value) j--;
        if (i <= j)
        {
            if(i < j)
            {
                int tmp = data[i];
                data[i]=data[j];
                data[j]=tmp;
            }
            i++;
            j--;
        }
    } while (i <= j);

    if (i < right)
    {
        quicksort_int(data, i, right);
    }
    if (left < j)
    {
        quicksort_int(data, left, j);
    }
}

/*--------------------------------------------------------------------------*/
/**@}*/
