/* $Id: visir_destripe.c,v 1.15 2012-02-09 13:44:15 jtaylor Exp $
 *
 * This file is part of the VISIR Pipeline
 * 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: jtaylor $
 * $Date: 2012-02-09 13:44:15 $
 * $Revision: 1.15 $
 * $Name: not supported by cvs2svn $
 */

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

/*-----------------------------------------------------------------------------
                                   Includes
 -----------------------------------------------------------------------------*/

#include "visir_destripe.h"
#include "visir_utils.h"

/*-----------------------------------------------------------------------------
                          Private function prototypes
 -----------------------------------------------------------------------------*/

static cpl_error_code visir_destripe_mask(cpl_mask *);

static int visir_destripe_image_one_float(cpl_image *, double, double,
                                          cpl_boolean, cpl_boolean);
static cpl_error_code visir_destripe_find_max_index_float(const cpl_image *,
                                                          int, int,
                                                          int, int, double *,
                                                          int *, int *, int *);

static int visir_destripe_image_one_double(cpl_image *, double, double,
                                           cpl_boolean, cpl_boolean);
static cpl_error_code visir_destripe_find_max_index_double(const cpl_image *,
                                                           int, int,
                                                           int, int, double *,
                                                           int *, int *, int *);

/*----------------------------------------------------------------------------*/
/**
 * @defgroup visir_destripe Functions to destripe VISIR data
 */
/*----------------------------------------------------------------------------*/

/**@{*/

/*----------------------------------------------------------------------------*/
/**
   @brief    Remove the stripes frome an image using iterations
   @param    self         Input image with stripes to be removed
   @param    niter        Maximum number of iterations
   @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
   @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
*/
/*----------------------------------------------------------------------------*/
cpl_error_code visir_destripe_image(cpl_image * self, int niter,
                                    double threshold, double thres_detect,
                                    cpl_boolean morpho)
{

    cpl_boolean do_horizontal = CPL_TRUE;
    cpl_boolean did_find = CPL_FALSE;

    bug_if(self == NULL);
    bug_if(niter < 1);

    do {

        const char * sdir = do_horizontal ? "horizontal" : "vertical";
        int j = 0; /* Avoid (false) uninit warning */

        switch (cpl_image_get_type(self)) {
        case CPL_TYPE_DOUBLE:
            for (j = 0; j < niter; j++) {
                if (visir_destripe_image_one_double(self, threshold,
                                                    thres_detect, morpho,
                                                    do_horizontal)) break;
            }
            break;
        case CPL_TYPE_FLOAT:
            for (j = 0; j < niter; j++) {
                if (visir_destripe_image_one_float(self, threshold,
                                                   thres_detect, morpho,
                                                   do_horizontal)) break;
            }
            break;
        default:
            bug_if( 1 );
        }

        if (j == 0) {
            cpl_msg_info(cpl_func, "No %s stripes found", sdir);
        } else if (j < niter) {
            did_find = CPL_TRUE;
            cpl_msg_info(cpl_func, "No more %s stripes found in iteration %d",
                         sdir, j+1);
        } else {
            did_find = CPL_TRUE;
            cpl_msg_info(cpl_func, "Stopped %s destriping after %d iterations",
                         sdir, niter);
        }

        skip_if(0);

        do_horizontal = !do_horizontal;
    } while (!did_find && !do_horizontal);

    end_skip;

    return cpl_error_get_code();

}

/**@}*/


/*----------------------------------------------------------------------------*/
/**
  @internal
  @brief    Morphological cleaning (erosion / dilation) of a mask
  @param    self  The mask to clean
  @return   CPL_ERROR_NONE iff OK.

 */
