gusucode.com > vision工具箱matlab源码程序 > vision/MSERRegions.m

    %MSERRegions Object for storing MSER regions
%
%   MSERRegions object describes MSER regions and corresponding ellipses
%   that have the same second moments as the regions
%
%   REGIONS = MSERRegions(PIXELLIST) constructs an MSERRegions object from 
%   PIXELLIST, an M-by-1 cell array of regions each containing P-by-2 array
%   of [x y] coordinates of the detected MSER regions. P varies based on
%   the number of pixels in a region.
%
%   The following properties are read-only and are calculated once the 
%   PIXELLIST is specified:
%
%   'Location'    M-by-2 array of [x y] centroid coordinates of ellipses 
%                 that have the same second moments as the MSER regions.
%
%   'Axes'        A two-element vector, [majorAxis minorAxis] specifies the
%                 major and minor axis of the ellipse that have the same
%                 second moments as the MSER regions.
%
%   'Orientation' Value in the range from -pi/2 to +pi/2 radians,
%                 describing orientation of the ellipse as measured from
%                 the X-axis to the major axis of the ellipse.
%                 'Orientation' is mainly useful for visualization purposes.
%
%   Notes:
%   ======
%   - The main purpose of this class is to pass the data between
%     detectMSERFeatures and extractFeatures functions. It can also be used
%     to manipulate and plot the data returned by these functions.
%   - MSERRegions is always a scalar object which may hold many regions. 
%     Therefore, NUMEL(MSERRegions) always returns 1. This may be
%     different from LENGTH(MSERRegions), which returns the true number
%     of regions held by the object.
%
%   MSERRegions methods:
%      plot             - Plot MSER regions
%      length           - Return number of stored regions
%      isempty          - Return true for empty MSERRegions object
%      size             - Return size of the MSERRegions object
%
%   MSERRegions public properties:
%      Count            - Number of regions held by the object
%      Location         - Matrix of [x,y] centroid coordinates of the 
%                         ellipse
%      Axes             - Matrix of [majorAxis, minorAxis] coordinates of
%                         the ellipse
%      Orientation      - Orientation of the ellipse
%      PixelList        - Cell array of [x,y] point coordinates of the
%                         detected MSER regions
%
%   Example 1
%   ---------
%   % Detect MSER features
%   I = imread('cameraman.tif');
%   regions = detectMSERFeatures(I);
%   imshow(I); hold on;
%   plot(regions);   % show Centroids and Axes of detected regions
%
%   Example 2
%   ---------
%   % Detect MSER features
%   I = imread('cameraman.tif');
%   regions = detectMSERFeatures(I);
%   % Display the first 10 regions in the MSERRegions object
%   imshow(I); hold on;
%   plot(regions(1:10),'showPixelList', true);
%
%   Example 3
%   ---------
%   % Combine MSER region detector with SURF descriptors
%   I = imread('cameraman.tif');
%
%   % Detect MSER features
%   regionsObj = detectMSERFeatures(I);
%
%   % Extract SURF descriptors and visualize descriptors including their
%   % scale and orientation which were computed during the extraction
%   % process
%   [features, validPtsObj] = extractFeatures(I, regionsObj);
%   imshow(I); hold on;
%   plot(validPtsObj,'showOrientation',true);
%
% See also detectMSERFeatures, extractFeatures, matchFeatures,
% detectSURFFeatures, detectHarrisFeatures, detectMinEigenFeatures
% detectFASTFeatures, SURFPoints, cornerPoints

% Copyright 2011 The MathWorks, Inc.

