function [accum, axis_rho, axis_theta, varargout] = ...
Hough_Grd(img, varargin)
%Detect lines in a grayscale image
%
% [accum, axis_rho, axis_theta, lineprm, lineseg, dbg_label] =
% Hough_Grd(img, grdthres, detsens)
% Hough transform for line detection based on image's gradient field.
% NOTE: Operates on grayscale images, NOT B/W bitmaps.
% NO loops involved in the implementation of Hough transform,
% which makes the operation fast.
% Able to detect the two ends of line segments.
%
% INPUT: (img, grdthres, detsens)
% img: A 2-D grayscale image (NO B/W bitmap)
% grdthres: (Optional, default is 8, must be non-negative)
% The algorithm is based on the gradient field of the
% input image. A thresholding on the gradient magnitude
% is performed before the voting process of the Hough
% transform to remove the 'uniform intensity' (sort-of)
% image background from the voting process. In other words,
% pixels with gradient magnitudes smaller than 'grdthres'
% are considered not belong to any line.
% detsens: (Optional, default is 0.08)
% A value that controls the sensitivity of line detection.
% The range of the value is from 0 to 1, open ends.
% Although the physical meaning of this parameter is not
% obvious, it controls the algorithm in a fuzzy manner.
% The SMALLER the value is, the MORE features in the image
% will be considered as lines.
%
% OUTPUT: [accum, axis_rho, axis_theta, lineprm, lineseg, dbg_label]
% accum: The result accumulation array from the Hough transform.
% The horizontal dimension is 'theta' and the vertical
% dimension is 'rho'.
% axis_rho: Vector that contains the 'rho' values for the rows in
% the accumulation array.
% axis_theta: Vector that contains the 'theta' values for the columns
% in the accumulation array.
% lineprm: (Optional)
% Parameters (rho, theta) of the lines detected. Is a N-by-2
% matrix with each row contains the parameters (rho, theta)
% for a line. The definitions of 'rho' and 'theta' are as
% the following:
% 'rho' is the perpendicular distance from the line to the
% origin of the image. 'rho' can be negative since 'theta'
% is constrained to [0, pi]. The unit of 'rho' is in pixels.
% 'theta' is the sweep angle from axis X (i.e. horizontal
% axis of the image) to the direction that is perpendicular
% to the line. The range of 'theta' is [0, pi].
% lineseg: (Optional)
% Parameters (x1, x2, y1, y2) of line segments detected.
% Lines given by (rho, theta) are infinite lines. This
% function tries to detect line segments in the raw image
% and output them as 'lineseg', which is a Ns-by-4 matrix
% with each row contains the parameters (x1, x2, y1, y2)
% that defines the two ends of a line segment.
% dbg_label: (Optional, for debug purpose)
% Labelled segmentation map from the search of peaks in
% the accumulation array
%
% EXAMPLE #1:
% rawimg = imread('TestHT_Chkbd1.bmp');
% fltr4img = [1 2 3 2 1; 2 3 4 3 2; 3 4 6 4 3; 2 3 4 3 2; 1 2 3 2 1];
% fltr4img = fltr4img / sum(fltr4img(:));
% imgfltrd = filter2( fltr4img , rawimg );
% tic;
% [accum, axis_rho, axis_theta, lineprm, lineseg] = ...
% Hough_Grd(imgfltrd, 6, 0.02);
% toc;
% figure(1); imagesc(axis_theta*(180/pi), axis_rho, accum); axis xy;
% xlabel('Theta (degree)'); ylabel('Pho (pixels)');
% title('Accumulation Array from Hough Transform');
% figure(2); imagesc(rawimg); colormap('gray'); axis image;
% DrawLines_2Ends(lineseg);
% title('Raw Image with Line Segments Detected');
%
% EXAMPLE #2:
% rawimg = imread('TestHT_Pentagon.png');
% fltr4img = [1 1 1 1 1; 1 2 2 2 1; 1 2 4 2 1; 1 2 2 2 1; 1 1 1 1 1];
% fltr4img = fltr4img / sum(fltr4img(:));
% imgfltrd = filter2( fltr4img , rawimg );
% tic;
% [accum, axis_rho, axis_theta, lineprm, lineseg] = ...
% Hough_Grd(imgfltrd, 8, 0.05);
% toc;
% figure(1); imagesc(axis_theta*(180/pi), axis_rho, accum); axis xy;
% xlabel('Theta (degree)'); ylabel('Pho (pixels)');
% title('Accumulation Array from Hough Transform');
% figure(2); imagesc(imgfltrd); colormap('gray'); axis image;
% DrawLines_Polar(size(imgfltrd), lineprm);
% title('Raw Image (Blurred) with Lines Detected');
% figure(3); imagesc(rawimg); colormap('gray'); axis image;
% DrawLines_2Ends(lineseg);
% title('Raw Image with Line Segments Detected');
%
% BUG REPORT:
% This is an alpha version. Please send your bug reports, comments and
% suggestions to [email protected] . Thanks.
% Author: Tao Peng
% Department of Mechanical Engineering
% University of Maryland, College Park, Maryland 20742, USA
% [email protected]
% Version: alpha Revision: Dec. 05, 2005
% Reserved for extension
% accumres: (Optional, default is [4, 1], minimum is [2, 0.5])
% The desired resolutions in 'rho' and 'theta'. Is a
% 2-element vector in following format:
% [resolution in 'rho' (pixels),
% resolution in 'theta' (degrees)]
%%%%%%%% Arguments and parameters %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Validation of arguments
if ndims(img) ~= 2 || ~isnumeric(img),
error('Hough_Grd: ''img'' has to be 2 dimensional');
end
if ~all(size(img) >= 16),
error('Hough_Grd: ''img'' has to be larger than 16-by-16');
end
% Parameters (default values)
prm_grdthres = 8;
prm_accumres = [4, 1];
prm_detsens = 0.08;
func_compu_lineprm = true;
prm_fltraccum = true;
% Validation of arguments
vap_grdthres = 1;
if nargin > vap_grdthres,
if isnumeric(varargin{vap_grdthres}) && ...
varargin{vap_grdthres}(1) >= 0,
prm_grdthres = varargin{vap_grdthres}(1);
else
error(['Hough_Grd: ''grdthres'' has to be ', ...
'a non-negative number']);
end
end
%{
vap_accumres = 3;
if nargin > vap_accumres,
if numel(varargin{vap_accumres}) == 2 && ...
isnumeric(varargin{vap_accumres}) && ...
( varargin{vap_accumres}(1) >= 2 && ...
varargin{vap_accumres}(2) >= 0.5 ),
prm_accumres = varargin{vap_accumres};
else
error(['Hough_Grd: ''accumres'' has to be a two-element ', ...
'vector and no smaller than [2, 0.5]']);
end
end
%}
vap_detsens = 2;
if nargin > vap_detsens,
if isnumeric(varargin{vap_detsens}) && ...
varargin{vap_detsens}(1) > 0 && varargin{vap_detsens}(1) < 1,
prm_detsens = varargin{vap_detsens};
else
error('Hough_Grd: ''detsens'' has to be in the range (0, 1)');
end
end
func_compu_lineprm = ( nargout > 3 );
% Size of the accumulation array
imgsize = size(img);
coef_rhorng = [ -imgsize(2), sqrt(sum(imgsize.^2)) ];
coef_thetarng = [-pi/18, pi+pi/18];
prm_accumsize = [ ...
round( (coef_rhorng(2)-coef_rhorng(1)) * (2/prm_accumres(1)) ) , ...
round( (coef_thetarng(2)-coef_thetarng(1)) * (180/pi) * ...
(2/prm_accumres(2)) ) ];
% Default filter for the accumulation array
prm_acmfltr_R = 4;
prm_acmfltr_w = [1 2 4 8];
fltr4accum = ones(2 * prm_acmfltr_R - 1) * prm_acmfltr_w(1);
for k = 2 : prm_acmfltr_R,
fltr4accum(k:(2*prm_acmfltr_R-k), k:(2*prm_acmfltr_R-k)) = ...
prm_acmfltr_w(k);
end
fltr4accum = fltr4accum / sum(fltr4accum(:));
% Parameters for the algorithm using repeated
% thresholding and segmentation
prm_lp_dthres = mi