
/*
 * Copyright (C) 2010-2022 TunaCode Pvt. Ltd.  All rights reserved.
 *
 * NOTICE TO USER:   
 *
 *
 * TUNACODE MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THIS SOURCE
 * CODE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED
 * WARRANTY OF ANY KIND.  TUNACODE DISCLAIMS ALL WARRANTIES WITH REGARD TO`
 * THIS SOURCE CODE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY,
 * NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL 
 * TUNACODE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
 * DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS,  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION,  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOURCE CODE.  
 */


#include "cuvi.h"
#include <iostream>
#include <stdio.h>


using namespace std;

void Demosaic(const string& ipath, bool show, bool dfpd, CuviBayerSeq seq)
{
	CuviStatus st = CUVI_SUCCESS;
	CuviImage img, out;

	st = img.create(ipath, CUVI_LOAD_IMAGE_GRAYSCALE_KEEP_DEPTH);
	if (CUVI_SUCCESS != st){cout << "CUVI Error: " <<st<< endl;	return;	}

	
	CuviTimer t;
	t.Start();

	if (dfpd)
		st = cuvi::colorOperations::demosaicDFPD(img, out, seq);
	else
		st = cuvi::colorOperations::demosaic(img, out, seq);
	t.Stop();

	string opath = ipath + ".result.jpg";
	st = cuvi::io::saveImage(out, opath);

	if (CUVI_SUCCESS != st)	{cout << "CUVI Error: " << st << endl; return; }
	
	else {cout << "Function Time: " << t.GetElapsedTime() <<"s"<< endl;	}

	if (show)
	{
		cuvi::showImage("CFA/Bayer Input", img);
		cuvi::showImage("Color Output", out);
		cuvi::waitKeyPress();
	}

	

}

void LowLight(const string& ipath, bool show)
{
	CuviStatus st = CUVI_SUCCESS;
	CuviImage img, out;
	
	st = img.create(ipath, CUVI_LOAD_IMAGE_COLOR_KEEP_DEPTH);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	CuviTimer t;
	
	t.Start();
	st = cuvi::colorOperations::lowlight(img, out);
	t.Stop();

	string opath = ipath + ".result.jpg";
	st = cuvi::io::saveImage(out, opath);

	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	else {cout << "Function Time: " << t.GetElapsedTime() << "s" << endl;}

	if (show)
	{
		cuvi::showImage("Input", img);
		cuvi::showImage("Output", out);
		cuvi::waitKeyPress();
	}

}

void Underwater(const string& ipath, bool show)
{
	CuviStatus st = CUVI_SUCCESS;
	CuviImage img, out;

	st = img.create(ipath, CUVI_LOAD_IMAGE_COLOR_KEEP_DEPTH);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	
	CuviTimer t;

	CuviEnhancementParams p;
	p.autoColor = true;
	p.filterStrength = 2;
	p.deGalreAmount = 0.657;

	t.Start();
	st = cuvi::imageFiltering::underwaterFilter(img, out, p);
	t.Stop();

	string opath = ipath + ".result.jpg";
	st = cuvi::io::saveImage(out, opath);


	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	else { cout << "Function Time: " << t.GetElapsedTime() << "s" << endl; }

	if (show)
	{
		cuvi::showImage("Input", img);
		cuvi::showImage("Output", out);
		cuvi::waitKeyPress();
	}

}

void ColorPick(const string& ipath, bool show, CuviColor color, Cuvi32f threshold)
{
	CuviStatus st = CUVI_SUCCESS;
	CuviTimer t;
	CuviImage img, out;

	st = img.create(ipath);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	
	t.Start();
	st = cuvi::colorOperations::colorPick(img, out, color, threshold);
	t.Stop();

	string opath = ipath + ".result.jpg";
	st = cuvi::io::saveImage(out, opath);
	
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	else { cout << "Function Time: " << t.GetElapsedTime() << "s" << endl; }

	if (show)
	{
		cuvi::showImage("Color Input", img);
		cuvi::showImage("Picked Color", out);
		cuvi::waitKeyPress();
	}

	
}

void FeatureSelection(const string& ipath, bool show, int featureCount)
{
	CuviStatus st = CUVI_SUCCESS;
	CuviImage plot, gimg;

	st = plot.create(ipath);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	CuviImage ref = plot;

	st = cuvi::colorOperations::rgb2gray(plot, gimg);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	CuviPointValue2D<float, float> *features = NULL;

	CuviRect roi(0, 0, gimg.width(), gimg.height());

	CuviFeaturesCriteria criteria;

	printf("Requested Features = %d\n\n", featureCount);

	st = cuvi::computerVision::goodFeaturesToTrack(gimg, roi, features, featureCount, criteria);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	printf("Features Found = %d\n\n", featureCount);

	
	CuviScalar color(0, 255, 255);
	CuviRect rect(0, 0, 5, 5);

	for (int i = 0; i<featureCount; i++)
	{
		rect.x = static_cast<int>(features[i].x - 1.0f);
		rect.y = static_cast<int>(features[i].y - 1.0f);

		cuvi::dataExchange::rectangle(plot, rect, color);
		if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	}

	if (show)
	{
		cuvi::showImage("Image", ref);
		plot.show("Features");
	}

	string opath = ipath + ".result.jpg";
	st = cuvi::io::saveImage(plot, opath);

	delete[] features;
	features = NULL;

	
}

