/*===========================================================================
  Copyright (C) 1993-2009 European Southern Observatory (ESO)
 
  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., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/******************************************************************************
**
**    UNIT
**
**    Version: 19.1
**
**    Author: Jean-Luc Starck
**
**    Date:  03/02/25
**    
**    File:  wa_direct.c
**
.VERSION
 090810		last modif

*******************************************************************************
**
**    DESCRIPTION deconvolve an image with a regularization in the wavelet
**    ----------- space by the van-cittert method
**                 
**
**    PARAMETERS    
**    ----------
** Imag: (keyword: IN_A): input image
**
** PSF: (keyword: IN_B): input PSF
**
** Object: (keyword: OUT_A): output deconvolved object
**
** Nbr_Plan:  (keyword: INPUTI[1]): number of scales
**           it is not necessary to take values for Nbr_Plan superior to 5
**          
** Tab_Gamma: (keyword: INPUTR[1..Nbr_Plan-1]): 
**           regularization parameters for all the scales
**
**    RESULTS 
**    ------- 
**   two files are created. The first contains the deconvolved object
**   and the second the residual                 
**            
******************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include<midas_def.h>

#include "Def_Math.h"
#include "Def_Mem.h"
#include "Def_Wavelet.h"

#define VISU_PARAM TRUE

extern void prepare_fft_real();
extern void io_read_file_to_pict_f(), io_write_pict_f_to_file();
extern void wave_io_free();
extern void wavelet_extract_plan(), wavelet_transform_data();
extern void dec_line_column(), dec_insert_ima(), dec_center_psf(), dec_extract_ima();



float pyr_2d_cf_filter();

/***************************************************************************/

float product_h(u,v,Num, Nl, Nc, Type_Wavelet, Fc)
int u,v;
int Num;
float Fc;
int Type_Wavelet, Nl, Nc;
{
    float Prod = 1., Phi;
    int i, Dep;
    
    for (i = Num; i >= 0; i--)
    {
        INT_POW (2,i,Dep);
        Phi = pyr_2d_cf_filter(FILTER_H,
                                   (float) (Dep * u),
                                   (float) (Dep * v),
                                   Fc,
                                   Nl, Nc,
                                   Type_Wavelet);
        Prod *= Phi;
    }
    return (Prod);
}

/***************************************************************************/

void wavelet_oper1 (Wavelet, Psf, Tab_Gamma, Oper1)
wave_transf_des *Wavelet;
complex_float *Psf;
float *Tab_Gamma, *Oper1;
{
    int i, j, u, v, Dep_Plan, Dep, Nl, Nc, Nbr_Etap;
    float Phi, Psi, Som_Regul, Den;
    float Fc, P2;
    int Type_Wavelet, Ind_Plan;

    Nbr_Etap = Wavelet->Nbr_Plan - 1;
    Nl = Wavelet->Nbr_Ligne;
    Nc = Wavelet->Nbr_Col;
    Type_Wavelet = Wavelet->Type_Wave_Transform;
    Fc = Wavelet->Pyramid.Freq_Coup;

    INT_POW (2,Nbr_Etap-1,Dep_Plan);

    /* calcul du denominateur */
    for (i = 0; i < Nl; i++)
    {
        u = i - Nl / 2;
        for (j = 0; j < Nl; j++)
        {
            v = j - Nl / 2;

            /* calcul de | psf |^2 ==> Val_Psf2*/
            P2 =  pow(Psf[i*Nc+j].re, 2.) + pow(Psf[i*Nc+j].im, 2.);

            /* calcul de phi(2^i nu)^2 */
            Phi = pyr_2d_cf_filter(FILTER_H,
                                   (float) (Dep_Plan * u),
                                   (float) (Dep_Plan * v),
                                   Fc,
                                   Nl, Nc,
                                   Type_Wavelet);
            Phi *= product_h(u, v, Nbr_Etap-2, Nl, Nc, Type_Wavelet, Fc);
            Den = Phi * Phi;
            Som_Regul = 0.;
            /* calcul de som_i psi(2^i nu)^2 */
            for (Ind_Plan = 1; Ind_Plan <= Nbr_Etap; Ind_Plan++)
            {
                INT_POW (2,Ind_Plan-1,Dep);
                Psi = pyr_2d_cf_filter(FILTER_G,
                                       (float) (Dep * u),
                                       (float) (Dep * v),
                                       Fc,
                                       Nl, Nc,
                                       Type_Wavelet);
                Psi *= product_h(u, v, Ind_Plan-2, Nl, Nc, Type_Wavelet, Fc);
                Som_Regul += Psi*Psi*Tab_Gamma[Ind_Plan-1];
                Den += Psi*Psi;
            }
            Oper1 [i*Nc+j] = Den * P2 + Som_Regul;
        }
    }
}

/***************************************************************************/