classdef MSERRegions
    
    properties (SetAccess='private', GetAccess='public', Dependent = true)
        %Count Number of stored regions
        Count;
        %Location Array of [x y] center coordinates of ellipses
        Location;
    end
    
    properties (SetAccess='private', GetAccess='public', Hidden=true)
        %Centroid Array of [x y] center coordinates of ellipses
        Centroid    = ones(0,2,'single');
    end
    
    properties (SetAccess='private', GetAccess='public')
        %Axes Array of [majorAxis minorAxis] of ellipses
        Axes        = ones(0,2,'single');
        %Orientation Orientation of the ellipses
        Orientation = ones(0,1,'single');
    end
    
    properties (Access='public', Dependent = true)
        %PixelList Cell array of M-by-1 regions each containing P-by-2 array
        %of [x y] coordinates of the detected MSER regions
        PixelList;
    end
    
    % Internal properties that are accessible only indirectly through
    % dependent properties
    properties (Access='private')
        pPixelList       = cell(0,1);
    end
    
    methods % Accessors for Dependent properties
        %-------------------------------------------------
        function this = set.PixelList(this, in)
            checkPixelList(in);
            this.pPixelList  = in;
            pixelListLen     = size(this.pPixelList,1);
            
            this.Centroid    = single(zeros(pixelListLen,2));
            this.Axes        = single(zeros(pixelListLen,2));
            this.Orientation = single(zeros(pixelListLen,1));
            
            for idx = 1:pixelListLen
                ellipseStruct           = computeEllipseProps(this.pPixelList{idx});
                this.Centroid(idx,:)    = single(ellipseStruct.Centroid);
                this.Axes(idx,:)        = single(ellipseStruct.Axes);
                this.Orientation(idx,1) = single(ellipseStruct.Orientation);
            end
        end
        function out = get.PixelList(this)
            out = this.pPixelList;
        end
        %-------------------------------------------------
        function out = get.Count(this)
            out = size(this.pPixelList,1);
        end
        %-----------------------------------------------
        function out = get.Location(this)
            out = this.Centroid;
        end
        %-----------------------------------------------
        function out = get.Centroid(this)
            out = this.Centroid;
        end
        %------------------------------------------------
        function out = get.Axes(this)
            out = this.Axes;
        end
        %-------------------------------------------------
        function out = get.Orientation(this)
            out = this.Orientation;
        end
    end
    
    methods(Access=private, Static)
        function name = matlabCodegenRedirect(~)
            name = 'vision.internal.MSERRegions_cg';
        end
    end
   
    %-----------------------------------------------------------------------
    methods (Access='public')
        function this = MSERRegions(varargin)
            %MSERRegions constructor
            if nargin >= 1
                inputs         = parseInputs(varargin{:});
                this.PixelList = inputs.PixelList;
            end
        end
           
        %------------------------------------------------------------------
        % Note:  NUMEL is not overridden because it interferes with the
        %        desired operation of this object.  MSERRegions is a scalar
        %        object which pretends to be a vector. NUMEL is used during
        %        subsref operations and therefore needs to represent true
        %        number of elements for the object, which is always 1.
        %------------------------------------------------------------------
        function out = length(this)
            %length Returns number of regions
            out = this.Count;
        end
        
        %------------------------------------------------------------------
        function out = isempty(this)
            %isempty Returns true if the object is empty
            out = this.Count == 0;
        end
        
        %------------------------------------------------------------------
        function varargout = size(this, varargin)
            %SIZE Size of MSERRegions object.
            %   SZ = size(V) returns the vector [length(V), 1].
            %
            %   SZ = size(V, 1) returns the length of V.
            %
            %   SZ = size(V, N), for N >= 2, returns 1.
            %
            %   [M, N] = size(V) returns length(V) for M and 1 for N.
            %
            
            try
                % Use the builtin function to validate the inputs and
                % outputs.
                switch nargout
                    case 0
                        % size(obj)       :  ans = [this.Count 1]
                        % size(obj, 1)    :  ans = this.Count
                        % size(obj, 2)    :  ans = 1
                        % size(obj, d > 2):  ans = 1
                        [varargout{1:nargout}] = ...
                            builtin('size', this, varargin{:});
                        if isempty(varargin)
                            % size(obj)
                            varargout{1}(1) = this.Count;
                        elseif numel(varargin) == 1 && varargin{1} ~= 1
                            % size(obj, 2), size(obj,n) n~=1 = 1
                            varargout{1} = 1;
                        else
                            % size(obj, 1)
                            varargout{1} = this.Count;
                        end
                        
                    case 1
                        % D = size(obj)       :  D = [this.Count, 1]
                        % n = size(obj, 1)    :  n = this.Count
                        % m = size(obj, 2)    :  m = 1
                        % p = size(obj, d > 2):  p = 1
                        n = builtin('size', this, varargin{:});
                        if isempty(varargin)
                            % D = size(obj);
                            varargout{1} = [this.Count, 1];
                        elseif numel(varargin) == 1 && varargin{1} ~= 1
                            % m = size(obj, 2);
                            % p = size(obj, d > 3);
                            varargout{1} = n;
                        else
                            % n = size(obj, 1);
                            varargout{1} = this.Count;
                        end
                        
                    case 2
                        % [n, m] = size(obj);
                        % [n, m] = size(obj, d) --> issues error
                        [n, ~] = builtin('size', this, varargin{:});
                        varargout{1} = this.Count;
                        varargout{2} = n;
                        
                    otherwise
                        % [n, m, p, ...] = size(obj)
                        % [n, m, p, ...] = size(obj, d) ---> issues error
                        %  p, ... are always 1
                        [n, ~, varargout{3:nargout}] = ...
                            builtin('size', this, varargin{:});
                        varargout{1} = this.Count;
                        varargout{2} = n;
                end
            catch e
                % throwAsCaller(e) in order to prevent the line:
                % Error using MSERRegions/size. Issue only
                % the error message.
                throwAsCaller(e);
            end
        end
        %-------------------------------------------------------------------
        function varargout = plot(this, varargin)
            %plot Plot MSER regions
            %
            %   MSERRegions.plot plots MSER regions
            %
            %   MSERRegions.plot(AXES_HANDLE,...) plots using axes with
            %   the handle AXES_HANDLE instead of the current axes (gca).
            %
            %   MSERRegions.plot(AXES_HANDLE, PARAM1, VAL1, PARAM2,
            %   VAL2, ...) controls additional plot parameters:
            %
            %     'showEllipses'    true or false.  When true, an ellipse
            %                       with the same second moments as the
            %                       detected regions is drawn for each
            %                       region. When false, only the ellipse
            %                       centers are plotted.
            %
            %                       Default: true
            %
            %     'showOrientation' true or false. When true, a line
            %                       corresponding to the region's
            %                       orientation is drawn from the ellipse's
            %                       centroid to the edge of the ellipse
            %                       indicating the region's majorAxis.
            %
            %                       Default: false
            %
            %     'showPixelList'   true or false. When true, only the
            %                       regions are plotted.
            %
            %                       Default: false
            %
            %
            %   Example
            %   -------
            %   % Extract MSER features
            %   I = imread('cameraman.tif');
            %   regions = detectMSERFeatures(I);
            %   imshow(I);hold on;
            %   plot(regions);
            %   % Plot MSER Regions
            %   figure; imshow(I);hold on;
            %   plot(regions,'showPixelList',true, 'showEllipses',false);
            %   hold off;
            
            nargoutchk(0,1);
            
            [h, inputs] = parsePlotInputs(varargin{:});
            
            if ~inputs.showEllipses && ~inputs.showOrientation ...
                    && ~inputs.showPixelList
                % invoke basic points plot
                plot(h, this.Centroid(:,1), this.Centroid(:,2),'g+');
            end
            
            if inputs.showPixelList
                % plot regions only
                wasHeld = ishold;
                               
                if (this.Count)
                    rgbVals = label2rgb(1:this.Count,'jet',[1 1 1],'shuffle');
                end
                
                for k = 1:length(this.pPixelList)
                    % plot larger regions first
                    [~, idx] = sort(...
                        cellfun(@length, this.pPixelList),'descend');
                    pt = this.pPixelList{idx(k)};
                    regionColor = squeeze(rgbVals(1,idx(k),:))';
                    regionColor = double(regionColor)/255;
                    
                    plot(h, pt(:,1), pt(:,2), 'Marker', '.',...
                        'LineStyle', 'none', 'Color', regionColor);
                    
                    % turn the hold state on so that all points are
                    % plotted; this is necessary since we are plotting in
                    % a loop, otherwise each plot command will overwrite 
                    % the previous result
                    if k==1 && ~wasHeld
                        hold('on');
                    end
                end
                
                if ~wasHeld
                    hold('off'); % restore original states of hold
                end
            end
            
            if inputs.showEllipses
                phi = linspace(0,2*pi);
                x   = cos(phi);
                y   = sin(phi);
                
                if inputs.showOrientation % Plot orientation
                    % the two zeros result in a horizontal line which
                    % will be rotated at a later stage
                    unitCircle = [x 0; y 0];
                else
                    unitCircle = [x; y];
                end
                
                wasHeld = ishold;
                
                for k = 1:this.Count
                    % Update scale using ellipse Axes
                    scale  = [this.Axes(k,1)/2 0; 0 this.Axes(k,2)/2]; 
                    pt     = this.Centroid(k,:)';
                    orient = this.Orientation(k);
                    
                    rotationMat = [cos(orient) sin(orient);...
                        -sin(orient) cos(orient)];
                    
                    mserEllipse = rotationMat*scale*unitCircle + ...
                        pt*ones(1,size(unitCircle,2));
                    
                    plot(h, pt(1), pt(2), 'g+'); % + marking center
                    % turn the hold state on so that all points are
                    % plotted; this is necessary since we are plotting in
                    % a loop, otherwise each plot command will overwrite 
                    % the previous result
                    if k==1 && ~wasHeld
                        hold('on');
                    end
                    plot(h, mserEllipse(1,:),mserEllipse(2,:),'g');
                    
                end
                
                if ~wasHeld
                    hold('off'); % restore original states of hold
                end
            end
            
            if nargout == 1
                varargout{1} = h;
            end
            
        end
        
        %-------------------------------------------------------------------
        function ind = end(this,varargin)
            %END Last index in indexing expression for MSERRegions
            %   end(V,K,N) is called for indexing expressions involving the
            %   MSERRegions vector V when END is part of the K-th index out of N
            %   indices. For example, the expression V(end-1,:) calls the
            %   MSERRegions vector's END method with END(V,1,2).
            %
            %   See also end
            
            if isempty(varargin) || varargin{1} == 1
                ind = this.Count;
            else
                ind = 1;
            end
        end
        
        %-------------------------------------------------------------------
        function varargout = subsref(this,s)
            try
                switch s(1).type
                    case '()'
                        nargoutchk(0,1);
                        % Centroid and Axes are Mx2 matrices while
                        % Orientation and PixelList are Mx1 matrices. When
                        % the indices for sub-referencing is a 1-D array,
                        % we explicitly specify the size for the second
                        % dimension.
                        opt1 = s(1);
                        opt2 = s(1);
                        if length(s(1).subs) == 1
                            opt1.subs{2} = 1;
                            opt2.subs{2} = 1:2;
                        end
                        
                        this.Orientation = subsref(this.Orientation,opt1);
                        this.pPixelList  = subsref(this.pPixelList,opt1);
                        this.Centroid    = subsref(this.Centroid,opt2);
                        this.Axes        = subsref(this.Axes,opt2);
                        
                        % invocation of plot or disp would result in setting
                        % isDotMethod
                        if numel(s) >= 2
                            isDotMethod = any(strcmp(s(2).subs, {'plot','disp'}));
                        else
                            isDotMethod = false;
                        end
                                                
                        % protect against indexing that would affect integrity
                        % of the object
                        if (~isDotMethod)
                            if ~(size(this.pPixelList,2)   == 1 && ...
                                    ismatrix(this.pPixelList) && ...
                                    numel(s) <= 2)                                    
                                error(message('vision:MSERRegions:invalidIndexingOperation'));
                            end
                        end
                        
                        if numel(s) <=1
                            varargout{1} = this;
                        else
                            % don't set 'ans' for disp() and plot()
                            if  isDotMethod && nargout ==  0
                                builtin('subsref',this,s(2));
                            else
                                outSubs = builtin('subsref',this,s(2));
                                
                                if iscell(outSubs) && (length(outSubs) == 1)
                                    % Extract the contents of the cell array if the
                                    % values are all numerical string.
                                    varargout{1} = cell2mat(outSubs);
                                else
                                    varargout{1} = outSubs;
                                end
                            end
                        end
                    case '{}'
                        % use of {} indexing is not supported by MSERRegions;
                        % let the builtin function error out as appropriate
                        builtin('subsref',this,s);
                    case '.'
                        % don't set 'ans' for disp() and plot()
                        if  strcmp(s(1).subs, 'disp') || ...
                                (strcmp(s(1).subs, 'plot') && nargout ==  0)
                            builtin('subsref',this,s);
                        else
                            outSubs = builtin('subsref',this,s);
                            if iscell(outSubs) && (length(outSubs) == 1)
                                % Extract the contents of the cell array if the
                                % values are all numerical string.
                                varargout{1} = cell2mat(outSubs);
                            else
                                varargout{1} = outSubs;
                            end
                        end
                end
            catch e
                throwAsCaller(e);
            end
        end
        
        
        %-------------------------------------------------------------------
        function out = subsasgn(this,s,in)
            try
                switch s(1).type
                    case '()'
                        if numel(s) == 2
                            % Centroid, Axes and Orientation are dependent
                            % properties of PixelList. They cannot be modified.
                            if ~(strcmp(s(2).subs,'PixelList'))
                                error(message('vision:MSERRegions:cannotSetDependentProps'));
                            end
                            this.(s(2).subs) = ...
                                subsasgn(this.(s(2).subs), s(1), in);
                        else
                            if ~((isa(in,'MSERRegions') && ~isempty(in)) ||...
                                    (isa(in,'double') && isempty(in)))
                                error(message('vision:MSERRegions:badAssignmentInput'));
                            end
                            if isempty(in)
                                this.PixelList = subsasgn(this.PixelList,s,in);
                            else
                                this.PixelList = ...
                                    subsasgn(this.PixelList,s,in.PixelList);
                            end
                        end
                        out = this;
                    case {'{}', '.'}
                        if ~(strcmp(s(1).subs,'PixelList'))
                            error(message('vision:MSERRegions:cannotSetDependentProps'));
                        end
                        
                        out = builtin('subsasgn',this,s,in);
                end
            catch e
                throwAsCaller(e);
            end
        end
        
        %-------------------------------------------------------------------
        % All of the methods below have to be managed because they can
        % create non-scalar arrays of objects and MSERRegions is strictly
        % a scalar object.
        %-------------------------------------------------------------------
        function out = cat(~, varargin) %#ok<STOUT>
            error(message('vision:MSERRegions:noCatAllowed'));
        end
        %
        function out = horzcat(varargin) %#ok<STOUT>
            error(message('vision:MSERRegions:noCatAllowed'));
        end
        %
        function out = vertcat(varargin) %#ok<STOUT>
            error(message('vision:MSERRegions:noCatAllowed'));
        end
        %
        function out = repmat(varargin) %#ok<STOUT>
            error(message('vision:MSERRegions:noCatAllowed'));
        end        
    end
     methods (Hidden)
        function sz = numArgumentsFromSubscript(~, ~, callingContext)
           switch callingContext.char
                case 'Statement'  % this(1:n).prop or this.plot
                    sz = 0;
                case {'Assignment', ... % [this(1:n).prop] = val
                      'Expression'}     % sin(this(1:n).prop)
                    sz = 1;
           end           
        end
    end