void Dehaze(const string& ipath, bool show)
{

	CuviStatus st = CUVI_SUCCESS;
	CuviImage img, out;

	st = img.create(ipath, CUVI_LOAD_IMAGE_COLOR_KEEP_DEPTH);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	CuviTimer t;

	t.Start();
	st = cuvi::colorOperations::dehaze(img, out,255,255,255);
	t.Stop();


	string opath = ipath + ".result.jpg";
	st = cuvi::io::saveImage(out, opath);


	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	else {cout << "Function Time: " << t.GetElapsedTime() << "s" << endl;}

	if (show)
	{
		cuvi::showImage("Input", img);
		cuvi::showImage("Output", out);
		cuvi::waitKeyPress();
	}


}

void Resize(const string& ipath, bool show)
{
	CuviStatus st = CUVI_SUCCESS;
	CuviImage img;

	st = img.create(ipath);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	const float x = 0.8f, y = 0.8f;

	const int newWidth = static_cast<int>(img.width() * x);
	const int newHeight = static_cast<int>(img.height() * y);

	CuviImage out;
	st = out.create(newWidth, newHeight, img.depth(), img.channels());
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	CuviTimer t;

	t.Start();
	st = cuvi::geometryTransforms::resize(img, out);
	t.Stop();

	string opath = ipath + "_resized.jpg";
	st = cuvi::io::saveImage(out, opath);


	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	else { cout << "Function Time: " << t.GetElapsedTime() << "s" << endl; }

	

	if (show)
	{
		cuvi::showImage("Input image", img);
		cuvi::showImage("Resized output", out);
		cuvi::waitKeyPress();
	}

	
}

void Taper(const string& ipath, bool show)
{
	CuviStatus st = CUVI_SUCCESS;
	CuviImage img;

	st = img.create(ipath, CUVI_LOAD_IMAGE_COLOR);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	Cuvi32u taperLeft = 20, taperRight = 20, taperTop = 20, taperBottom = 20;
	//Cuvi32u taperLeft = 164, taperRight = 164, taperTop = 164, taperBottom = 164;

	//const int newWidth = static_cast<int>(img.width() * x);
	//const int newHeight = static_cast<int>(img.height() * y);

	CuviImage out;
	//st = out.create(newWidth, newHeight, img.depth(), img.channels());
	//if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	CuviTimer t;

	t.Start();
	st = cuvi::geometryTransforms::taper(img,out,taperLeft,taperRight, taperTop,taperBottom);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	t.Stop();


	string opath = ipath + ".result.jpg";
	st = cuvi::io::saveImage(out, opath);


	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	else { cout << "Function Time: " << t.GetElapsedTime() << "s" << endl; }


	

	if (show)
	{
		cuvi::showImage("Input image", img);
		cuvi::showImage("Resized output", out);
		cuvi::waitKeyPress();
	}


}

void ColorConvert(const string& ipath, bool show)
{
	CuviStatus st = CUVI_SUCCESS;
	CuviImage img, out;

	st = img.create(ipath);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	CuviTimer t;
	t.Start();
	st = cuvi::colorOperations::rgb2gray(img,out);
	t.Stop();

	string opath = ipath + ".result.jpg";
	st = cuvi::io::saveImage(out, opath);


	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	else{ cout << "Function Time: " << t.GetElapsedTime() << "s" << endl; }

	if(show)
	{
		cuvi::showImage("Color Input", img);
		cuvi::showImage("Gray Output", out);
		cuvi::waitKeyPress();
	}

	
}

void Filter(const string& ipath, bool show)
{
	CuviStatus st = CUVI_SUCCESS;
	CuviImage img, out;

	st = img.create(ipath, CUVI_LOAD_IMAGE_GRAYSCALE);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	Cuvi32f sobel[9] = {
		1.0f, 2.0f, 1.0f,
		0.0f, 0.0f, 0.0f,
		-1.0f, -2.0f, -1.0f
	};

	CuviFilter f(3,3,sobel);

	CuviTimer t;
	t.Start();
	st = cuvi::imageFiltering::imageFilter(img,out,f);
	t.Stop();

	string opath = ipath + ".result.jpg";
	st = cuvi::io::saveImage(out, opath);

	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	else { cout << "Function Time: " << t.GetElapsedTime() << "s" << endl; }

	if(show)
	{
		cuvi::showImage("Input image", img);
		cuvi::showImage("Filtered output", out);
		cuvi::waitKeyPress();
	}

}

