// Raytrix API LF Example 02

#include <SDKDDKVer.h>
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <conio.h>

// The Raytrix Core library
#include "Rx.Core\RxCore.h"
#include "Rx.Core\RxPixel.h"

// The Raytrix Light Field API
#include "Rx.ApiLF\Rx.ApiLF.h"

// Include CLUViz Tool DLL to display/load/save images.
#include "Rx.CluViz.Core.CluVizTool\CvCluVizTool.h"

using namespace Rx;
using namespace Rx::ApiLF;

int _tmain(int argc, _TCHAR* argv[])
{
	int iView = 0;
	unsigned uImgID = 0;
	double dT = 0.0;
	CRxString sxFile, sxFile2;
	CRxImage xImage;

	// Initialize CLUVizTool
	printf("Initializing CLUVizTool...\n");
	if(!CLUViz::Tool::Init())
	{
		printf("... error.\n");
		return -1;
	}

	// Create an image view
	printf("Creating image view window...\n");
	if(!CLUViz::Tool::CreateViewImage( iView, 600, 100, 800, 600, "Image" ))
	{
		printf("... error.\n");
		return -1;
	}

	try
	{
		// Initialize Raytrix Library
		printf("Initializing API...\n");
		RX_TRY( RxInit(true, "", "", 0) );

		// Automatic CUDA device selection if no device ID or a negative device ID is given.
		printf("Selecting CUDA device...\n");
		RX_TRY( RxCudaSelectDevice() );
		
		// Construct path to ray image
		sxFile = "..\\ExampleImages\\Demo_01.ray";

		// Load a ray image
		printf("Loading image '%s'...\n", sxFile.ToCString() );
		RX_TRY( RxRayLoad(uImgID, sxFile) );

		// Bind the loaded ray image. This copies the ray image to the CUDA device.
		printf("Binding image...\n");
		RX_TRY( RxRayBind( uImgID ) );

		// Deactivate white image to draw grid into raw image
		RX_TRY(RxSetPar( EPar::Ray_ApplyWhiteImage, 0U ));

		// Process image with no white image
		RX_TRY(RxPreProcess());

		// Draw grid
		RX_TRY(RxGrid());

		// Get grid image from CUDA device
		RX_TRY( RxGetImage( EImgID::RayGrid, xImage ) );

		// Display the image
		CLUViz::Tool::ViewSetImage( iView, &xImage );

		// Wait for user to press any key.
		printf("Press any key...\n");
		_getch();

		///////////////////////////////////////////////////////////////
		// Evaluate scene depth.

		// Set Pre-Process Parameters
		RX_TRY(RxSetPar(EPar::PreProc_DenoiseNLM_Enable, 1U));
		RX_TRY(RxSetPar(EPar::PreProc_DenoiseNLM_FilterDia, 4U));
		RX_TRY(RxSetPar(EPar::PreProc_DenoiseNLM_NoiseLevel, 0.15));
		RX_TRY(RxSetPar(EPar::PreProc_DenoiseNLM_BlendFactor, 0.2));

		RX_TRY(RxSetPar(EPar::PreProc_Sharp1_Enable, 1U));
		RX_TRY(RxSetPar(EPar::PreProc_Sharp1_BlurStdDev, 2.5));
		RX_TRY(RxSetPar(EPar::PreProc_Sharp1_Factor, 1.8));


		// Select new depth path algorithm
		RX_TRY(RxSetPar(EPar::Depth_Algo, "depth_path"));
		RX_TRY(RxSetPar(EPar::Depth_Path_NearResLevel, 3U));

		// Set depth parameters for different lens types
		// Lens Type 1
		RX_TRY(RxSetPar(EPar::Depth_Path_LensTypeIdx, 0U));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_PixelStep, 0.5));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_MinStdDev, 0.1));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_MinCor, 0.97));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_MinPolyParAbs, 0.05));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_MaxPolyCtrDelta, 0.5));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_PatchDia, 5U));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_PatchStride, 4U));
		// Lens Type 2
		RX_TRY(RxSetPar(EPar::Depth_Path_LensTypeIdx, 1U));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_PixelStep, 0.5));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_MinStdDev, 0.1));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_MinCor, 0.97));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_MinPolyParAbs, 0.05));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_MaxPolyCtrDelta, 0.5));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_PatchDia, 4U));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_PatchStride, 3U));
		// Lens Type 3
		RX_TRY(RxSetPar(EPar::Depth_Path_LensTypeIdx, 2U));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_PixelStep, 0.5));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_MinStdDev, 0.1));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_MinCor, 0.97));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_MinPolyParAbs, 0.05));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_MaxPolyCtrDelta, 0.5));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_PatchDia, 3U));
		RX_TRY(RxSetPar(EPar::Depth_Path_LT_PatchStride, 2U));

		// Depth Fuse Parameters
		RX_TRY(RxSetPar(EPar::Depth_Fuse_MedianRad, 0U));
		RX_TRY(RxSetPar(EPar::Depth_Fuse_MeanRad, 0U));
		RX_TRY(RxSetPar(EPar::Depth_Fuse_MeanMaxBlackPart, 0.2));

		// Depth Fill Parameters
		RX_TRY(RxSetPar(EPar::Depth_Fill_Enabled, 1U));
		RX_TRY(RxSetPar(EPar::Depth_Fill_IterCnt, 4U));
		RX_TRY(RxSetPar(EPar::Depth_Fill_IterSize, 4U));
		RX_TRY(RxSetPar(EPar::Depth_Fill_Complete, 1U));
		RX_TRY(RxSetPar(EPar::Depth_Fill_MedianRad, 1U));
		RX_TRY(RxSetPar(EPar::Depth_Fill_MeanRad, 1U));

		// Depth Fill Non-Local-Mean denoise filter
		RX_TRY(RxSetPar(EPar::Depth_Fill_NLM_Enabled, 1U));
		RX_TRY(RxSetPar(EPar::Depth_Fill_NLM_FilterDia, 4U));
		RX_TRY(RxSetPar(EPar::Depth_Fill_NLM_NoiseLevel, 0.15));
		RX_TRY(RxSetPar(EPar::Depth_Fill_NLM_BlendFactor, 0.2));


		// The result of this operation is stored in the 
		// internal image RxImg_Depth
		printf("Calculating scene depth from ray image...");
		RX_TRY( RxDepth( false ) );

		// Get the execution time of the previously executed CUDA algorithm
		dT = RxGetLastExecTime();
		printf(" %g ms\n", dT );


		// Activate white image to refocus raw image
		RX_TRY(RxSetPar( EPar::Ray_ApplyWhiteImage, 1U ));

		// Process image with white image
		RX_TRY(RxPreProcess());

				// Focus ray image on the depth surface stored in the 
		// internal image RxImg_Depth. At places where no depth could be calculated
		// use the given depth 0.5.
		printf("Focusing on calculated scene depth...");
		RX_TRY( RxSetPar( EPar::Focus_Focus, 0.5 ));
		RX_TRY( RxFocusOnSurface( ) );

		// Get the execution time of the previously executed CUDA algorithm
		dT = RxGetLastExecTime();
		printf(" %g ms\n", dT );

		// Get focused image from CUDA device
		printf("Reading image from CUDA device...\n");
		RX_TRY( RxGetImage( EImgID::Focus, xImage ) );

		// Display the image
		printf("Displaying focused image...\n");
		CLUViz::Tool::ViewSetImage( iView, &xImage );

		// Save image
		sxFile2 = "C:\\Users\\Public\\Pictures\\Demo_Focused.png";
		printf("Saving focused image as '%s'...\n", sxFile2.ToCString() );
		CLUViz::Tool::WriteImage( sxFile2.ToCString(), &xImage );

		// Wait for user to press any key.
		printf("Press any key...\n");
		_getch();

		// Make sure depth image contains virtual depth values
		RX_TRY(RxSetPar(EPar::Depth_ImageType, unsigned(EDepthImageType::Virtual)));

		// Now update the depth image to set it to the new depth image type
		RX_TRY(RxUpdateImage( EImgID::Depth ));

		// Get depth image from CUDA device
		printf("Reading depth image from CUDA device...\n");
		RX_TRY( RxGetImage( EImgID::Depth , xImage ) );

		// Now write out values of depth image as comma separated list
		int iX, iY, iIdx;
		CRxImageFormat xF = xImage.GetFormat();

		if ( xF.m_ePixelType == EPixelType::Lum && xF.m_eDataType == EDataType::Float )
		{
			char pcLine[512];
			CRxString sxData;
			TPixel_L_f *pfData = (TPixel_L_f *) xImage.GetDataPtr();

			printf("Preparing depth data as comma separated list...");
			// Write image size as first two elements
			sprintf_s(pcLine, "%d, %d\n", xF.m_iWidth, xF.m_iHeight);
			sxData << pcLine;

			for ( iY = 0, iIdx = 0; iY < xF.m_iHeight; ++iY )
			{
				printf(".");
				for ( iX = 0; iX < xF.m_iWidth; ++iX, ++iIdx )
				{
					// Create text line with X, Y, Virtual Depth
					sprintf_s(pcLine, "%d, %d, %g\n", iX, iY, (double) pfData[iIdx].value[0]);
					sxData << pcLine;
				}
			}

			printf("\n");
			printf("Writing data to '%s'...", "C:\\Users\\Public\\Pictures\\DepthData.txt");

			int iErr = 0;
			FILE *pFile = 0;
			if ( (iErr = fopen_s(&pFile, "C:\\Users\\Public\\Pictures\\DepthData.txt", "w")) == 0 )
			{
				fwrite(sxData.ToCString(), 1, sxData.Size(), pFile);
				fclose(pFile);
				printf("ok\n");
			}
			else
			{
				printf("error\n");
			}
		}


		// Transform depth image to relative depth values in range [0,1]
		// which is optimal for displaying the depth image.
		RX_TRY(RxSetPar(EPar::Depth_ImageType, unsigned(EDepthImageType::Relative)));
		// Now update the depth image to set it to the new depth image type
		RX_TRY(RxUpdateImage( EImgID::Depth ));

		// Get depth image from CUDA device
		printf("Reading depth image from CUDA device...\n");
		RX_TRY( RxGetImage( EImgID::Depth , xImage ) );

		// Convert the image format from float to unsigned short. This is needed to save the image.
		// Note that 16bit images can only be saved as PNG files.
		Rx::CRxImage xNewImage;
		RX_TRY( RxConvertImage( xNewImage, xImage, Rx::EPixelType::Lum, Rx::EDataType::UShort) );

		// Display the image. Here you can also use the float image version.
		printf("Displaying depth image...\n");
		CLUViz::Tool::ViewSetImage( iView, &xImage );

		// Save image
		sxFile2 = "C:\\Users\\Public\\Pictures\\Demo_Depth.png";
		printf("Saving depth image as '%s'...\n", sxFile2.ToCString() );
		CLUViz::Tool::WriteImage( sxFile2.ToCString(), &xNewImage );

		printf("finished.\n");

		// Wait for user to press any key.
		printf("Press any key...\n");
		_getch();

	}
	catch( int iError )
	{
		printf("Press any key to end program...\n");
		_getch();
		return iError;
	}

	// Close Raytrix API, free all memory on host and CUDA device
	// and close all open cameras.
	printf("Closing Raytrix API...\n");
	RxFinalize();

	// Close all CLUViz Windows and free internally used memory
	printf("Closing CLUViz Windows...\n");
	CLUViz::Tool::Finalize();


	return 0;
}

