/*
 * 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
 */
/************************************************************************
* E.S.O. - VLT project
*
* 
*
* who       when      what
* --------  --------  ----------------------------------------------
* schreib  04/07/00  created
*/

/************************************************************************
*   NAME              
*        sinfo_merge.c - merges the rows of two image data frames into
*                  one frame with doubled column length     
*        
*   SYNOPSIS
*   #include "merge.h"
*
*   a) cpl_image * sinfo_sinfo_merge_images ( cpl_image * im1, 
*                               cpl_image * im2, 
*                               cpl_image * res_image )
*
*   1) cpl_image * sinfo_new_remove_general_offset( cpl_image * im1, 
*                                      cpl_image * im2, 
*                                      cpl_image * res_image, 
*                                      int n )
*
*   2) cpl_image * sinfo_new_remove_regional_tilt ( cpl_image * im1,
*                                      cpl_image * im2, 
*                                      cpl_image * res_image )
*
*   3) cpl_image * sinfo_new_remove_column_offset ( cpl_image * im1, 
*                                      cpl_image * im2, 
*                                      cpl_image * res_image )
*
*   4) cpl_image * sinfo_new_remove_residual_tilt ( cpl_image * im2, 
                                                   cpl_image * res_image )
*
*   5) cpl_image * sinfo_new_remove_residual_offset( cpl_image * im2, 
                                                    cpl_image * res_image )
*
*   DESCRIPTION
*   a) merges the rows of two image frames in a way that the resulting
*      image has double length in y-direction
*
*        The procedures are used in the SPIFFI data reduction to merge two 
*        data frames. In order to fully match the two input frames there
*        are five steps (procedures) foreseen:
*        1) remove general offset between the frames, created by e.g. different
*           air masses. 
*        2) remove regional tilt between frames, created by e.g. different 
*           emissivities.
*        3) remove individual column offsets, created e.g. by imperfect 
*           guiding, offset is divided out.
*        4) remove residual individual column tilts, created by previous 
*           operations.
*        5) remove residual column offsets by subtracting the sinfo_median.
*
*   FILES
*
*   ENVIRONMENT
*
*   RETURN VALUES 
*   always the pointer to the image data structure cpl_image
*
*   CAUTIONS 
*
*   EXAMPLES
*
*   SEE ALSO
*   Python script merging.py
*
*   BUGS   
*
*------------------------------------------------------------------------
*/

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

/* 
 * System Headers
 */

/* 
 * Local Headers
 */

#include "sinfo_merge.h"
#include "sinfo_globals.h"
/**@{*/
/**
 * @addtogroup sinfo_utilities merging functions
 *
 * TBD
 */

/**
@brief merges the rows of two image frames in a way that the resulting 
       image has double length in y-direction
 @name sinfo_sinfo_merge_images()
 @param im1 image to merge,
 @param im2 image to merge 
 @param  res_image dummy for residual image
 @note: first must have smaller wavelength than the 
        second for the same pixel row.
 @return resulting merged image, final residual image
 */