void Haar(const string& ipath, bool show) {

	CuviStatus st = CUVI_SUCCESS;
	CuviImage img;

	st = img.create(ipath);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	

	CuviImage cA, cH, cV, cD, regenrated; 

	CuviTimer t, t2;

	t.Start();
	st = cuvi::imageTransforms::haarFwd(img, cA, cH, cV, cD);
	t.Stop();
	
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	else { cout << "Haar Fwd Function Time: " << t.GetElapsedTime() << "s" << endl; }


	t2.Start();
	st = cuvi::imageTransforms::haarInv(cA, cH, cV, cD, regenrated);
	t2.Stop();

	string opath = ipath + ".result.jpg";
	st = cuvi::io::saveImage(regenrated, opath);

	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	else { cout << "Haar Inv Function Time: " << t2.GetElapsedTime() << "s" << endl; }

	if (show) {
		cuvi::showImage("Original image", img);
		cuvi::showImage("Approximation image", cA);
		cuvi::showImage("horizontal coefficients", cH);
		cuvi::showImage("vertical coefficients", cV);
		cuvi::showImage("diagonal coefficients", cD);
		cuvi::showImage("Regenrated image", regenrated);
		cuvi::waitKeyPress();
	}

}

void Mean(const string& ipath, bool show) {

	CuviStatus st = CUVI_SUCCESS;
	CuviImage img;

	st = img.create(ipath,CUVI_LOAD_IMAGE_DEFAULT);
	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }

	CuviScalar mean;
	CuviTimer t;

	t.Start();
	st = cuvi::imageStatistics::mean(img, mean);
	t.Stop();

	if (CUVI_SUCCESS != st) { cout << "CUVI Error: " << st << endl;	return; }
	else { cout << "Function Time: " << t.GetElapsedTime() << "s" << endl; }
	
	if (show) {
		for (int i = 0; i < img.channels(); i++)
			cout <<"Mean["<<i<<"] " << mean[i] << endl;
		cout << endl;

	}

	if (show) {
		cuvi::showImage("Image", img);
		
		cuvi::waitKeyPress();
	}

	

	

}






int main()
{
	int example = -1;
	std::string ipath;
	
	bool show = true;

	//Checking if system has a CUDA capable device
	Cuvi32s device = cuvi::device::getCurentDevice();
	if (device < 0) {
		cout << "Error: None or Unsupported CUDA Device Found" << endl;
		system("pause");
		return 0;
	}


	
	cout<<"Enter example number to run:"<<endl<<endl;
	cout<<"1: Demosaic DFPD\n"
		  "2: Low Light\n"
		  "3: Underwater Enhancement\n"
		  "4: Color Pick \n"
		  "5: Feature Detection\n"
		  "6: Dehaze\n"
		  "7: Resize\n"
		  "8: Color Conversion\n"
	   	  "9: 2D Filtering\n"
		  "10: Haar Transform\n"
		  "11: Taper\n"
		  "12: Mean\n"
		  "0: Exit"<<endl<<endl;

	if(cin>>example)
	{
		
		switch (example)
		{
			case 0:
				exit(EXIT_SUCCESS);
				break;
			case 1:      
				ipath = "datasets/demosaic.tif";
				Demosaic(ipath, show, true, CUVI_BAYER_RGGB);
				break;
			case 2:
				ipath = "datasets/lowlight.jpg";
				LowLight(ipath, show);
				break;
			case 3:
				ipath = "datasets/underwater.jpg";
				Underwater(ipath, show);
				break;
			case 4:
				ipath = "datasets/colorpick.jpg";
				ColorPick(ipath, show, CUVI_COLOR_RED,20.0);
				break;
			case 5:
				ipath = "datasets/features.jpg";
				FeatureSelection(ipath, show, 20); 
				break;
			case 6:
				ipath = "datasets/dehaze.jpg";
				Dehaze(ipath, show);
				break;
			case 7:
				ipath = "datasets/flip.jpg";
				Resize(ipath,show);
				break;
			case 8:
				ipath = "datasets/colorconvert.jpg";
				ColorConvert(ipath, show);
				break;
			case 9:
				ipath = "datasets/filter.jpg";
				Filter(ipath,show);
				break;
			case 10:
				ipath = "datasets/flip.jpg";
				Haar(ipath, show);
				break;
			case 11:
				ipath = "datasets/taper.jpg";
				Taper(ipath, show);
				break;
			case 12:
				ipath = "datasets/mean.jpg";
				Mean(ipath, show);
				break;
			default:
				cout<<"Error: Invalid Example Number"<<endl;
				while (getchar() != 'e')
					cout << "Enter 'e' to Exit " << endl;
				break;
		}
	}
	else
	{
			cout<<"Error: Invalid Example Number"<<endl;
	}

		system("pause");
		return 0;
}
