/*
 *
 * Copyright (C) 2012 Francisco Madrigal
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

// Magnitud_Gradiente.cpp : Defines the entry point for the console application.
//

/*C++*/
#include <iostream>

/*CUDA*/
#include <cutil.h>
#include <cuda_runtime.h>

/*OpenCV*/
#include <opencv2/opencv.hpp>


#include "CornerHarris.h"

int main(int argc, char **argv){

	/*Host Variables*/
	int imageW, imageH;			 //Size of Image
	cv::Mat  frame_Original; //Original Image
  cv::Mat  frame_gray;     //Gray scale image
	cv::Mat  frame_MG;
  cv::Mat  frame_Hr;
  
	/*Device Variables*/
	uchar *Image_dev;
	float *ImageDx_dev;
	float *ImageDy_dev;
	float *ImageMG_dev;
	float *ImageHD_dev;
  
	float *ImageD2x_dev;
	float *ImageD2y_dev;
	float *ImageDxy_dev;

	cv::namedWindow("Original Frame",CV_WINDOW_AUTOSIZE);
	cv::namedWindow("GradientMagnitude Frame",CV_WINDOW_AUTOSIZE);
	cv::namedWindow("CornerHarris Frame",CV_WINDOW_AUTOSIZE);

	/*Load Image*/
	frame_Original = cv::imread(argc>1?argv[1]:"../../Images/Calibration_Grid.bmp",1);
  cvtColor(frame_Original, frame_gray, CV_RGB2GRAY);
  
  if (frame_Original.empty()) {
    std::cout << "Image can not be load....." << std::endl;
    return 0;
  }
  cudaSetDevice(0);
	/*Size of Image*/
	imageW=frame_Original.cols;
	imageH=frame_Original.rows;
	size_t size =imageW*imageH*sizeof(uchar);
	size_t sizef=imageW*imageH*sizeof(float);

	/*Create host memory*/
	frame_MG.create(imageH,imageW,CV_32FC(1));
	frame_Hr.create(imageH,imageW,CV_32FC(1));

	/*Create device memory*/
	cudaMalloc((void **)&Image_dev,size);
	cudaMalloc((void **)&ImageDx_dev,sizef);
	cudaMalloc((void **)&ImageDy_dev,sizef);
	cudaMalloc((void **)&ImageMG_dev,sizef);
	cudaMalloc((void **)&ImageHD_dev,sizef);
  
	cudaMalloc((void **)&ImageD2x_dev,sizef);
	cudaMalloc((void **)&ImageD2y_dev,sizef);
	cudaMalloc((void **)&ImageDxy_dev,sizef);

	/*Copy Memory (Host-->Device)*/
	cudaMemcpy(Image_dev,frame_gray.data,size,cudaMemcpyHostToDevice);

	/*Define the size of the grid and thread blocks*/
	int   N = imageW * imageH;
	//float k = 0.10;
	float k = 0.04;
	dim3 threads(512,1,1);
	dim3 grid(N/threads.x + (N%threads.x == 0 ? 0:1),1,1);

	/*Launch the Kernel Functions*/
	CUDA_Dx(ImageDx_dev,Image_dev,N,imageW,grid,threads);
	CUDA_Dy(ImageDy_dev,Image_dev,N,imageW,grid,threads);
	CUDA_MG(ImageMG_dev,ImageDx_dev,ImageDy_dev,N,grid,threads);
  
	/*Copy Memory (Device-->Host)*/
	cudaMemcpy(frame_MG.data,ImageMG_dev,sizef,cudaMemcpyDeviceToHost);
  
	cv::normalize(frame_MG,frame_MG,1.0,0.0,CV_MINMAX);
	cv::imshow("Original Frame",frame_Original);
	cv::imshow("GradientMagnitude Frame",frame_MG);
  
	cv::waitKey(1);
  
	int window=1;

	dim3 threads2(16,16,1);
	dim3 grid2(imageW/threads2.x + (imageW%threads2.x == 0 ? 0:1),imageH/threads2.y + (imageH%threads2.y == 0 ? 0:1),1);

	CUDA_Harris_window(ImageHD_dev,ImageDx_dev,ImageDy_dev,imageW,imageH,window,k,grid2,threads2);

  
	/*Copy Memory (Device-->Host)*/
  cudaMemcpy(frame_Hr.data,ImageHD_dev,sizef,cudaMemcpyDeviceToHost);
  
  /*Re-scale image and apply a threshold*/
  double thr = 0.08;//130
  
  cv::normalize(frame_Hr,frame_Hr,1.0,0.0,CV_MINMAX);

  cv::threshold(frame_Hr, frame_Hr, thr, 1.0f, cv::THRESH_BINARY);
  
  /*For display only*/
	cv::Mat strel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5,5));
  cv::morphologyEx(frame_Hr, frame_Hr, cv::MORPH_DILATE, strel); 
 

   // cv::normalize(frame_Hr,frame_Hr,1.0,0.0,CV_MINMAX);

	cv::imshow("Original Frame",frame_Original);
	cv::imshow("CornerHarris Frame",frame_Hr);

	std::cout << "Done..." << std::endl;
	
	cv::waitKey(0);

	/*Clean Memory*/
	/*Host*/
	cv::destroyWindow("Original Frame");
	cv::destroyWindow("GradientMagnitude Frame");
	cv::destroyWindow("CornerHarris Frame");
	
	/*Device*/
	cudaFree(Image_dev);
	cudaFree(ImageDx_dev);
	cudaFree(ImageDy_dev);
	cudaFree(ImageMG_dev);
  
	cudaFree(Image_dev);
	cudaFree(ImageDx_dev);
	cudaFree(ImageDy_dev);
	cudaFree(ImageMG_dev);
  
  cudaDeviceReset();
	return(0);
}