cpl_image * sinfo_sinfo_merge_images ( cpl_image * im1, 
                         cpl_image * im2, 
                         cpl_image * res_image )
{
    cpl_image * out_image ;
    cpl_image * residual ;
    int i, j ;

    int lx1=0;
    int ly1=0;
    int lx2=0;
    int ly2=0;


    float* pi1data=NULL;
    float* pi2data=NULL;
    float* pirdata=NULL;
    float* poutdata=NULL;
    float* ptmpdata=NULL;



    if ( im1 == NULL || im2 == NULL || res_image == NULL)
    {
        sinfo_msg_error (" null image 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);



    pi1data=cpl_image_get_data_float(im1);
    pi2data=cpl_image_get_data_float(im2);
    pirdata=cpl_image_get_data_float(res_image);


    if ( lx1 != lx2 || ly1 != ly2 )
    {
        sinfo_msg_error ("input images are not compatible in size") ;
        return NULL ;
    }

    /* allocate memory */
    if ( NULL == (out_image = cpl_image_new (lx1, 2 * ly1,CPL_TYPE_FLOAT)) )
    {
        sinfo_msg_error (" cannot allocate new image") ;
        return NULL ;
    }
    poutdata=cpl_image_get_data_float(out_image);

    if ( NULL == (residual = cpl_image_new (lx1, ly1,CPL_TYPE_FLOAT)) )
    {
        sinfo_msg_error (" cannot allocate new image ") ;
        return NULL ;
    }
    ptmpdata=cpl_image_get_data_float(residual);

    /* now compute the final residual image */
    for ( i = 0 ; i < (int) lx1*ly1 ; i ++ )
    {
        if ( isnan(pi1data[i]) || isnan(pi2data[i]) )
        {
            ptmpdata[i] = ZERO ;
        }   
        else
        {
            ptmpdata[i] = pi1data[i] - pi2data[i] ;
        }
        pirdata[i] = ptmpdata[i] ;
    }

    /* now merge the two images */
    for ( i = 0 ; i < ly1 ; i ++ )
    {
        for ( j = 0 ; j < lx1 ; j ++ )
        {
            /* transfer rows to output */
            poutdata[2*i*lx1 + j] = pi1data[i*lx1 + j]  ;
            poutdata[(2*i+1) * lx1 + j] = pi2data[i*lx1 + j] ;
        }     
    }

    cpl_image_delete (residual) ;

    return out_image ;
}
    

/**
@brief removes the offset between two images
@name sinfo_new_remove_general_offset()
@param im1 
@param im2 two images, 
@param res_image result image
@param n number of rows from which the offset is determined.

@return changed second image, residual image if wanted or needed

@doc       
adds general offset between two frames to the second image and delivers the 
residual image, assuming that the background cancellation did 
not work perfectly well.
 */

cpl_image * sinfo_new_remove_general_offset( cpl_image * im1, 
                                cpl_image * im2, 
                                cpl_image * res_image, 
                                int n )
{
    cpl_image * out_image ;
    cpl_image * residual  ;
    pixelvalue sum, sqr_sum ;
    pixelvalue mean, stdev  ;
    int i, npix ;

    int lx1=0;
    int ly1=0;
    int lx2=0;
    int ly2=0;
    int lxr=0;
    int lyr=0;
    int lxt=0;
    int lyt=0;

    float* pi1data=NULL;
    float* pi2data=NULL;
    float* pirdata=NULL;
    float* poutdata=NULL;
    float* ptmpdata=NULL;



    if ( im1 == NULL || im2 == NULL )
    {
        sinfo_msg_error (" null image 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);



    pi1data=cpl_image_get_data_float(im1);
    pi2data=cpl_image_get_data_float(im2);


    if ( lx1 != lx2 || ly1 != ly2 )
    {
        sinfo_msg_error (" input images are not compatible in size") ;
        return NULL ;
    }
    
    if ( n <= 0 )
    {
        sinfo_msg_error("number of rows for offset determination "
                        "is 0 or smaller ") ;
        return NULL ;
    }
        
    /* allocate memory */
    if ( NULL == (residual = cpl_image_new (lx1, ly1, CPL_TYPE_FLOAT)) )
    {
        sinfo_msg_error (" cannot allocate new image ") ;
        return NULL ;
    }
       
    out_image = cpl_image_duplicate( im2 ) ;
    poutdata=cpl_image_get_data_float(out_image);
    ptmpdata=cpl_image_get_data_float(residual);
    lxt=cpl_image_get_size_x(residual);
    lyt=cpl_image_get_size_y(residual);

    /* ---------------------------------------------------------------------
     * first we determine the "good" pixels and subtract the two images 
     * then we determine the mean and 3 sigma
     */

    sum = 0. ;
    sqr_sum = 0. ;
    npix = 0 ;
    for ( i = 0 ; i < (int) lx1*ly1 ; i ++ )
    {
        if ( isnan(pi1data[i]) || isnan(pi2data[i]) )
        {
            ptmpdata[i] = ZERO ;
            continue ;
        }   
        else
        {
            ptmpdata[i] = pi1data[i] - pi2data[i] ;
        }

        sum += ptmpdata[i] ;
        sqr_sum += (ptmpdata[i]) * (ptmpdata[i]) ;
        npix ++ ;
    }
    if ( npix <= 1 )
    {
        mean = 0. ;
        stdev = 0. ;
    }
    else
    {
        mean = sum / (pixelvalue) npix ;   
        /* stdev is 3 sigma */
        stdev = 3 * sqrt(( sqr_sum - sum*mean ) / (pixelvalue)(npix - 1)) ;
    }
    
    /* exclude everything > 3 sigma */
    
    for ( i = 0 ; i < (int) lxt*lyt ; i++ )
    {
        if ( fabs( ptmpdata[i] - mean ) > stdev )
        {
            ptmpdata[i] = ZERO ;
        }
    }
    
    /* now subtract the general offset which is determined 
       as mean of the first n rows */
    
    sum = 0. ;
    npix = 0 ;
    for ( i = 0 ; i < n * lxt ; i++ )
    {
        if ( isnan(ptmpdata[i]) )
        {
            continue ;
        }

        sum += ptmpdata[i] ;
        npix ++ ;
    }
    if ( npix == 0 )
    {
        mean = 0. ;
    }
    else
    {
        mean = sum / (pixelvalue) npix ;      
    } 
    
    /* now apply this to the second input image */
    for ( i = 0 ; i < (int) lx2*ly2 ; i++ )
    {
        if ( isnan(pi2data[i]) )
        {
            poutdata[i] = ZERO ;
            continue ;
        }
        poutdata[i] = pi2data[i] + mean ;
    }    
 
    /* now determine the residual image if available */
    if ( res_image != NULL )
    {

       lxr=cpl_image_get_size_x(res_image);
       lyr=cpl_image_get_size_y(res_image);
       pirdata=cpl_image_get_data_float(res_image);


        for ( i = 0 ; i < (int) lxt*lyt ; i++ )
        {
            if ( isnan(ptmpdata[i]) )
            {
                pirdata[i] = ZERO ;
                continue ;
            }
            pirdata[i] = ptmpdata[i] - mean ;
        }
    }    

    cpl_image_delete (residual) ;

    return out_image ;
}

/**
@brief removes a general tilt from the spectra , created e.g. 
       by different emissivities of the telescope itself and
       delivers the residual image
   @name sinfo_new_remove_regional_tilt()
   @param im1 
   @param im2 both images to merge, 
   @param res_image residual image (obligatory no NULL).
   @return changed second image, residual image 
    
 */

cpl_image * sinfo_new_remove_regional_tilt ( cpl_image * im1, 
                                cpl_image * im2, 
                                cpl_image * res_image )
{
    cpl_image * out_image ;
    cpl_image * filtered  ;
    int i, j, k, npix, nrunning ;
    pixelvalue a, b, sum, sumx, sumy, sumc, sum2 ;
    int lx1=0;
    int ly1=0;
    int lx2=0;
    int ly2=0;
    int lxr=0;
    int lyr=0;
    int lxf=0;
    int lyf=0;

    float* pi1data=NULL;
    float* pi2data=NULL;
    float* pirdata=NULL;
    float* poutdata=NULL;
    float* pfildata=NULL;


    if ( im1 == NULL || im2 == NULL || res_image == NULL )
    {
        sinfo_msg_error ("null image 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);

    lxr=cpl_image_get_size_x(res_image);
    lyr=cpl_image_get_size_y(res_image);


    pi1data=cpl_image_get_data_float(im1);
    pi2data=cpl_image_get_data_float(im2);
    pirdata=cpl_image_get_data_float(res_image);

    if ( lx1 != lx2 || ly1 != ly2 ||
         lx2 != lxr || ly2 != lyr )
    {
        sinfo_msg_error ("input images are not compatible in size") ;
        return NULL ;
    }

    /* allocate memory */
    if ( NULL == ( filtered = cpl_image_new (lx2, ly2,CPL_TYPE_FLOAT)) )
    {
        sinfo_msg_error ("cannot allocate new image ") ;
        return NULL ;
    }

    out_image = cpl_image_duplicate( im2 ) ;
    poutdata=cpl_image_get_data_float(out_image);
    pfildata=cpl_image_get_data_float(filtered);
    lxf=cpl_image_get_size_x(filtered);
    lyf=cpl_image_get_size_y(filtered);
    
    /*-------------------------------------------------------------------------
     * Now work in the given difference image res_image on each 
       column separately. This image is first smoothed columnwise 
       by a running box
     */
    
    nrunning = 31 ; /* # of points in the running box, odd number required */

    for ( j = 0 ; j < lyr ; j ++ ) /* select a row */
    {
        for ( i = 0 ; i < lxr ; i ++ ) /* go through one row */
        {
            npix = 0 ;
            sum = 0. ;
            for (k = i - (nrunning-1)/2 ; k < i + (nrunning+1)/2; k ++ )
            {
                /* marginal pixels are not considered */
                if ( k < 2 )
                {
                    continue ;
                }
                if ( k > (lxr) - 2 )
                {
                    break ;

                }
                if ( isnan(pirdata[j*lxr + k]) )
                {
                    continue ;
                }
                npix ++ ;
                sum += pirdata[j*lxr + k] ;
            }
            if ( npix != 0 )
            {
                pfildata[j*lxr + i] = sum/npix ;
            }
            else
            {
                pfildata[j*lxr + i] = ZERO ;
            }
        }
    }                  
   
    /*------------------------------------------------------------------
     * now determine the tilt in each column and remove it in such a way
     * that the first rows are used as references that are not changed
     * a free regression fit is (index i means the sum over i):
     * ax + b: a = [<xiyi>-<xi><yi>]/[<xi^2>-<xi>^2]
     * =>    : a = [xiyi - xi<yi>]/[xi^2 - xi<xi>]
     *         b = <yi> - a<xi>
     */
    
    for ( i = 0 ; i < lxf ; i ++ ) /* one column selected */
    {
        sumy = 0. ;                   /* yi   */
        sumc = 0. ;                   /* xiyi */
        sumx = 0. ;                   /* xi   */
        sum2 = 0. ;                   /* xi^2 */
        npix = 0  ;  
          
        for ( j = 0 ; j < lyf ; j ++ ) 
        {
            if ( isnan(pfildata[i + j*lxf]) )
            {
                continue ;
            }
            sumy +=  pfildata[i + j*lxf] ;
            sumc += (pfildata[i + j*lxf]) * j ;
            sum2 += j*j ;
            sumx += j   ;
            npix ++     ;
        }
        if ( npix > 2 && fabs(sum2 - sumx*sumx/npix) >= 1e-6 ) 
        {
            a = ( sumc - sumx*sumy/npix ) / ( sum2 - sumx*sumx/npix ) ;
            b = ( sumy - a*sumx ) / npix ;
        } 
        else
        {
            a = ZERO ;
            b = ZERO ;
        }

        /*-----------------------------------------------------------
         * now correct the second input image im2 and the res_image.
         */ 

        if ( !isnan(a) && !isnan(b) && fabs(a) < 1e8 && fabs(b) < 1e8 )
        {
            for ( j = 0 ; j < lyf ; j ++ ) /* the same column */
            {
                if ( !isnan(poutdata[i + j*lxf]) )
                {
                    poutdata[i + j*lxf] += a*j+b ;        
                }
            }
        }
    }
   
    /* now compute the final residual image */
    for ( i = 0 ; i < (int) lx1*ly1 ; i ++ )
    {
        if ( isnan(pi1data[i]) || isnan(poutdata[i]) )
        {
            pirdata[i] = ZERO ;
        }   
        else
        {
            pirdata[i] = pi1data[i] - poutdata[i] ;
        }
    }

    cpl_image_delete (filtered) ;
   
    return out_image ;
}


/**
@brief removes individual column offset, created e.g. by imperfect guiding. 
       The offset is divided out. The ratio is derived from the medians of 
       the contributions.
   @name  sinfo_new_remove_column_offset()
   @param im1 first image 
   @param im2 already corrected second image to merge, 
   @param res_image residual image (obligatory no NULL).
   @return changed second image, residual image 
   
 */

cpl_image * sinfo_new_remove_column_offset ( cpl_image * im1, 
                                cpl_image * im2, 
                                cpl_image * res_image )
{
    cpl_image * out_image ;
    int i, j, npix, nrunning ;
    pixelvalue sum, sum2, mean, stdev, median1, median2, ratio ;
    pixelvalue * column1, * column2 ;
    int lx1=0;
    int ly1=0;
    int lx2=0;
    int ly2=0;
    int lxr=0;
    int lyr=0;
    float* pi1data=NULL;
    float* pi2data=NULL;
    float* pirdata=NULL;
    float* poutdata=NULL;

    if ( im1 == NULL || im2 == NULL || res_image == NULL )
    {
        sinfo_msg_error ("null image 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);

    lxr=cpl_image_get_size_x(res_image);
    lyr=cpl_image_get_size_y(res_image);


    pi1data=cpl_image_get_data_float(im1);
    pi2data=cpl_image_get_data_float(im2);
    pirdata=cpl_image_get_data_float(res_image);

    if ( lx1 != lx2 || ly1 != ly2 ||
         lx2 != lxr || ly2 != lyr )
    {
        sinfo_msg_error ("input images are not compatible in size") ;
        return NULL ;
    }

    out_image = cpl_image_duplicate( im2 ) ;
    poutdata=cpl_image_get_data_float(out_image);

    /*------------------------------------------------------------------------- 
     *  now we deal with a constant offset in every column. We assume that it 
        is due to redistribution of the flux. So we should divide the offset 
        out.  The ratio is derived from the medians of the contributions 
        rather than the means.
     */

    for ( i = 0 ; i < lx2 ; i ++ ) /* select a column */
    {
        /* statistics on columns */
        sum  = 0.  ;
        sum2 = 0. ;
        npix = 0  ;
        for ( j = 0 ; j < ly2 ; j ++ )
        {
            /* first select only the good pixels */
            if ( isnan(pirdata[i + j*lxr]) )
            {
                continue ;
            }
            sum  += pirdata[i + j*lxr] ;
            sum2 += pirdata[i + j*lxr] * 
                    pirdata[i + j*lxr] ; 
            npix ++ ;
        }
        if ( npix <= 1 )
        {
            continue ;
        }
        else
        {
            mean  = sum/(pixelvalue) npix ;
            if ( (sum2 - sum * mean) < 0 )
            {
                sinfo_msg_error ("variance is negative") ; 
                continue ;
            }
            else
            {
                /* 2 sigma */
                stdev = 2 * sqrt ( (sum2 - sum*mean)/(pixelvalue)(npix - 1) ) ;
            }
        }

        /* do it only if the S/N is high enough */
        if ( fabs(mean)/stdev < 0.5 )
        {
            continue ;
        }

        /* exclude everything > 2 sigma */
        for ( j = 0 ; j < lyr ; j ++ )
        {
            if ( pirdata[i + j*lxr] < mean - stdev ||
                 pirdata[i + j*lxr] > mean + stdev )
            {
                pirdata[i + j*lxr] = ZERO ;
            }
        } 
        
        /* now deal with the offset */
        median1 = 0. ;
        median2 = 0. ;
        nrunning = 0 ;
        /* allocate memory for the column buffers */
        column1 = (pixelvalue *) cpl_calloc ( ly1 , sizeof (pixelvalue *) ) ;
        column2 = (pixelvalue *) cpl_calloc ( ly2 , sizeof (pixelvalue *) ) ; 

        for ( j = 0 ; j < lyr ; j++ ) /* go through one column */
        {
            if ( isnan(pirdata[i + j*lxr]) )
            {
                continue ;
            }
            if ( isnan(pi1data[i+j*lx1]) || isnan(pi2data[i+j*lx2]) )
            {
                continue ;
            }
        column1[nrunning] = pi1data[i + j*lx1] ;
            column2[nrunning] = pi2data[i + j*lx2] ;
            nrunning ++ ;
        }

        /* change the second input image only if there are more then 
           10 % good pixels in a column */
        if ( nrunning > 0.1*lyr )
        {
            /* --------------------------------------------------------------
             * determine the medians of the columns of both images and compute 
               the ratio, the columns of the second input image are multiplied 
               by this ratio to adjust the column offsets. 
             */
            median2 = sinfo_new_median( column2, nrunning ) ;
            if ( median2 != 0. )
            {
                median1 = sinfo_new_median( column1, nrunning ) ;
                ratio = median1 / median2 ;
                if ( ratio > 0 )
                {
                    for ( j = 0 ; j < ly2 ; j++ ) /* go through one column */
                    {
                        if ( !isnan(pi2data[i + j*lx2]) )
                        {
                            poutdata[i + j*lx2] = pi2data[i + j*lx2] * ratio ;
                        }
                        else
                        {
                            poutdata[i + j*lx2] = ZERO ;
                        }
                    }
                }
            }
        }   
        cpl_free ( column1 ) ;
        cpl_free ( column2 ) ;
    }

    /* now compute the final residual image */
    for ( i = 0 ; i < (int) lx1*ly1 ; i ++ )
    {
        if ( isnan(pi1data[i]) || isnan(poutdata[i]) )
        {
            pirdata[i] = ZERO ;
        }   
        else
        {
            pirdata[i] = pi1data[i] - poutdata[i] ;
        }
    }
   
    return out_image ;
}


/**
@brief removes a residual column tilt (determined from the residual image) 
       created by previous operations
   @name sinfo_new_remove_residual_tilt()
   @param im2 second image to merge, 
   @param res_image residual image (obligatory no NULL).
   @return changed second image, residual image  
 */

cpl_image * 
sinfo_new_remove_residual_tilt ( cpl_image * im2, cpl_image * res_image )
{
    cpl_image * out_image ;
    cpl_image * residual  ;
    int i, j, npix ;
    pixelvalue a, b, sum, sumx, sumy, sumc, sum2, mean, stdev ;
    int lx2=0;
    int ly2=0;
    int rlx=0;
    int rly=0;
    float* pi2data=NULL;
    float* pirdata=NULL;
    float* poutdata=NULL;
    float* ptmpdata=NULL;




    if ( im2 == NULL || res_image == NULL )
    {
        sinfo_msg_error ("null image as input") ;
        return NULL ;
    }
    lx2=cpl_image_get_size_x(im2);
    ly2=cpl_image_get_size_y(im2);
    rlx=cpl_image_get_size_x(res_image);
    rly=cpl_image_get_size_y(res_image);
    pi2data=cpl_image_get_data_float(im2);
    pirdata=cpl_image_get_data_float(res_image);

    if ( lx2 != rlx || ly2 != rly )
    {
        sinfo_msg_error ("input images are not compatible in size") ;
        return NULL ;
    }

    out_image = cpl_image_duplicate( im2 ) ;
    residual  = cpl_image_duplicate( res_image ) ;
    poutdata=cpl_image_get_data_float(out_image);
    ptmpdata=cpl_image_get_data_float(residual);

    for ( i = 0 ; i < lx2; i++ ) /* select one column */
    {
        sum  = 0. ;
        sum2 = 0. ;
        npix = 0  ;
        for ( j = 0 ; j < ly2 ; j++ ) 
        {
            /* first select good pixels and derive the mean 
               and sigma of each column */
            if ( isnan(pirdata[i + j*rlx]) )
            {
                continue ;
            }
            sum  += pirdata[i + j*rlx] ;
            sum2 += pirdata[i + j*rlx] *
                    pirdata[i + j*rlx] ;
            npix ++ ;
        }
        
        if ( npix <= 1 )
        {
            continue ;
        }
        else
        {
            mean  = sum / (pixelvalue) npix ;
            stdev = 1.5 * sqrt( (sum2 - sum*mean) / (pixelvalue)(npix - 1) ) ;
        }
 
        /* exclude everything > 1.5 sigma */
        for ( j = 0 ; j < ly2 ; j++ )
        {
            if ( pirdata[i + j*rlx] < mean - stdev ||
                 pirdata[i + j*rlx] > mean + stdev )
            {
                pirdata[i + j*rlx] = ZERO ;
            }
        }

        /* now determine the tilt, see function sinfo_removeRegionalTilt 
           for explanation */
        sumy = 0. ;                   /* yi   */
        sumc = 0. ;                   /* xiyi */
        sumx = 0. ;                   /* xi   */
        sum2 = 0. ;                   /* xi^2 */
        npix = 0  ;  
          
        for ( j = 0 ; j < rly ; j ++ ) 
        {
            if ( isnan(pirdata[i + j*rlx]) )
            {
                continue ;
            }
            sumy +=  pirdata[i + j*rlx] ;
            sumc += (pirdata[i + j*rlx]) * j ;
            sum2 += j*j ;
            sumx += j   ;
            npix ++     ;
        }
        if ( npix > 2 && fabs(sum2 - sumx*sumx/npix) >= 1e-6 ) 
        {
            a = ( sumc - sumx*sumy/npix ) / ( sum2 - sumx*sumx/npix ) ;
            b = ( sumy - a*sumx ) / npix ;
        } 
        else
        {
            a = ZERO ;
            b = ZERO ;
        }

        /*-------------------------------------------------------------------
         * now correct the second input image im2 and the res_image.
         */ 

        if ( !isnan(a) && !isnan(b) && fabs(a) < 1e8 && fabs(b) < 1e8 )
        {
            for ( j = 0 ; j < ly2 ; j ++ ) /* the same column */
            {
                if ( !isnan(poutdata[i+j*lx2]) )
                {
                    poutdata[i + j*lx2] += a*j+b ;        
                    pirdata[i + j*lx2] = ptmpdata[i + j*lx2] -(a*j+b) ;
                }
            }
        }
    }

    cpl_image_delete (residual) ;

    return out_image ;
}


/**
@brief removes the residual offset by adding the median of the residual 
       image to each column 
   @name sinfo_new_remove_residual_offset()
   @param im2 second image that will be changed,
   @param res_image residual image must be given.
   @return changed second image, residual image

 */

cpl_image * 
sinfo_new_remove_residual_offset( cpl_image * im2, cpl_image * res_image )
{
    cpl_image * out_image ;
    int i, j, npix ;
    pixelvalue res_median ;
    pixelvalue * column ;
    int lx2=0;
    int ly2=0;
    int rlx=0;
    int rly=0;
    int olx=0;
    int oly=0;
    float* pi2data=NULL;
    float* pirdata=NULL;
    float* poudata=NULL;


    if ( im2 == NULL || res_image == NULL )
    {
        sinfo_msg_error ("null image as input") ;
        return NULL ;
    }
    lx2=cpl_image_get_size_x(im2);
    ly2=cpl_image_get_size_y(im2);
    rlx=cpl_image_get_size_x(res_image);
    rly=cpl_image_get_size_y(res_image);
    pi2data=cpl_image_get_data_float(im2);
    pirdata=cpl_image_get_data_float(res_image);


    if ( lx2 != rlx || ly2 != rly )
    {
        sinfo_msg_error ("input images are not compatible in size") ;
        return NULL ;
    }

    out_image = cpl_image_duplicate( im2 ) ;
    poudata=cpl_image_get_data_float(res_image);
    olx=cpl_image_get_size_x(res_image);
    oly=cpl_image_get_size_y(res_image);

    column = (pixelvalue *) cpl_calloc ( ly2 , sizeof (pixelvalue *) ) ;
    
    for ( i = 0 ; i < lx2 ; i++ ) /* select one column */
    {
        npix = 0  ;
    for (j=0;j<ly2;j++)
        column[j]=0;

        for ( j = 0 ; j < rly ; j++ ) /* go through one column */
        {
            if ( isnan(pirdata[i + j*rlx]) )
            {
                continue ;
            }
   
            column[npix] = pirdata[i + j*rlx] ;
            npix ++ ;
        }
            
        /* determine the sinfo_median of a column of the residual image */
        if ( npix > 0.1 * rly )
        {
            res_median = sinfo_new_median( column, npix ) ;
        }
        else
        {
            continue ;
        }
        
        for ( j = 0 ; j < ly2 ; j++ ) /* go through one column */
        {
            if ( !isnan(pi2data[i+j*lx2]))
            {
                poudata[i + j*lx2] = pi2data[i + j*lx2] + res_median ;
            }
            else
            {
                poudata[i + j*lx2] = ZERO ;
            }
            if ( !isnan(pirdata[i + j*rlx]) )
            {
                pirdata[i + j*rlx] -= res_median ;
            }
        }
    }
    cpl_free ( column ) ;
    return out_image ;
}
/**@}*/
/*___oOo___*/