end

%--------------------------------------------------------------------------
% Plot input parser
%--------------------------------------------------------------------------
function [h, inputs] = parsePlotInputs(varargin)

% Parse the PV pairs
parser = inputParser;

parser.addOptional('AXIS_HANDLE', [], ...
    @vision.internal.inputValidation.validateAxesHandle)

parser.addParameter('showEllipses', true,  @checkFlag);

parser.addParameter('showOrientation', false, @checkFlag);

parser.addParameter('showPixelList', false, @checkFlag);

% Parse input
parser.parse(varargin{:});

% Assign return values
h = parser.Results.AXIS_HANDLE;

if isempty(h)
    h = gca;
end

inputs.showEllipses     = parser.Results.showEllipses;
inputs.showOrientation  = parser.Results.showOrientation;
inputs.showPixelList    = parser.Results.showPixelList;

end

%--------------------------------------------------------------------------
%  Plot checkers
%--------------------------------------------------------------------------
function tf = checkFlag(in)

validateattributes(in, {'logical'}, {'scalar'}, mfilename);

tf = true;
end

%--------------------------------------------------------------------------
% Main parser for the class
%--------------------------------------------------------------------------
function [inputs, unused] = parseInputs(varargin)

% Parse the PV pairs
parser = inputParser;

parser.addOptional('PixelList', cell(0,1), @checkPixelList);

