CANNYEDGEMAP_BASE - Base function for CANNYEDGEMAP.

Contents

Syntax

   map = CANNYEDGEMAP_BASE(gx, gy, der, mag, or, hyst, ratio);
   [map, mag, or] = CANNYEDGEMAP_BASE(gx, gy, der, mag, or, hyst, ratio);

Acknowledgment

This function is a copy/paste and crop of several proposed functions implemented for thresholding, non maximum suppression and thinning of edge maps. Specifically: VISTA functions, Kovesi's library.

See also

Ressembles: CANNYEDGEMAP, CANNYEDGE_BASE, EDGECORNER_BASE, ROTHWELLEDGE_BASE, CONGRUENCYEDGE_BASE, COMPASSEDGE_BASE, ANISOEDGE_BASE, ELDERZUCKEREDGE_BASE, KOETHEDGE_BASE, SDGDEDGE_BASE, PETROUEDGE_BASE.

Function implementation

function [map,varargout] = cannyedgemap_base(gx, gy, der, mag, or, hyst, ratio)

retrieve the gradient magnitude if not passed as an argument

C = size(gx,3);

if isempty(mag)
    mag = zeros(size(gx(:,:,1)));
    for c = 1:C
        mag = max(mag, sqrt((gx(:,:,c).*gx(:,:,c)) + (gy(:,:,c).*gy(:,:,c))));
    end
    magmax = max(mag(:));
    if magmax>0
        mag = mag / magmax;   % normalize
    end
end

retrieve the gradient orientation if not passed as an argument

if isempty(or)
    or = atan2(gy,gx);
end

if isempty(ratio),   ratio = [1/3 0.08];  end;

if strcmpi(der,'vista'),    nbins = 128;
else                        nbins = 64;   end;

deal with the case we have features for each channel, ie. channels are in fact processed separately

C = size(mag,3);
if C>1
    map = zeros(size(mag));
    if nargout>=2,  varargout{1} = zeros(size(mag));
        if nargout==3,  varargout{2} = zeros(size(mag));  end
    end
    for c = 1:C
        [map(:,:,c), m, o] = cannyedgemap_base(gx(:,:,c), gy(:,:,c), der, ...
            mag(:,:,c), or(:,:,c), hyst, ratio);
    end
    if nargout>=2,  varargout{1}(:,:,c) = m;
        if nargout==3,  varargout{2}(:,:,c) = o;  end
    end
    return
end

compute hystheresis thresholds

if isempty(hyst)
    hyst = selecthyst(mag, ratio, nbins);
elseif hyst(1) > hyst(2)    % swap values
    tmp = hyst(1); hyst(1) = hyst(2); hyst(2) = tmp;
end

estimations

switch der

    case 'kovesi'
        radius = 1;
        % note that the orientation in Kovesi's function is defined with
        % respect to the standard Matlab axis
        or = (or+pi/2);  or(or>pi) = or(or>pi) - 2*pi;
        or = -or;

        or = or.*~(or<0) + (or+pi).*(or<0); % map angles to 0-pi
        or = or * 180/pi;   % do convert to degrees
        map = nonmaxsup_kovesi(mag, or, radius);
        % hyst = max(mag(:)) * hyst; % mag already normalized
        map = hysthresh_kovesi(map, hyst);

    case {'matlab','vista'}
        map = hystnonmaxsup_matlab(gx, gy, mag, hyst );
end

outputs

if nargout>=2,  varargout{1} = mag;
    if nargout==3,  varargout{2} = or;  end
end
end  % end of cannyedgemap_base

Subfunctions

SELECTHYST - Hysteresis thresholds' selection.

%--------------------------------------------------------------------------
function hyst = selecthyst(mag, ratio, nbins)
[m,n] = size(mag(:,:,1));
[counts,x] = imhist(mag, nbins);
pixratio = ratio(1);
if pixratio<0
    meanx = sum(counts.*x)/sum(counts);
    stdx = sqrt(sum(counts.*((x-meanx).^2))/sum(counts));
    pixratio = 1-(meanx+2*stdx)/max(x);
