/********************************************************************
**                Image Component Library (ICL)                    **
**                                                                 **
** Copyright (C) 2006-2013 CITEC, University of Bielefeld          **
**                         Neuroinformatics Group                  **
** Website: www.iclcv.org and                                      **
**          http://opensource.cit-ec.de/projects/icl               **
**                                                                 **
** File   : ICLFilter/src/ICLFilter/GaborOp.cpp                    **
** Module : ICLFilter                                              **
** Authors: Christof Elbrechter                                    **
**                                                                 **
**                                                                 **
** GNU LESSER GENERAL PUBLIC LICENSE                               **
** This file may be used under the terms of the GNU Lesser General **
** Public License version 3.0 as published by the                  **
**                                                                 **
** Free Software Foundation and appearing in the file LICENSE.LGPL **
** included in the packaging of this file.  Please review the      **
** following information to ensure the license requirements will   **
** be met: http://www.gnu.org/licenses/lgpl-3.0.txt                **
**                                                                 **
** The development of this software was supported by the           **
** Excellence Cluster EXC 277 Cognitive Interaction Technology.    **
** The Excellence Cluster EXC 277 is a grant of the Deutsche       **
** Forschungsgemeinschaft (DFG) in the context of the German       **
** Excellence Initiative.                                          **
**                                                                 **
********************************************************************/

#include <ICLFilter/GaborOp.h>
#include <ICLFilter/ConvolutionOp.h>
#include <cmath>

using namespace icl::utils;
using namespace icl::core;

namespace icl{
  namespace filter{
    GaborOp::GaborOp(){}
    GaborOp::GaborOp(const Size &kernelSize,
                     std::vector<icl32f> lambdas,
                     std::vector<icl32f> thetas,
                     std::vector<icl32f> psis,
                     std::vector<icl32f> sigmas,
                     std::vector<icl32f> gammas){
      m_vecLambdas = lambdas;
      m_vecThetas = thetas;
      m_vecPsis = psis;
      m_vecSigmas = sigmas;
      m_vecGammas = gammas;
      m_oKernelSize = kernelSize;
      
      updateKernels();
    }
    
    GaborOp::~GaborOp(){
      for(unsigned int i=0;i<m_vecResults.size();++i){
        delete m_vecResults[i];
      }
    }
    
    void GaborOp::setKernelSize(const Size &size){
      m_oKernelSize = size;
     
      updateKernels();
    }
    
    void GaborOp::addLambda(float lambda){ m_vecLambdas.push_back(lambda); }
    void GaborOp::addTheta(float theta){ m_vecThetas.push_back(theta); }
    void GaborOp::addPsi(float psi){ m_vecPsis.push_back(psi); }
    void GaborOp::addSigma(float sigma){ m_vecSigmas.push_back(sigma); }
    void GaborOp::addGamma(float gamma){ m_vecGammas.push_back(gamma); }
    
    void GaborOp::updateKernels(){
      m_vecKernels.clear();
      for(unsigned int i=0;i<m_vecResults.size();i++){
        delete m_vecResults[i];
      }
      m_vecResults.clear();
      
      ICLASSERT_RETURN( m_oKernelSize != Size::null );
  
      for(unsigned int l = 0;l<m_vecLambdas.size();l++){
        for(unsigned int t = 0;t<m_vecThetas.size();t++){
          for(unsigned int p = 0;p<m_vecPsis.size();p++){
            for(unsigned int s = 0;s<m_vecSigmas.size();s++){
              for(unsigned int g = 0;g<m_vecGammas.size();g++){
                Img32f *k = createKernel(m_oKernelSize,
                                         m_vecLambdas[l],
                                         m_vecThetas[t],
                                         m_vecPsis[p],
                                         m_vecSigmas[s],
                                         m_vecGammas[g]);
                m_vecKernels.push_back(*k);
                m_vecResults.push_back(0);
                delete k;
              }
            }
          }
        }
      }
    }
  
    void GaborOp::apply(const ImgBase *poSrc, ImgBase **ppoDst){
      ICLASSERT_RETURN( poSrc );
      ICLASSERT_RETURN( ppoDst );
      ICLASSERT_RETURN( poSrc != *ppoDst);
      
  
      if(!*ppoDst){
        *ppoDst = new Img32f(Size::null,0);
      }
      ImgBase *poDst = *ppoDst;
      poDst->setChannels(0);
      poDst->setSize(Size::null);
      
      for(unsigned int i=0;i<m_vecKernels.size();i++){
        ConvolutionOp co(ConvolutionKernel(m_vecKernels[i].getData(0),m_oKernelSize, false));
        co.setCheckOnly(false);
        co.setClipToROI(true);
  
        co.apply(poSrc,&(m_vecResults[i]));
  
        poDst->setSize(m_vecResults[i]->getSize());
        poDst->asImg<icl32f>()->append(m_vecResults[i]->asImg<icl32f>());
      }
    }
    
    std::vector<icl32f> GaborOp::apply(const ImgBase *poSrc, const Point &p){
      ICLASSERT_RETURN_VAL( poSrc && poSrc->getChannels() && poSrc->getSize() != Size::null, std::vector<icl32f>() );
      std::vector<icl32f> v;
      
      
      Img32f resPix(Size(1,1),poSrc->getChannels());
      ImgBase *resPixBase  = &resPix;
      
      const ImgBase *poSrcROIPix = poSrc->shallowCopy(Rect(p,Size(1,1)));
  
      for(unsigned int i=0;i<m_vecKernels.size();i++){
        ConvolutionOp co(ConvolutionKernel(m_vecKernels[i].getData(0),m_oKernelSize, false));
        co.setCheckOnly(false);
        co.setClipToROI(true);
  
        co.apply(poSrcROIPix,&resPixBase);
        for(int c=0;c<resPix.getChannels();c++){
          v.push_back(resPix(0,0,c));
        }
      }   
      return v;
    }
   
  
    Img32f *GaborOp::createKernel(const Size &size, float lambda, float theta, float psi, float sigma, float gamma){
      Img32f *poKernelImage = new Img32f (size,1);
      
      int xCenter = size.width/2;
      int yCenter = size.height/2;
      
      Channel32f k = (*poKernelImage)[0];
      
      gamma *=gamma;
      sigma *=sigma*2;
      
      for(int x=0;x<k.getWidth();++x){
        for(int y=0;y<k.getHeight();++y){
          float xTrans = xCenter-x;
          float yTrans = yCenter-y;
          float x2 = xTrans*cos(theta) + yTrans*sin(theta);
          float y2 = -xTrans*sin(theta) + yTrans*cos(theta);
          
          k(x,y) = exp( -(x2*x2+gamma*y2*y2)/sigma) * cos( (2.0*M_PI*x2)/lambda  + psi );
        }
      }
      
      return poKernelImage;
    }
  } // namespace filter
}