%% Feature Extraction Using SURF
%
% Object Recognition using Speeded-Up Robust Features (SURF) is composed 
% of three steps: feature extraction, feature description, and feature matching.
% This example performs feature extraction, which is the first step of the
% SURF algorithm. The algorithm used here is based on the OpenSURF library implementation.
% This example shows how you can use GPU Coder(TM) to solve this compute 
% intensive problem through CUDA(R) code generation. 

% Copyright 2017-2019 The MathWorks, Inc.

%% Prerequisites
% * CUDA enabled NVIDIA(R) GPU with compute capability 3.2 or higher.
% * NVIDIA CUDA toolkit and driver.
% * Image Processing Toolbox(TM) for reading and displaying images.
% * Environment variables for the compilers and libraries. For information 
% on the supported versions of the compilers and libraries, see 
% <docid:gpucoder_gs#mw_aa8b0a39-45ea-4295-b244-52d6e6907bff
% Third-Party Products>. For setting up the environment variables, see 
% <docid:gpucoder_gs#mw_453fbbd7-fd08-44a8-9113-a132ed383275
% Environment Variables>.

%% Create a Folder and Copy Relevant Files
%
% The following line of code creates a folder in your current working folder (pwd) 
% and copies all the relevant files into this folder. If you do not want to perform 
% this operation or if you cannot generate files in this folder, change your 
% current working folder.
gpucoderdemo_setup('gpucoderdemo_feature_extraction');

%% Verify GPU Environment
% To verify that the compilers and libraries necessary for running this example
% are set up correctly, use the <docid:gpucoder_ref#mw_0957d820-192f-400a-8045-0bb746a75278 coder.checkGpuInstall>
% function.
envCfg = coder.gpuEnvConfig('host');
envCfg.BasicCodegen = 1;
envCfg.Quiet = 1;
coder.checkGpuInstall(envCfg);

%% Feature Extraction
%
% Feature extraction is a fundamental step in any object recognition algorithm. 
% It refers to the process of extracting useful information referred to as
% _features_ from an input image. The extracted features must be representative 
% in nature, carrying important and unique attributes of the image.
%
% The <matlab:edit(fullfile(matlabroot,'toolbox','gpucoder','gpucoderdemos','gpucoderdemo_feature_extraction','SurfDetect.m')) SurfDetect.m>
% function is the main entry-point, that performs feature extraction. 
% This function accepts an 8-bit RGB or an 8-bit grayscale image as the input. The output returned is an array 
% of extracted interest points. This function is composed of the following
% function calls, which contain computations suitable for GPU parallelization: 
%
% * The <matlab:edit(fullfile(matlabroot,'toolbox','gpucoder','gpucoderdemos','gpucoderdemo_feature_extraction','Convert32bitFPGray.m')) Convert32bitFPGray.m>
% function converts an 8-bit RGB image to an 8-bit grayscale image. If the
% input provided is already in the 8-bit grayscale format, skip this step. 
% After this step, the 8-bit grayscale image is converted to a 32-bit
% floating-point representation for enabling fast computations on the GPU.
%
% * The <matlab:edit(fullfile(matlabroot,'toolbox','gpucoder','gpucoderdemos','gpucoderdemo_feature_extraction','MyIntegralImage.m')) MyIntegralImage.m>
% function calculates the integral image of the 32-bit floating-point grayscale image
% obtained in the previous step. The integral image is useful for
% simplifying finding the sum of pixels enclosed within
% any rectangular region of the image. Finding the sum of pixels helps in 
% improving the speed of convolutions performed in the next step.
%
% * The <matlab:edit(fullfile(matlabroot,'toolbox','gpucoder','gpucoderdemos','gpucoderdemo_feature_extraction','FastHessian.m')) FastHessian.m>
% function performs convolution of the image with box filters of different
% sizes and stores the computed responses. For this example, use these
% parameters:
%
%      Number of Octaves: 5
%
%      Number of Intervals: 4
%
%      Threshold: 0.0004
%
%      Filter Sizes: Octave 1 -  9,  15,  21,  27
%
%                    Octave 2 - 15,  27,  39,  51
%
%                    Octave 3 - 27,  51,  75,  99
%
%                    Octave 4 - 51,  99, 147, 195
%
%                    Octave 5 - 99, 195, 291, 387
%
% * The <matlab:edit(fullfile(matlabroot,'toolbox','gpucoder','gpucoderdemos','gpucoderdemo_feature_extraction','NonMaxSuppression_gpu.m')) NonMaxSuppression_gpu.m>
% function performs non-maximal suppression to filter out only the useful
% interest points from the responses obtained earlier. To generate a kernel 
% that uses the |atomicAdd| operation, use the 
% <docid:coder_ref#br49spw-1 coder.ceval> construct. Because this construct 
% is not compatible when invoked directly from MATLAB(R), there are two 
% different function calls. The 
% <matlab:edit(fullfile(matlabroot,'toolbox','gpucoder','gpucoderdemos','gpucoderdemo_feature_extraction','NonMaxSuppression_gpu.m')) NonMaxSuppression_gpu.m> 
% function is invoked when GPU code generation is enabled and the 
% <matlab:edit(fullfile(matlabroot,'toolbox','gpucoder','gpucoderdemos','gpucoderdemo_feature_extraction','NonMaxSuppression.m')) NonMaxSuppression.m> 
% is invoked when you are executing the algorithm directly in MATLAB.
%
% * The <matlab:edit(fullfile(matlabroot,'toolbox','gpucoder','gpucoderdemos','gpucoderdemo_feature_extraction','OrientationCalc.m')) OrientationCalc.m> 
% function calculates and assigns orientation to the interest points
% in the previous step.
%
% The final result is an array of interest points where an
% interest point is a structure that consists of these fields: 
%
%      x, y (coordinates), scale, orientation, Laplacian
%

%% Read Input Image
%
% Read an input image into MATLAB by using the |imread| function.
%
imageFile = 'peppers.png';
inputImage = imread(imageFile);
imshow(inputImage);

%% Generate CUDA MEX for the Function
%
% To generate CUDA MEX for the |SurfDetect| function, create a GPU Coder 
% configuration object, and then run the <docid:coder_ref#br46oyi-1 codegen> function.
%
cfg = coder.gpuConfig('mex');
evalc('codegen -config cfg SurfDetect -args {inputImage}');

%% Run the MEX Function on a GPU
%
% You can invoke the generated MEX function |SurfDetect_mex| to run on a
% GPU:
%
disp('Running GPU Coder SURF');
interestPointsGPU = SurfDetect_mex(inputImage);
fprintf('    GPU Coder SURF found: %d interest points\n',length(interestPointsGPU));

%% Depict the Extracted Interest Points
%
% The output |interestPointsGPU| is an array of extracted interest points. 
% These interest points are depicted over the input image in a figure 
% window.
%

DrawIpoints(imageFile, interestPointsGPU);

%% Cleanup
% Remove files, perform clean up, and return to the original folder.
cleanup

displayEndOfDemoMessage(mfilename)
%% References
% 
% # Notes on the OpenSURF Library by Christopher Evans.
% # SURF: Speeded-Up Robust Features by Herbert Bay, Tinne Tuytelaars, and
% Luc Van Gool.
% 