end
thresratio = ratio(2);
highThresh = find(cumsum(counts) > (1-pixratio)*m*n,1,'first') / nbins;
lowThresh = thresratio*highThresh;
hyst = [lowThresh highThresh];
end % end of selecthyst

HYSTNONMAXSUP_MATLAB - Non-maximum supression.

%--------------------------------------------------------------------------
function map = hystnonmaxsup_matlab(gx, gy, mag, hyst)
[m,n] = size(gx(:,:,1));                                               %#ok
map = zeros(size(mag)); % map = repmat(false, m, n);

% we accrue indices which specify ON pixels in strong map; the array e will
% become the weak edge map
idxStrong = [];
for dir = 1:4
    idxLocalMax = findmaxima_matlab(dir,gx,gy,mag);
    idxWeak = idxLocalMax(mag(idxLocalMax) > hyst(1));
    map(idxWeak)=1;
    idxStrong = [idxStrong; idxWeak(mag(idxWeak) > hyst(2))];          %#ok
end

rstrong = rem(idxStrong-1, m)+1;
cstrong = floor((idxStrong-1)/m)+1;
% apply connectivity rules
map = bwselect(map, cstrong, rstrong, 8);
% thin double (or triple) pixel wide contours
map = bwmorph(map, 'thin', 1);
end % end of hystnonmaxsup_edge

FINDMAXIMA_MATLAB - This function helps with the non-maximum supression in the Canny edge detector. It is a copy paste of the original FINDMAXIMA function implemented in Matlab.

Inputs: direction : the index of which direction the gradient is pointing, read from the diagram below. direction is 1, 2, 3, or 4.

|ix| : input image filtered by derivative of Gaussian along X
|iy| : input image filtered by derivative of Gaussian along Y
|mag| : the gradient magnitude image

Output: idxLocalMax : index of the local maxima in the gradient magnituce image map

%--------------------------------------------------------------------------
function idxLocalMax = findmaxima_matlab(direction,ix,iy,mag)
[m,n] = size(mag(:,:,1));

find the indices of all points whose gradient (specified by the vector (ix,iy)) is going in the direction we're looking at.

switch direction
    case 1
        idx = find((iy<=0 & ix>-iy)  | (iy>=0 & ix<-iy));
    case 2
        idx = find((ix>0 & -iy>=ix)  | (ix<0 & -iy<=ix));
    case 3
        idx = find((ix<=0 & ix>iy) | (ix>=0 & ix<iy));
    case 4
        idx = find((iy<0 & ix<=iy) | (iy>0 & ix>=iy));
end

exclude the exterior pixels

if ~isempty(idx)
    v = mod(idx,m);
    idx(v==1 | v==0 | idx<=m | (idx>(n-1)*m)) = [];
end

ixv = ix(idx);
iyv = iy(idx);
gradmag = mag(idx);

do the linear interpolations for the interior pixels

switch direction
    case 1
        d = abs(iyv./ixv);
        gradmag1 = mag(idx+m).*(1-d) + mag(idx+m-1).*d;
        gradmag2 = mag(idx-m).*(1-d) + mag(idx-m+1).*d;
    case 2
        d = abs(ixv./iyv);
        gradmag1 = mag(idx-1).*(1-d) + mag(idx+m-1).*d;
        gradmag2 = mag(idx+1).*(1-d) + mag(idx-m+1).*d;
    case 3
        d = abs(ixv./iyv);
        gradmag1 = mag(idx-1).*(1-d) + mag(idx-m-1).*d;
        gradmag2 = mag(idx+1).*(1-d) + mag(idx+m+1).*d;
    case 4
        d = abs(iyv./ixv);
        gradmag1 = mag(idx-m).*(1-d) + mag(idx-m-1).*d;
        gradmag2 = mag(idx+m).*(1-d) + mag(idx+m+1).*d;
end
idxLocalMax = idx(gradmag>=gradmag1 & gradmag>=gradmag2);
end % end of findmaxima_edge

HYSTHRESH_KOVESI - Perform hysteresis thresholding of an image.

Inputs: im : image to be thresholded (assumed to be non-negative)

|hyst=[T1,T2]| : upper and lower threshold values |(T1 > T2)|

