/* $Id: visir_destripe_body.c,v 1.9 2011/12/21 14:42:52 llundin Exp $
 *
 * This file is part of the irplib package 
 * Copyright (C) 2002,2003 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, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */

/*
 * $Author: llundin $
 * $Date: 2011/12/21 14:42:52 $
 * $Revision: 1.9 $
 * $Name: visir-3_5_1 $
 */

#define TYPE_ADD(a) CONCAT2X(a, PIXEL_TYPE)
#define TYPE_ADD_CONST(a) CONCAT2X(a, CONCAT2X(PIXEL_TYPE, const))

/*----------------------------------------------------------------------------*/
/**
  @internal
  @brief    Find the maximum...
  @param    self        The image with pixels of type PIXEL_TYPE
  @param    per_stripe  Period of stripe in X-dimension
  @param    n_per       Number of blocks each with per_stripe X dimy
  @param    dimy        Size in Y-dimension
  @param    off_min     Minimum size of stripe
  @param    pmaximum    Pointer to maximum value on success
  @param    pindex      Pointer to image plane on success
  @param    pmax_x      Pointer to image X-pixel on success (1 for first)
  @param    pmax_y      Pointer to image Y-pixel on success (1 for first)
  @return   0 iff everything is ok
  @note update this text, since it is clearly insufficient and yet misleading

 */