static void calcul_etap2 (Tab_Coeff, Pict_Fft, Pict_UV,  
                     Nl1, Nc1, Num_Etap, Wavelet)
int Tab_Coeff;
complex_float *Pict_Fft,*Pict_UV;
int Nl1,Nc1,Num_Etap;
wave_transf_des *Wavelet;
{
    int i,j,Dep,Nl,Nc, Type_Wavelet; 
    int ind_pict_uv,ind_pict_fft;
    int Nl_2, Nc_2,Nl1_2, Nc1_2;
    int u,v,u1,v1;
    float Coef, Fc;

    Nl = Wavelet->Nbr_Ligne;
    Nc = Wavelet->Nbr_Col;
    Type_Wavelet = Wavelet->Type_Wave_Transform;
    Fc = Wavelet->Pyramid.Freq_Coup;

    Nl1_2 = Nl1 / 2;
    Nc1_2 = Nc1 / 2;
    Nl_2 = Nl / 2;
    Nc_2 = Nc / 2;

    INT_POW (2,Num_Etap-1,Dep);

    for (i = 0; i < Nl1; i++)
    {
        u = (i - Nl1_2);
        u1 = Dep * u;
        for (j = 0; j < Nc1; j++)
        {
            v = (j - Nc1_2);
            ind_pict_uv = (u + Nl_2) * Nc + v + Nc_2;
            ind_pict_fft = i * Nc1 + j;
            v1 = Dep * v;

            Coef = pyr_2d_cf_filter(Tab_Coeff, (float) u1, (float) v1,
                                    Fc, Nl, Nc, Type_Wavelet);
            Coef *= product_h(u, v, Num_Etap-2, Nl, Nc, Type_Wavelet, Fc);
            Pict_UV [ind_pict_uv].re += Pict_Fft [ind_pict_fft].re * Coef;
            Pict_UV [ind_pict_uv].im += Pict_Fft [ind_pict_fft].im * Coef;
        }
    }
}

/***************************************************************************/

void wavelet_oper2 (Wavelet, Psf, Oper2)
wave_transf_des *Wavelet;
complex_float *Psf;
complex_float *Oper2;
{
    int i, j, Nl, Nc;
    complex_float Val_Psf, Val_Psf2;
    float *Plan;
    complex_float *Plan_cf;
    int Nl_p,Nc_p;

    Nl = Wavelet->Nbr_Ligne;
    Nc = Wavelet->Nbr_Col;

    for (i = 0; i < Nl*Nc; i++) Oper2[i].re = Oper2[i].im = 0.;

    for (i = 1; i <= Wavelet->Nbr_Plan; i++)
    {
        wavelet_extract_plan (Wavelet, &Plan, &Nl_p, &Nc_p, i);
        Plan_cf = cf_vector_alloc (Nl_p*Nc_p);
        for (j = 0; j < Nl_p*Nc_p; j++) 
                           Plan[j] *= (float)(Nl * Nc) / (float)(Nl_p * Nc_p);
        prepare_fft_real (Plan, Plan_cf, Nl_p);
        ft_cf_any_power_of_2 (Plan_cf, 1, Nl_p);

        if (i != Wavelet->Nbr_Plan)
              calcul_etap2 (FILTER_G, Plan_cf, Oper2, Nl_p, Nc_p, i, Wavelet);
        else  calcul_etap2 (FILTER_H, Plan_cf, Oper2, Nl_p,Nc_p, i-1, Wavelet);
        free ((char *) Plan);
        free ((char *) Plan_cf);
    }

    for (i = 0; i < Nl; i++)
    {
        for (j = 0; j < Nl; j++)
        {
            Val_Psf.re = Psf[i*Nc+j].re;
            Val_Psf.im = -Psf[i*Nc+j].im;
            CF_MLT(Val_Psf, Oper2[i*Nc+j], Val_Psf2);
            Oper2[i*Nc+j].re = Val_Psf2.re;
            Oper2[i*Nc+j].im = Val_Psf2.im;
        }
    }
}

/***************************************************************************/

