function [locs, descriptors, im] = features(image, detector, descriptor, extra)
% [locs, descriptors, im] = features(image, detector, descriptor, extra)
%
% This function reads an image and returns its  keypoints and descriptors
%   Input parameters:
%     image:  image matrix or image file name. If no image or file is supplied a
%           camera will be used instead
%     detector:   Feature Detectors {"SIFT", "SURF", "ORB",  "AKAZE", "KAZE", "BRISK",
%						   "FAST", "MSER", "GFTT", "HARRIS", "AGAST","SimpleBlob", "STAR", "MSD",  "TBMR", "HARRISLP"}
%                 if ommited uses default SIFT
%     descriptor:  Feature Descriptors scalar: {"SIFT", "SURF", "DAISY",  "VGG"}     
%				   binary: {"BRISK", "ORB",  "AKAZE/MLDB", "MSURF", "KAZE", "BRIEF", "FREAK", "LUCID", "LATCH", "BOOST", "BEBLID"}
%                 if ommited it uses the detector type as descriptor
%     extra: string with extra parameters (see below) '-d -s --nf XXX --ht XXX  --ft XXX --scale XXX'
%                   -d  => force display when a file is processed 
%                   -s  => display only keyPoints when display is active (scale/orientation is ommited) 
%                   -w <delay>   => file mode display time in ms (0 until exit) (def: 5000ms)
%                   -h, --help  => shows help
%           Features Detector Specific Parameters (OPTIONAL) 
%                   --nf Number_Features (int) (for SIFT, ORB, GFFT, HARRIS)
%                   --ht Hessian_Threshold (float) (for SURF)
%                   --ft FAST_hreshold (int) (for FAST)
%                   --scale SCALE (float) (for BEBLID, VGG descriptors)
%
%   Returned:
%     locs: K-by-5 matrix, in which each row has the 5 values for a
%         keypoint location (column, row, scale, orientation, response).  The 
%         orientation is in the range [-PI, PI] radians.
%     descriptors: a K-by-N matrix, where each row gives an invariant
%         descriptor for one of the K keypoints.  The descriptor is a vector
%         of N values depending on descriptor type (SIFT normalized to unit length)
%     im: the image array when the camera is used, or the input image

descriptors=[];
locs=[];
im = [];

if nargin >0
    if ischar(image)
        imageFile = image;
    elseif ismatrix(image)
        imageFile = 'tmp_image.png';
        imwrite(image, imageFile);
    else   
        disp('Wrong parameters'); % error
        return;
    end
end

delete tmp.key;

% Call features executable
if ismac
    system('chmod +x features');
    command = './features ';
elseif isunix 
    system('chmod +x features_lx');
    command = './features_lx ';
else
    command = 'features.exe ';
end

if nargin <1
    % process images from camera, deafult SIFT detector and descriptor
    command = [command, ' > tmp.key'];
elseif nargin <2
    % process supplied image File,  default SIFT detector and descriptor
    % You can include extra parameters in the same string (see
    % below for features.exe help)
    command = [command, imageFile, ' > tmp.key'];
elseif nargin <3
   % process supplied image File with an especific feature detector 
   % (tries to use same descriptor as detector)
    command = [command, imageFile, ' --feat ', detector, ' > tmp.key'];
elseif nargin <4
   % process supplied image File with an especific feature detector and descriptor
    command = [command, imageFile, ' --feat ', detector, ' --desc ', descriptor, ' > tmp.key'];
else 
   % process supplied image File with an especific feature detector and descriptor
   % with extra parameters (string)
   %  -d  => force display when a file is processed
   %  -s  => display only keyPoints when display is active (scale/orientation is ommited) 
   %  -w <delay>   => file mode display time in ms (0 until exit) (def: 5000ms)
   %  --nf Number_Features (int) (for SIFT, ORB, GFFT, HARRIS)
   %  --ht Hessian_Threshold (float) (for SURF)
   %  --ft FAST_hreshold (int) (for FAST)      
    command = [command, imageFile, ' --feat ', detector, ' --desc ', descriptor, ' ', extra, ' > tmp.key'];
end

[status,cmdout] = system(command);

% Open tmp.key 
tmp_file = fopen('tmp.key', 'r');
if tmp_file == -1
    error('Could not open file tmp.key.');
end

filename=''; num=0; len=0;
% Read header
keyword = 'filename= ';
while ~feof(tmp_file)
    text=fgetl(tmp_file);
    pos = strfind(text, keyword);
    if ~isempty(pos)
        filename = text(pos+size(keyword,2):end);
        break;
    end
end   
        
% Read number of points
keyword = 'numPoints= ';
while ~feof(tmp_file)
    text=fgetl(tmp_file);
    pos = strfind(text, keyword);
    if ~isempty(pos)
        num = str2num(text(pos+size(keyword,2):end));
        break;
    end
end   

% Read descriptor size
keyword = 'descriptorDim= ';
while ~feof(tmp_file)
    text=fgetl(tmp_file);
    pos = strfind(text, keyword);
    if ~isempty(pos)
        len = str2num(text(pos+size(keyword,2):end));
        break;
    end
end   


% Creates the two output matrices (use known size for efficiency)
locs = double(zeros(num, 5));
descriptors = double(zeros(num, len));

% Parse tmp.key
for i = 1:num
    [vector, count] = fscanf(tmp_file, '%f %f %f %f %f ', [1 5]); %col row scale ori resp 
    if count ~= 5
        error('Invalid keypoint file format');
    end
    locs(i, :) = vector(1, :);
    
    [descrip, count] = fscanf(tmp_file, '%lf', [1 len]);
    if (count ~= len)
        error('Invalid keypoint file value.');
    end
    
    % Normalize each input vector to unit length
    % descrip = descrip / sqrt(sum(descrip.^2));
    descriptors(i, :) = descrip(1, :);
end

fclose(tmp_file);

if ~isempty(filename)
    im = imread(filename);
end

end % end script