/*----------------------------------------------------------------------------*/
static cpl_error_code
TYPE_ADD(visir_destripe_find_max_index)(const cpl_image * self,
                                        int per_stripe, int n_per,
                                        int dimy, int off_min,
                                        double * pmaximum,
                                        int * pindex,
                                        int * pmax_x, int * pmax_y)
{
    const PIXEL_TYPE * pimf = TYPE_ADD_CONST(cpl_image_get_data)(self);
    double maximum = 0.0;
    int iindex = -1; /* Avoid (false) uninit warning */
    int max_x  = -1; /* Avoid (false) uninit warning */
    int max_y  = -1; /* Avoid (false) uninit warning */
    int i;


    bug_if(self == NULL);
    bug_if(cpl_image_get_type(self) != PIXEL_TYPE_CPL);
    bug_if(pmaximum == NULL);
    bug_if(pindex == NULL);
    bug_if(pmax_x == NULL);
    bug_if(pmax_y == NULL);
    bug_if(off_min <= 0);
    bug_if(pimf == NULL);
    bug_if(dimy < 2 * off_min);
    bug_if(n_per < 1);


    /*find the maximum of the 'maxi' vector and the indice of the maximum*/
    for (i = 0; i < n_per; i++) {
        int istart;
        for (istart = 0; istart < dimy - 2 * off_min; istart++) {
            int istop = istart + off_min;
            int j;
            double ilen = (double)off_min;
            double sum;
            double sum_pix = 0.0;

            for (j = istart; j <= istop; j++)
                sum_pix += pimf[i + j * per_stripe];

            sum = sum_pix * sum_pix;
            if (sum > maximum * ilen) {
                maximum = sum / ilen;
                max_x = istart;
                max_y = istop;
                iindex = i;
            }

            for (istop++; istop < dimy - off_min; istop++) {
                sum_pix += pimf[i + istop * per_stripe];
                sum = sum_pix * sum_pix;
                ilen += 1.0;
                if (sum > maximum * ilen) {
                    maximum = sum / ilen;
                    max_x = istart;
                    max_y = istop;
                    iindex = i;
                }
            }
        }
    }

    *pindex = iindex;
    *pmax_x = 1 + max_x;
    *pmax_y = 1 + max_y;
    *pmaximum = maximum;

    cpl_msg_debug(cpl_func, "Maximum image is image nb %d", 1 + *pindex);
    cpl_msg_debug(cpl_func, "Max x : %d   Max y : %d", *pmax_x, *pmax_y);

    end_skip;

    return cpl_error_get_code();
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Remove the stripes frome an image
   @param    self          Input image with stripes to be removed
   @param    threshold     Use for example 3.5 * 1.64
   @param    thres_detect  Use for example 1.3
   @param    morpho        Flag to enable the morphological cleaning
   @param    do_horizontal Destripe horizontally, instead of vertically
   @return   positive if done, zero if continue, negative on error

   Possible #_cpl_error_code_ set in this function:
   - CPL_ERROR_NULL_INPUT if an input pointer is NULL
*/
/*----------------------------------------------------------------------------*/
static int TYPE_ADD(visir_destripe_image_one)(cpl_image * self,
                                              double      threshold,
                                              double      thres_detect,
                                              cpl_boolean morpho,
                                              cpl_boolean do_horizontal)
{

    /*period of stripe*/
    const int           per_stripe=16;
    const int           img_border = 2 + per_stripe;
    const int           dimx = cpl_image_get_size_y(self); /* Rotated */
    const int           dimy = cpl_image_get_size_x(self);
    cpl_image       *   bkgd = NULL;
    cpl_image       *   imabc = NULL;
    cpl_image       *   image_extract = NULL;
    double              noise, bgd;
    cpl_image       *   ima_med = NULL;
    cpl_image       *   supp_total = NULL;
    cpl_mask        *   supp_total_mask = NULL;
    cpl_image       *   imf = NULL;
    cpl_image       *   suppf = NULL;
    int                 n_per;
    /*minimum size of stripe*/
    int                 off_min=20;
    cpl_image       *   stripes = NULL;
    double              maximum = 0.0; /* Avoid (false) uninit warning */
    int                 max_x   = 0;   /* Avoid (false) uninit warning */
    int                 max_y   = 0;   /* Avoid (false) uninit warning */
    int                 indice  = 0;   /* Avoid (false) uninit warning */
    int                 ind;
    int                 i,j;
    cpl_vector      *   val_stripe = NULL;
    cpl_mask        *   kernel = cpl_mask_new(29, 29);
    cpl_mask        *   kernel3 = cpl_mask_new(3, 3);
    int                 isdone = 0;


    bug_if(0);
    bug_if(cpl_image_get_type(self) != PIXEL_TYPE_CPL);

    /* Low-pass filter 29x29 */
    bkgd = TYPE_ADD(cpl_image_wrap)(dimy, dimx,
                                    cpl_malloc(dimx*dimy*sizeof(PIXEL_TYPE)));

    bug_if(cpl_mask_not(kernel));
    bug_if(cpl_image_filter_mask(bkgd, self, kernel, CPL_FILTER_AVERAGE,
                                 CPL_BORDER_FILTER));

    imabc = cpl_image_subtract_create(self, bkgd);
    cpl_image_delete(bkgd);
    bkgd = NULL;
    bug_if(0);

    if (do_horizontal) {
        /* Stripes have to be vertical
           - turn the image to destripe horizontally */
        bug_if (cpl_image_turn(imabc, -1));
    }

    /* Extract an image to calculate the stdev and background  */
    /* because of the low quality borders  */
    image_extract = cpl_image_extract(imabc, img_border, img_border,
                                      dimx-img_border, dimy-img_border);

    bug_if (image_extract == NULL);

    noise = visir_image_sigma_clip(image_extract, &bgd);
    skip_if (0);

    cpl_image_delete(image_extract);
    image_extract = NULL;

    cpl_msg_debug(cpl_func, "imabc noise = %g", noise);
    cpl_msg_debug(cpl_func, "imabc bgd   = %g", bgd);
    bug_if(cpl_image_subtract_scalar(imabc, bgd));

    /* Create supp_total */
    ima_med = TYPE_ADD(cpl_image_wrap)(dimy, dimx,
                                   cpl_malloc(dimx*dimy*sizeof(PIXEL_TYPE)));
    bug_if(cpl_mask_not(kernel3));
    bug_if(cpl_image_filter_mask(ima_med, imabc, kernel3, CPL_FILTER_MEDIAN,
                                 CPL_BORDER_FILTER));

    /*CAREFUL:ima_med changed in abs(ima_med)*/
    bug_if (cpl_image_abs(ima_med));
    supp_total_mask = cpl_mask_threshold_image_create(ima_med,
                                                      -0.5 * DBL_MAX,
                                                      thres_detect * noise);

    bug_if (supp_total_mask == NULL);
    cpl_image_delete(ima_med);
    ima_med = NULL;
    cpl_msg_debug(cpl_func, "Stripe mask = %d/%d [pixel]",
                  (int)cpl_mask_count(supp_total_mask),
                  (int)(cpl_mask_get_size_x(supp_total_mask)*
                        cpl_mask_get_size_y(supp_total_mask)));

    supp_total = cpl_image_new_from_mask(supp_total_mask);
#ifndef _OPENMP
    bug_if(cpl_image_save(supp_total, "supp_total_1.fits", CPL_BPP_8_UNSIGNED,
                          NULL, CPL_IO_DEFAULT));
#endif

    if (morpho) {

        bug_if(visir_destripe_mask(supp_total_mask));

        cpl_image_delete(supp_total); /* :-( */
        supp_total = cpl_image_new_from_mask(supp_total_mask);

#ifndef _OPENMP
        bug_if(cpl_image_save(supp_total, "supp_total_2.fits",
                              CPL_BPP_8_UNSIGNED, NULL, CPL_IO_DEFAULT));
#endif
    }

    cpl_mask_delete(supp_total_mask);
    supp_total_mask = NULL;

    /* imabc *= supp_total / noise */
    bug_if(cpl_image_multiply(imabc, supp_total));
    bug_if(cpl_image_divide_scalar(imabc, noise));

    /* Extract the 16 first columns */
    /*sum of 16 columns by 16 columns*/
    imf   = cpl_image_extract(imabc,      1, 1, per_stripe, dimy);
    bug_if( imf == NULL);

    suppf = cpl_image_extract(supp_total, 1, 1, per_stripe, dimy);
    bug_if( suppf == NULL);

    n_per=dimx/per_stripe;
    for (i=1; i <n_per; i ++)
    {
        cpl_image * suppf_tmp;
        cpl_image * imf_tmp =
            cpl_image_extract(imabc, (per_stripe*i)+1, 1,
                              per_stripe*(i+1), dimy);
        cpl_error_code error = cpl_image_add(imf, imf_tmp);

        cpl_image_delete(imf_tmp);

        bug_if(error);

        suppf_tmp = cpl_image_extract(supp_total, (per_stripe*i)+1, 1,
                                     per_stripe*(i+1), dimy);
        error = cpl_image_add(suppf, suppf_tmp);
        cpl_image_delete(suppf_tmp);
        bug_if(error);
    }
    bug_if(cpl_image_divide_scalar(imf, n_per));

    /* Set to 0 in imf the pixels that are equal to 0 in suppf */
    bug_if(cpl_image_threshold(suppf, -0.5*DBL_MAX, 1, 0, 1));
    bug_if(cpl_image_multiply(imf, suppf));
    cpl_image_delete(suppf);
    suppf = NULL;

    bug_if (TYPE_ADD(visir_destripe_find_max_index)(imf, per_stripe, n_per, dimy,
                                                    off_min, &maximum, &indice,
                                                    &max_x, &max_y));
    cpl_image_delete(imf);
    imf = NULL;

    /* If maximum too low, no stripes found */
    if (maximum < threshold) {
        cpl_msg_info(cpl_func,"No stripes found: maximum=%g < treshold=%g",
                     maximum, threshold);
        isdone = 1;
    } else {

        const int   * psupp_total;
        const PIXEL_TYPE * pimabc;
        cpl_boolean   is_ok = CPL_FALSE;

        pimabc = TYPE_ADD_CONST(cpl_image_get_data)(imabc);
        psupp_total = cpl_image_get_data_int_const(supp_total);

        /*average stripes: take the median*/
        val_stripe = cpl_vector_new(n_per);

        for (i=0; i < n_per; i++) {
            double value = 0.0;
            int    N = 0;
            for (j=max_x; j<max_y+1; j++)
                {
                    ind = i*per_stripe+indice + j * dimx;
                    if (psupp_total[ind] == 1)
                        {
                            value += pimabc[ind];
                            N++;
                        }
                }
            if (N > 0) {
                value /= N;
                is_ok = CPL_TRUE;
            }
#ifdef VISIR_DESTRIPE_AVERAGE
            for (j=max_x; j<max_y+1; j++) {
                pima[i*per_stripe+indice+j*dimx] = value;
            }
#else
            cpl_vector_set(val_stripe, i, value);
#endif
        }
        if (is_ok) {
#ifdef VISIR_DESTRIPE_AVERAGE
            cpl_msg_info(cpl_func,"Destriping: maximum=%g < treshold=%g",
                         maximum, threshold);

            /*to recompensate for the normalisation*/
            bug_if(cpl_image_multiply_scalar(stripes, noise));
#else
            const double stripe_median = noise
                * cpl_vector_get_median(val_stripe);
            PIXEL_TYPE * pima;

            stripes = cpl_image_new(dimx, dimy, PIXEL_TYPE_CPL);
            pima = TYPE_ADD(cpl_image_get_data)(stripes);

            bug_if(0);

            for (i=0; i<n_per; i++) {
                for (j=max_x; j<max_y+1; j++) {
                    pima[i*per_stripe+indice+j*dimx] = stripe_median;
                }
            }
            cpl_msg_info(cpl_func,"Destriping: maximum=%g < treshold=%g. "
                         "value=%g @ %dX%d. (noise=%g)", maximum, threshold,
                         stripe_median, n_per, max_y+1-max_x, noise);
#endif

            if (do_horizontal) {
                bug_if(cpl_image_turn(stripes, 1));
            }

            bug_if(cpl_image_subtract(self, stripes));
            /* cpl_plot_image("", "", "", stripes); */
        } else {
            cpl_msg_warning(cpl_func, "Destriping found no stripes");
            isdone = 1;
        }
        cpl_vector_delete(val_stripe);
        val_stripe = NULL;
    }

    bug_if(0);

    end_skip;

    cpl_mask_delete(kernel);
    cpl_mask_delete(kernel3);
    cpl_image_delete(imabc);
    cpl_vector_delete(val_stripe);
    cpl_image_delete(imf);
    cpl_image_delete(suppf);
    cpl_mask_delete(supp_total_mask);
    cpl_image_delete(ima_med);
    cpl_image_delete(image_extract);
    cpl_image_delete(stripes);
    cpl_image_delete(bkgd);
    cpl_image_delete(supp_total);

    return cpl_error_get_code() ? -1 : isdone;
}