% Parse input
parser.parse(varargin{:});

% Populate the parameters to pass into OpenCV's ocvExtractMSER()
inputs.PixelList = parser.Results.PixelList;

unused = parser.UsingDefaults;

end

%--------------------------------------------------------------------------
function tf = checkPixelList(in)
validateattributes(in, {'cell'}, {'size',[NaN,1]}, mfilename);

for idx = 1:length(in)
    validateattributes(in{idx},{'int32'}, {'nonnan', 'finite', ...
        'nonsparse', 'finite', 'nonsparse', 'real', 'size',[NaN,2]}, ...
        mfilename);
end

tf = true;
end

%==========================================================================
% Calculate Ellipse parameters
%==========================================================================
function EllipseStruct = computeEllipseProps(region)
%computeEllipseProps  Calculate ellipse properties 
%
%   Find the ellipse that has the same normalized second central moments as 
%   the region. Compute the axes lengths and orientation of the ellipse. 

%   Reference:
%       Haralick and Shapiro, Computer and Robot Vision vol I, 
%       Addison-Wesley 1992, Appendix A.

EllipseStruct.Centroid = mean(region, 1);
EllipseStruct.Axes = [0 0]; %[majorAxis minorAxis]
EllipseStruct.Orientation = 0;

% Assign X and Y variables so that we're measuring orientation
% counterclockwise from the horizontal axis.

