
%% Sub-pixel Accurate Edge Detection
%
% Computes sub-pixel accurate edge locations for a given image by applying 
% classic non-maximum suppression (with quadratic interpolation) and
% subsequently performing quadratic interpolation to locate the edge
% maximum along a 'surviving' edge-gradient.
%
% Uses GPU-acceleration for parallelized edge refinement.
%
% References:
%1]Devernay, Frédéric. "A non-maxima suppression method for edge detection 
%  with sub-pixel accuracy." (1995).


%% Input Arguments

% img - Input grayscale or color image.
%       If color, applies gaussian filter on each channel seperately.
%       NxMx(3) matrix.


%% Output Arguments

% edge - Non-maximally-suppressed gradient magnitude map
%        NxMx(3) matrix.

% xDelta - Sub-pixel offset in x-direction (column)
%          NxMx(3) matrix.

% yDelta - Sub-pixel offset in y-direction (row)
%          NxMx(3) matrix.


%% Sub-pixel Accurate Edge Detector

function [edge, xDelta, yDelta, varargout] = subpixel_edge(img)

%general constants
GAUSS_SIGMA  = 2;
NCOLOR_COMPS = 3;

NREQ_OUTPUTS = 3;
NOPT_OUTPUTS = 2;

%add opencl tools
addpath('./subpixel_edge_gpu/');

%parse input parameters
[img, nRows, nCols, isColor] = input_parser(img);


%--------------------------------------------------
%		 <step 1>
%--------------------------------------------------
%description: apply edge operator (high-pass filter)

[Ix, Iy] = deriche_gaussian_filter(img, GAUSS_SIGMA, 'first');


%--------------------------------------------------
%		 <step 2>
%--------------------------------------------------
%description: compute sub-pixel accurate edges

if isColor
    
    edge   = zeros(nRows, nCols, NCOLOR_COMPS);
    xDelta = zeros(nRows, nCols, NCOLOR_COMPS);
    yDelta = zeros(nRows, nCols, NCOLOR_COMPS);
    
    for i = 1:NCOLOR_COMPS,    
        [edge(:,:,i), xDelta(:,:,i), yDelta(:,:,i)] = subpixel_edge_gpu( Ix(:,:,i), Iy(:,:,i) );
    end

else
    
    [edge, xDelta, yDelta] = subpixel_edge_gpu(Ix, Iy);
    
end


%package output parameters
nargoutchk(NREQ_OUTPUTS, NREQ_OUTPUTS+NOPT_OUTPUTS);
nOptOuts = nargout - NREQ_OUTPUTS;
if nOptOuts >= 1
    varargout{1} = Ix;
end
if nOptOuts >= 2
    varargout{2} = Iy;
end

end % end of subpixel edge


%% Input Parser

function [img, nRows, nCols, isColor] = input_parser(img)

% parse input arguments
p = inputParser;
p.addRequired('img', @(x) ...
				validateattributes(x,{'numeric'},{'real','nonnan'})); 		       
p.parse(img);
inputs = p.Results;
img    = inputs.img;

[nRows, nCols, nComps] = size(img);

if nComps == 3
    isColor = true;
elseif nComps == 1
    isColor = false;
else
    error('subPixlEdge:InvalidImg','Input image should be gray-scale or color image');
end  

end