Output: bw : the thresholded image (containing values 0 or 1)

%--------------------------------------------------------------------------
function bw = hysthresh_kovesi(im, hyst)

T1 = hyst(1); T2=hyst(2);
if T1 < T2    % T1 and T2 reversed - swap values
    tmp = T1;
    T1 = T2;
    T2 = tmp;
end

aboveT2 = im > T2;   % edge points above lower threshold.
[aboveT1r, aboveT1c] = find(im > T1);  % Row and colum coords of points
% above upper threshold.

% Obtain all connected regions in aboveT2 that include a point that has a
% value above T1
bw = bwselect(aboveT2, aboveT1c, aboveT1r, 8);
end % end of hysthresh_kovesi

NONMAXSUP_KOVESI - Function for performing non-maxima suppression on an image using an orientation image. It is assumed that the orientation image gives feature normal orientation angles in degrees (0-180).

Inputs: inimage : image to be non-maxima suppressed.

|orient| : image containing feature normal orientation angles in degrees
   (0-180), angles positive anti-clockwise.
|radius| : distance in pixel units to be looked at on each side of each
   pixel when determining whether it is a local maxima or not; this
   value cannot be less than 1; suggested value about 1.2 - 1.5.

Output im : non maximally suppressed image.

%--------------------------------------------------------------------------
function im = nonmaxsup_kovesi(inimage, orient, radius)
[rows,cols] = size(inimage);
im = zeros(rows,cols); % preallocate memory for output image

iradius = ceil(radius);

precalculate x and y offsets relative to centre pixel for each orientation angle

angle = (0:180).*pi/180; % array of angles in 1 degree increments (but in radians).
xoff = radius*cos(angle); % x and y offset of points at specified radius and angle
yoff = radius*sin(angle); % from each reference position.

hfrac = xoff - floor(xoff); % fractional offset of xoff relative to integer location
vfrac = yoff - floor(yoff); % fractional offset of yoff relative to integer location

orient = fix(orient)+1; % orientations start at 0 degrees but arrays start with index 1.

now run through the image interpolating grey values on each side of the centre pixel to be used for the non-maximal suppression.

for row = (iradius+1):(rows - iradius)
    for col = (iradius+1):(cols - iradius)

        or = orient(row,col);   % Index into precomputed arrays
        x = col + xoff(or);     % x, y location on one side of the point in question
        y = row - yoff(or);

        fx = floor(x);          % Get integer pixel locations that surround location x,y
        cx = ceil(x);
        fy = floor(y);
        cy = ceil(y);
        tl = inimage(fy,fx);    % Value at top left integer pixel location.
        tr = inimage(fy,cx);    % top right
        bl = inimage(cy,fx);    % bottom left
        br = inimage(cy,cx);    % bottom right

        upperavg = tl + hfrac(or) * (tr - tl);  % Now use bilinear interpolation to
        loweravg = bl + hfrac(or) * (br - bl);  % estimate value at x,y
        v1 = upperavg + vfrac(or) * (loweravg - upperavg);

        if inimage(row, col) > v1 % We need to check the value on the other side...

            x = col - xoff(or);     % x, y location on the `other side' of the point in question
            y = row + yoff(or);

            fx = floor(x);
            cx = ceil(x);
            fy = floor(y);
            cy = ceil(y);
            tl = inimage(fy,fx);    % Value at top left integer pixel location.
            tr = inimage(fy,cx);    % top right
            bl = inimage(cy,fx);    % bottom left
            br = inimage(cy,cx);    % bottom right
            upperavg = tl + hfrac(or) * (tr - tl);
            loweravg = bl + hfrac(or) * (br - bl);
            v2 = upperavg + vfrac(or) * (loweravg - upperavg);

            if inimage(row,col) > v2            % This is a local maximum.
                im(row, col) = inimage(row, col); % Record value in the output
                % image.
            end

        end
    end
end

finally thin the 'nonmaximally suppressed' image by pointwise multiplying itself with a morphological skeletonization of itself.

skel = bwmorph(im,'skel',Inf);
im = im.*skel;
end  % end of nonmaxsup_kovesi