xbar = EllipseStruct.Centroid(1);
ybar = EllipseStruct.Centroid(2);

x =   region(:,1) - xbar;
y = -(region(:,2) - ybar); % This is negative for the
% orientation calculation (measured in the
% counter-clockwise direction).

N = length(x);

% Calculate normalized second central moments for the region. 1/12 is
% the normalized second central moment of a pixel with unit length.
uxx = sum(x.^2)/N + 1/12;
uyy = sum(y.^2)/N + 1/12;
uxy = sum(x.*y)/N;

% Calculate major axis length, minor axis length.
common = sqrt((uxx - uyy)^2 + 4*uxy^2);
EllipseStruct.Axes(1) = 2*sqrt(2)*sqrt(uxx + uyy + common);
EllipseStruct.Axes(2) = 2*sqrt(2)*sqrt(uxx + uyy - common);

% Calculate orientation.
if (uyy > uxx)
    num = uyy - uxx + sqrt((uyy - uxx)^2 + 4*uxy^2);
    den = 2*uxy;
else
    num = 2*uxy;
    den = uxx - uyy + sqrt((uxx - uyy)^2 + 4*uxy^2);
end

if (num == 0) && (den == 0)
    EllipseStruct.Orientation = 0;
else
    EllipseStruct.Orientation = atan(num/den);
end

end
% LocalWords:  OpenCV