void wa_dec_direct (Imag, Psf, Result, Nl, Nc, Nbr_Plan, Tab_Gamma)
float *Imag, *Psf, *Result;
int Nl,Nc,Nbr_Plan;
float *Tab_Gamma;
{
    complex_float *Psf_cf;
    wave_transf_des Wavelet;
    int i, Type_Transform = 6;
    float Fc = 0.5, *Oper1;
    complex_float *Oper2;

    wavelet_transform_data (Imag, Nl, Nc, &Wavelet, 
                            Type_Transform, Fc, Nbr_Plan);

    Psf_cf = cf_vector_alloc (Nl*Nc);
    prepare_fft_real (Psf, Psf_cf, Nl);
    ft_cf_any_power_of_2 (Psf_cf, 1, Nl);

    Oper1 = f_vector_alloc (Nl*Nc);
    wavelet_oper1 (&Wavelet, Psf_cf, Tab_Gamma, Oper1);

    Oper2 = cf_vector_alloc (Nl*Nc);
    wavelet_oper2 (&Wavelet, Psf_cf, Oper2);

    for (i = 0; i < Nl*Nc; i++)
    {
        if (fabs(Oper1[i]) > FLOAT_EPSILON)
        {
            Oper2[i].re /= Oper1[i];
            Oper2[i].im /= Oper1[i];
        }
        else  Oper2[i].re = Oper2[i].im = 0.;
    }
    ft_cf_any_power_of_2 (Oper2, -1, Nl);

    /* Sauvegarde du resultat */
    for (i = 0; i < Nl*Nc; i++) Result[i] = Oper2[i].re;

    wave_io_free (&Wavelet);
    free ((char *) Psf_cf);
    free ((char *) Oper1);
    free ((char *) Oper2);
}

/***************************************************************************/

int main()
{
   float *Tab_Gamma;
   int i,Nl,Nc,Nl_Psf,Nc_Psf,Nl1,Nc1;
   int Nbr_Plan;
   string Name_File_Imag, Name_File_Psf, Name_File_Out;
   float *Psf, *Imag, *Imag1, *Psf1, *Result;
   int Unit;
   int Null, Actvals, Buffer_Int, Maxvals, Felem;
   int Stat;

    /* Initialisation */  
    SCSPRO("wa_direct");

    /* read the image 1 file name */
    Felem = 1;
    Maxvals = 60;
    Stat = SCKGETC("IN_A", Felem, Maxvals, &Actvals, Name_File_Imag);

    /* read the PSF file name */
    Stat = SCKGETC("IN_B", Felem, Maxvals, &Actvals, Name_File_Psf);

    /* read the output file name */
    Stat = SCKGETC("OUT_A", Felem, Maxvals, &Actvals, Name_File_Out);

    /* read the number of scales */
    Maxvals = 1;
    Stat = SCKRDI("INPUTI", Felem, Maxvals, &Actvals, 
                            &Buffer_Int, &Unit, &Null);
    Nbr_Plan = Buffer_Int;

    Tab_Gamma = f_vector_alloc (Nbr_Plan-1);
    for (i = 0; i < Nbr_Plan-1; i++)
    {
       float Val;

       Felem = i+1;
       Maxvals = 1;
       Stat = SCKRDR("INPUTR", Felem, Maxvals, &Actvals, &Val, &Unit, &Null);
       Tab_Gamma[i] = Val;
    }

    /* read the input images */
    io_read_file_to_pict_f (Name_File_Imag, &Imag, &Nl, &Nc);
    io_read_file_to_pict_f (Name_File_Psf, &Psf, &Nl_Psf, &Nc_Psf);

#if VISU_PARAM
   { char Send[100];
    sprintf(Send," File_Name_Imag = %s\n", Name_File_Imag);
    SCTPUT(Send);
    sprintf(Send," File_Name_Psf = %s\n", Name_File_Psf);
    SCTPUT(Send);
    sprintf(Send," File_Name_Out = %s\n", Name_File_Out);
    SCTPUT(Send);
    sprintf(Send," Nbr_Plan = %d\n", Nbr_Plan);
    SCTPUT(Send);
    for (i = 0; i < Nbr_Plan-1; i++) {
           printf ("Gamma[%d] = %f\n", i+1, Tab_Gamma[i]);
           SCTPUT(Send);}
   }
#endif

    /* the image is inserted in a bigger square image
       with a size which is a power of two
    */
    dec_line_column (Nl, &Nl1);
    dec_line_column (Nc, &Nc1);
    if (Nl1 < Nc1) Nl1 = Nc1;
    else Nc1 = Nl1;
#if VISU_PARAM
    printf ("Image Size: %d\n", Nl1);
#endif
    Imag1 = f_vector_alloc (Nl1*Nc1);
    dec_insert_ima (Imag, Nl, Nc, Imag1, Nl1, Nc1);

    /* Compute the FFT of the PSF in an image with the same size */
    Psf1 = f_vector_alloc (Nl1*Nc1);
    dec_center_psf (Psf, Nl_Psf, Nc_Psf, Psf1, Nl1, Nc1);
    NORM_ENERG(Psf1, Nl1*Nc1);
    free ((char *) Psf);

    Result = f_vector_alloc (Nl1*Nc1);
    wa_dec_direct (Imag1, Psf1, Result, Nl1, Nc1, Nbr_Plan, Tab_Gamma);

    dec_extract_ima (Result, Nl1, Nc1, Imag, Nl, Nc);
    io_write_pict_f_to_file (Name_File_Out, Imag, Nl, Nc);

    free ((char *) Imag);
    free ((char *) Imag1);
    free ((char *) Result);
    free ((char *) Psf1);
    SCSEPI();
return 0;
}


