/********************************************************************
**                Image Component Library (ICL)                    **
**                                                                 **
** Copyright (C) 2006-2012 CITEC, University of Bielefeld          **
**                         Neuroinformatics Group                  **
** Website: www.iclcv.org and                                      **
**          http://opensource.cit-ec.de/projects/icl               **
**                                                                 **
** File   : ICLAlgorithms/src/HoughLineDetector.cpp                **
** Module : ICLAlgorithms                                          **
** Authors: Christof Elbrechter                                    **
**                                                                 **
**                                                                 **
** Commercial License                                              **
** ICL can be used commercially, please refer to our website       **
** www.iclcv.org for more details.                                 **
**                                                                 **
** GNU General Public License Usage                                **
** Alternatively, this file may be used under the terms of the     **
** GNU General Public License version 3.0 as published by the      **
** Free Software Foundation and appearing in the file LICENSE.GPL  **
** included in the packaging of this file.  Please review the      **
** following information to ensure the GNU General Public License  **
** version 3.0 requirements will be met:                           **
** http://www.gnu.org/copyleft/gpl.html.                           **
**                                                                 **
** 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 <ICLAlgorithms/HoughLineDetector.h>
#include <ICLUtils/DynMatrixUtils.h>
#include <ICLFilter/ConvolutionOp.h>

namespace icl{


  HoughLineDetector::HoughLineDetector(float dRho, float dR, const Range32f rRange, float rInhibitionRange, float rhoInhibitionRange,
                                       bool gaussianInhib, bool blurHoughSpace,bool dilateEntries,bool blurredSampling):
    
    m_dRho(dRho),m_dR(dR),m_rRange(rRange),m_rInhib(rInhibitionRange),m_rhoInhib(rhoInhibitionRange),
    m_gaussianInhibition(gaussianInhib), m_blurHoughSpace(blurHoughSpace),m_dilateEntries(dilateEntries),
    m_blurredSampling(blurredSampling){
    
    
    m_w = ceil(2*M_PI/dRho);
    m_h = (rRange.maxVal-rRange.minVal)/dR;
    
    m_image = Img32s(Size(m_w,m_h),1);
    m_lut = m_image[0];

    m_mr = (m_h-1)/(rRange.maxVal-rRange.minVal);
    m_br = -rRange.minVal * m_mr;
    
    m_mrho = (m_w-1)/(2*M_PI);

    if(gaussianInhib){
      /// create inhibition image
      float dx = m_rhoInhib/(2*M_PI) * float(m_w);
      float dy = m_rInhib/(m_rRange.maxVal-m_rRange.minVal) * float(m_h);

      int w = 2*dx, h=2*dy;
      if(dx >0 && dy >0){
        m_inhibitImage = Img32f(Size(2*dx,2*dy),1);
        Channel32f I = m_inhibitImage[0];
        
        Point32f c(I.getWidth()/2,I.getHeight()/2);
        
        for(int x=0;x<I.getWidth();++x){
          for(int y=0;y<I.getHeight();++y){
            float r = (c-Point32f(x,y)).transform(2./w,2./h).norm();
            I(x,y) = 1.0 - exp(-r*r);
          }
        }
      }
    }
  }
  
  void HoughLineDetector::add_intern(float x, float y){
    if(m_dilateEntries){
      add_intern2(x,y);
      add_intern2(x-1,y);
      add_intern2(x+2,y);
      add_intern2(x,y-1);
      add_intern2(x,y+1);
    }else{
      add_intern2(x,y);
    }
  }
  
  void HoughLineDetector::add_intern2(float x, float y){
    if(m_blurredSampling){
      for(float rho=0;rho<2*M_PI;rho+=m_dRho){
        incLutBlurred(rho,r(rho,x,y));
      }
    }else{
      for(float rho=0;rho<2*M_PI;rho+=m_dRho){
        incLut(rho,r(rho,x,y));
      }
    }
  }

  void HoughLineDetector::clear(){
    std::fill(m_lut.begin(),m_lut.end(),0);
  }

  static inline int mult_float_int(const int &a, const float &b){
    return round(a*b);
  }
    
  void HoughLineDetector::apply_inhibition(const Point &p){
    float dx = m_rhoInhib/(2*M_PI) * float(m_w);
    float dy = m_rInhib/(m_rRange.maxVal-m_rRange.minVal) * float(m_h);
    const Rect r(p.x-dx,p.y-dy,2*dx,2*dy);  

    if(m_gaussianInhibition){
      Channel32f c0 = m_inhibitImage[0];
      for(int x=r.x;x<r.right();++x){
        for(int y=r.y;y<r.bottom();++y){
          if(!m_inhibitImage.getImageRect().contains(x-r.x,y-r.y)){
          }else{
            cyclicLUT(x,y) *= c0(x-r.x,y-r.y);
          }
        }
      }
    }else{
      for(int x=r.x;x<r.right();++x){
        for(int y=r.y;y<r.bottom();++y){
          cyclicLUT(x,y)=0;
        }
      }
    }
  }

  void HoughLineDetector::blur_hough_space_if_necessary(){ 
    if(m_blurHoughSpace){
      ImgBase *image = 0;
      ConvolutionOp co(ConvolutionKernel(ConvolutionKernel::gauss3x3));
      co.setClipToROI(false);
      co.apply(&m_image,&image);
      m_image = *image->asImg<icl32s>();
      ICL_DELETE(image);
      m_image.setFullROI();
      m_lut = m_image[0];
    }
  }
  
  std::vector<StraightLine2D> HoughLineDetector::getLines(int max) {
    blur_hough_space_if_necessary();
    
    std::vector<StraightLine2D> ls;
    ls.reserve(max);
    
    for(int i=0;i<max;++i){
      Point p(-1,-1);
      int m = m_image.getMax(0,&p);
      if(m == 0) return ls;
      ls.push_back(StraightLine2D(getRho(p.x),getR(p.y)));
      apply_inhibition(p);

    }
    
    return ls;
  }

  
  std::vector<StraightLine2D> HoughLineDetector::getLines(int max, std::vector<float> &significances){
    blur_hough_space_if_necessary();
    
    std::vector<StraightLine2D> ls;
    ls.reserve(max);
    significances.clear();
    significances.reserve(max);
    
    int firstMax = -1;
    for(int i=0;i<max;++i){
      Point p(-1,-1);
      int m = m_image.getMax(0,&p);
      if(!i) firstMax = m;
      significances.push_back(float(m)/firstMax);
      if(m == 0) return ls;
      ls.push_back(StraightLine2D(getRho(p.x),getR(p.y)));
      
      apply_inhibition(p);

    }
    
    return ls;
  }
}