/*-----------------------------------------------------------------------

  File        : hough_transform.cpp

  Description : Implementation of the Hough transform

  Author      : David Tschumperlé
  
  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.
  
  --------------------------------------------------------------------*/

#include "../CImg.h"
using namespace cimg_library;
// The lines below are not necessary in your own code, it simply allows 
// the source compilation with compilers that do not respect the C++ standart.
#if ( defined(_MSC_VER) && _MSC_VER<=1200 ) || defined(__DMC__)
#define std
#endif

int main(int argc,char **argv) {
  cimg_usage("Illustration of the Hough transform");
  std::fprintf(stderr,"\
\nInstruction : \n\
------------- \n\
1) When clicking on the image, all lines crossing the clicked point will be voted in the Hough Transform image\n\
2) When clicking on the vote image, the corresponding line is drawn on the image\n\
3) When pressing the space bar, the vote is done from the image gradients\n\n\
Note that a logarithmic transform is done for the display of the vote image\n\
See also the available options (run '%s -h')\n",argv[0]);
  
  CImg<unsigned char> src(cimg_option("-i","img/parrot_original.ppm","Input image"));
  CImg<> vote(500,400,1,1,0), img = CImg<>(src).get_norm_pointwise().normalize(0,255).resize(-100,-100,1,2,2);
  
  CImgDisplay disp(src,"Image",0), dispvote(vote,"Hough Transform");
  const unsigned char col1[3]={255,0,255}, col2[3]={0,255,0};
  const double
    alpha = cimg_option("-a",1.5,"Gradient smoothing"),
    sigma = cimg_option("-s",0.5,"Hough Transform smoothing"),		
    rhomax = std::sqrt((double)(img.width*img.width+img.height*img.height))/2,
    thetamax = 2*cimg::PI;
  
  while (!disp.closed && !dispvote.closed &&
         disp.key!=cimg::keyQ && dispvote.key!=cimg::keyQ && 
         disp.key!=cimg::keyESC && dispvote.key!=cimg::keyESC) {
    
    // When pressing space bar, the vote is performed from the image gradients.
    if (dispvote.key==cimg::keySPACE || disp.key==cimg::keySPACE) {
      CImgl<> grad = img.get_gradientXY(3);
      cimgl_map(grad,l) grad[l].blur((float)alpha);
      vote.fill(0);
      cimg_mapXY(img,x,y) {
        const double 
          X = (double)x-img.width/2,
          Y = (double)y-img.height/2,
          gx = grad[0](x,y),
          gy = grad[1](x,y);
        double 
          theta = std::atan2(gy,gx),
          rho   = std::sqrt(X*X+Y*Y)*std::cos(std::atan2(Y,X)-theta);
        if (rho<0) { rho=-rho; theta+=cimg::PI; }
        theta = cimg::mod(theta,thetamax);
        vote((int)(theta*dispvote.width/thetamax),(int)(rho*dispvote.height/rhomax))+=(float)std::sqrt(gx*gx+gy*gy);
      }
      vote.blur((float)sigma);
      CImg<> vote2(vote,false); { cimg_mapXY(vote2,x,y) vote2(x,y) = (float)std::log(1+vote(x,y)); vote2.display(dispvote); }
    }
     
     // When clicking on the vote window.
    if (dispvote.button) {
      const double
        rho   = dispvote.mousey*rhomax/dispvote.height,
        theta = dispvote.mousex*thetamax/dispvote.width,
        x = img.width/2  + rho*std::cos(theta),
        y = img.height/2 + rho*std::sin(theta);
      const int 
        x0 = (int)(x+1000*std::sin(theta)),
        y0 = (int)(y-1000*std::cos(theta)),
        x1 = (int)(x-1000*std::sin(theta)),
        y1 = (int)(y+1000*std::cos(theta));
      CImg<unsigned char>(src).
        draw_line(x0,y0,x1,y1,col1,0xF0F0F0F0).draw_line(x0,y0,x1,y1,col2,0x0F0F0F0F).
        draw_line(x0+1,y0,x1+1,y1,col1,0xF0F0F0F0).draw_line(x0+1,y0,x1+1,y1,col2,0x0F0F0F0F).
        draw_line(x0,y0+1,x1,y1+1,col1,0xF0F0F0F0).draw_line(x0,y0+1,x1,y1+1,col2,0x0F0F0F0F).
        display(disp);
     }
     
     // When clicking on the image.
    if (disp.button) {
       const double 
         x0 = (double)disp.mousex-disp.width/2,
         y0 = (double)disp.mousey-disp.height/2,
         rho0 = std::sqrt(x0*x0+y0*y0),
         theta0 = std::atan2(y0,x0);
       
       for (double t=0; t<thetamax; t+=0.001) {
         double theta = t, rho = rho0*std::cos(theta0-t);
         if (rho<0) { rho=-rho; theta=cimg::mod(theta+cimg::PI,thetamax); }
         vote((int)(theta*dispvote.width/thetamax),(int)(rho*dispvote.height/rhomax))+=1;
       }
       CImg<> vote2(vote,false); cimg_mapXY(vote2,x,y) vote2(x,y) = (float)std::log(1+vote(x,y)); vote2.display(dispvote);
    }
    dispvote.resize(dispvote);
    disp.resize(disp).wait(20);
  }
  
  exit(0);
  return 0;
}