/*----------------------------------------------------------------------------*/
static cpl_error_code visir_destripe_mask(cpl_mask * self)
{

    const int  niter  = 3;
    cpl_mask * kernel = cpl_mask_new(3, 5);
    cpl_mask * copy   = cpl_mask_new(cpl_mask_get_size_x(self),
                                     cpl_mask_get_size_y(self));
    int        i;

    bug_if(0);

    /* self = 1-(closing(1-self)) */
    cpl_mask_not(self);

    /* Fill the kernel */
    cpl_mask_not(kernel);

    bug_if(cpl_mask_filter(self, self, kernel,
                           CPL_FILTER_CLOSING, CPL_BORDER_ZERO));

    cpl_mask_delete(kernel);

    /* Create the new kernel */
    kernel = cpl_mask_new(5, 3);
    cpl_mask_not(kernel);
    cpl_mask_set(kernel, 1, 1, CPL_BINARY_0);
    cpl_mask_set(kernel, 5, 1, CPL_BINARY_0);
    cpl_mask_set(kernel, 1, 3, CPL_BINARY_0);
    cpl_mask_set(kernel, 5, 3, CPL_BINARY_0);
    /* self = 1-(dilation(1-self)) */
    for (i = 0; i < 2*(niter/2); i += 2) {
        bug_if(cpl_mask_filter(copy, self, kernel,
                               CPL_FILTER_DILATION, CPL_BORDER_ZERO));
        bug_if(cpl_mask_filter(self, copy, kernel,
                               CPL_FILTER_DILATION, CPL_BORDER_ZERO));
    }
    for (; i < niter; i++) { /* Last iteration w. copy, when niter is odd */
        bug_if(cpl_mask_filter(copy, self, kernel,
                               CPL_FILTER_DILATION, CPL_BORDER_ZERO));
        bug_if(cpl_mask_copy(self, copy, 1, 1));
    }

    cpl_mask_delete(kernel);

    /* Create the new kernel */
    kernel = cpl_mask_new(5, 5); /* Initialized to zero */
    cpl_mask_set(kernel, 3, 1, CPL_BINARY_1);
    cpl_mask_set(kernel, 2, 2, CPL_BINARY_1);
    cpl_mask_set(kernel, 3, 2, CPL_BINARY_1);
    cpl_mask_set(kernel, 4, 2, CPL_BINARY_1);
    cpl_mask_set(kernel, 1, 3, CPL_BINARY_1);
    cpl_mask_set(kernel, 2, 3, CPL_BINARY_1);
    cpl_mask_set(kernel, 3, 3, CPL_BINARY_1);
    cpl_mask_set(kernel, 4, 3, CPL_BINARY_1);
    cpl_mask_set(kernel, 5, 3, CPL_BINARY_1);
    cpl_mask_set(kernel, 2, 4, CPL_BINARY_1);
    cpl_mask_set(kernel, 3, 4, CPL_BINARY_1);
    cpl_mask_set(kernel, 4, 4, CPL_BINARY_1);
    cpl_mask_set(kernel, 3, 5, CPL_BINARY_1);
    /* self = 1-(dilation(1-self)) */
    for (i = 0; i < 2*(niter/2); i += 2) {
        bug_if(cpl_mask_filter(copy, self, kernel,
                               CPL_FILTER_DILATION, CPL_BORDER_ZERO));
        bug_if(cpl_mask_filter(self, copy, kernel,
                               CPL_FILTER_DILATION, CPL_BORDER_ZERO));
    }
    for (; i < niter; i++) { /* Last iteration w. copy, when niter is odd */
        bug_if(cpl_mask_filter(copy, self, kernel,
                               CPL_FILTER_DILATION, CPL_BORDER_ZERO));
        bug_if(cpl_mask_copy(self, copy, 1, 1));
    }

    bug_if(cpl_mask_not(self));

    end_skip;

    cpl_mask_delete(copy);
    cpl_mask_delete(kernel);

    return cpl_error_get_code();
}



/* These macros are needed for support of the different pixel types */

#define CONCAT(a,b) a ## _ ## b
#define CONCAT2X(a,b) CONCAT(a,b)

#define PIXEL_TYPE double
#define PIXEL_TYPE_CPL CPL_TYPE_DOUBLE
#include "visir_destripe_body.c"
#undef PIXEL_TYPE
#undef PIXEL_TYPE_CPL

#define PIXEL_TYPE float
#define PIXEL_TYPE_CPL CPL_TYPE_FLOAT
#include "visir_destripe_body.c"
#undef PIXEL_TYPE
#undef PIXEL_TYPE_CPL
