gusucode.com > vision工具箱matlab源码程序 > vision/+vision/+internal/LBPImpl.m

    % Internal LPB Implementation
%#codegen
classdef LBPImpl < handle
    % ---------------------------------------------------------------------
    % Store relevant LBP properties
    % ---------------------------------------------------------------------
    properties(GetAccess = public, SetAccess = protected)
        NumNeighbors 
        Radius 
        Interpolation
        Normalization
        CellSize
        Uniform
        Upright
        LUT
    end
    
    % ---------------------------------------------------------------------
    % Minor properties
    % ---------------------------------------------------------------------
    properties(Access = protected, Hidden)
        % Number of bins for LBP histogram
        NumBins                           
        
        % Flag to control use of look-up table
        UseLUT
    end
    
    % ---------------------------------------------------------------------
    % Interpolation related properties. Support for nearest and linear
    % interpolation.
    % ---------------------------------------------------------------------
    properties(GetAccess = public, SetAccess = protected)                
        % Offsets - Offsets to each neighbor. Offsets are relative to the
        % the center pixel.
        Offsets
        
        % BilinearWeights - Bilinear interpolation weights
        BilinearWeights
    end
    
    % ---------------------------------------------------------------------
    % Public Methods
    % ---------------------------------------------------------------------
    methods
        
        function [lbpHist] = extractLBPFeatures(this, I)
            
            I = im2uint8(I);
            
            params = getParams(this);
            
            lbpHist = visionExtractLBPFeatures(I, params, this.LUT, this.Offsets, this.BilinearWeights);
            
            % remove border cells
            lbpHist = lbpHist(:, 2:end-1, 2:end-1);
            
            % normalize
            if strcmpi(this.Normalization, 'l2')
                lbpHist = bsxfun(@rdivide, lbpHist, sqrt(sum(lbpHist.^2)) + eps('single'));
            end
            
            % output features as 1-by-N
            lbpHist = reshape(lbpHist, 1, []);
        end
    end
    
    % ---------------------------------------------------------------------
    % Codegen static methods that implement LBP
    % ---------------------------------------------------------------------
    methods(Static)
        
        % -----------------------------------------------------------------
        function h = initializeHist(cellSize, numBins, M, N)
            numCells = floor([M N]./cellSize);
            h = zeros([numBins numCells+2],'single');  % +2 for cells bins at edges, these are remove later
        end
        
        % -----------------------------------------------------------------
        function [xmax, ymax] = computeRange(cellSize, radius, M, N)
            
            ymax = floor(M/cellSize(1)) * cellSize(1);
            xmax = floor(N/cellSize(2)) * cellSize(2);
            
            % range up to last pixel in cell or that fits in image
            ymax = min(ymax, M-radius);
            xmax = min(xmax, N-radius);
        end
        
        % -----------------------------------------------------------------
        % Return uniform LBP code from plain LBP (stored in multi-byte
        % format).
        function bin = getUniformLBPCode(lbp, index, numNeighbors, scaling, numBins, upright)
            coder.inline('always');
            u = vision.internal.LBPImpl.uniformLBP(lbp, numNeighbors);
            
            if u <= 2
                value = sum(scaling.*single(lbp));
                value = cast(value, 'uint32');
                
                numBits = vision.internal.LBPImpl.getNumSetBits(value, numNeighbors);
                
                [~, numShifts] = vision.internal.LBPImpl.rotateLBP(value, numNeighbors);
                
                if ~upright
                    % uniform + rotated
                    bin =  index(numBits+1) + 1; % +1 for 1-based
                else
                    % uniform
                    bin =  index(numBits+1) + numShifts + 1;
                end
            else
                bin = numBins;
            end
        end
        
        % -----------------------------------------------------------------
        % Return uniform rotated LBP code from plain LBP (stored in
        % multi-byte format).
        function [bin] = getUniformRotatedLBPCode(lbp, index, numNeighbors, scaling, numBins, upright)
            coder.inline('always');
            bin = vision.internal.LBPImpl.getUniformLBPCode(lbp, index, numNeighbors, scaling, numBins, upright);
        end
        
        % -----------------------------------------------------------------
        % Return LBP code and bin for histogram using look-up tables.
        % Used for uniform and uniform+rotated
        function [bin] = getLBPCodeLUT(lbp, lut, scaling)
            lbp = sum(scaling.*single(lbp));
            % convert multi-byte string into single value.
            bin = lut(single(lbp)+1) + 1; % LUT is 0-based, +1 to keep it one based.
        end
        
        % -----------------------------------------------------------------
        % Return plain LBP code and bin for histogram.
        function [bin] = getLBPCodePlain(lbp, scaling)
            lbp = sum(scaling.*single(lbp));
            bin = single(lbp)+1;
        end
        
        % -----------------------------------------------------------------
        function px = bilinearInterp(I, x, y, idx, offsets, weights)
            coder.inline('always')
            
            y = int32(y);
            x = int32(x);
            
            % neighbors of pixel,x
            % f(0,0) -- f(1,0)
            % |       x    |
            % f(0,1) -- f(1,1)
            f00 = single(I(y + offsets(2,1,idx), x + offsets(1,1,idx)));
            f10 = single(I(y + offsets(2,2,idx), x + offsets(1,2,idx)));
            f01 = single(I(y + offsets(2,3,idx), x + offsets(1,3,idx)));
            f11 = single(I(y + offsets(2,4,idx), x + offsets(1,4,idx)));
            
            a = f00;
            b = f10 - f00;
            c = f01 - f00;
            d = f00 - f10 - f01 + f11;
            
            xval = weights(1, idx);
            yval = weights(2, idx);
            xy   = weights(3, idx);
            
            px = a + b*xval + c*yval + d*xy;
            
        end
        
        % -----------------------------------------------------------------
        function px = nearestInterp(I, x, y, idx, offsets)
            coder.inline('always')
            y = int32(y);
            x = int32(x);
            px = single(I(y + offsets(2, idx), x + offsets(1, idx)));
        end
        
        % -----------------------------------------------------------------
        % Returns a multi-byte LBP code stored in stored as multiple uint8
        % values. lbp(1) is the MSB, lbp(end) is the LSB.
        % -----------------------------------------------------------------
        function lbp = computeMultibyteLBP(I, x, y, numNeighbors, interpolation, numBytes, offsets, weights)
            
            coder.inline('always');
            
            lbp = zeros(1,numBytes,'uint8');
            center = I(y,x);            
            
            p2 = coder.internal.indexInt(numNeighbors);
            p1 = coder.internal.indexInt((8*numBytes)-7+1);
            for n = coder.unroll(1:numBytes) % MSB [xxxx] LSB
                for p = p2:-1:p1 % reverse order b/c of bitshift to left
                    
                    if strcmpi(interpolation, 'linear')
                        neighbor = vision.internal.LBPImpl.bilinearInterp(I, x, y, p, offsets, weights);
                    else
                        neighbor = vision.internal.LBPImpl.nearestInterp(I, x, y, p, offsets);
                    end
                    
                    lbp(n) = bitor(lbp(n), uint8(neighbor >= center));
                    lbp(n) = bitshift(uint8(lbp(n)),uint8(1));
                end
                
                % bit p1-1
                if strcmpi(interpolation, 'linear')
                    neighbor = vision.internal.LBPImpl.bilinearInterp(I, x, y, p1-1, offsets, weights);
                else
                    neighbor = vision.internal.LBPImpl.nearestInterp(I, x, y, p1-1, offsets);
                end
                
                lbp(n) = bitor(lbp(n), uint8(neighbor >= center));
                
                % next byte
                p2 = p1-2;
                p1 = p2-7+1;
            end
        end
        % -----------------------------------------------------------------
        function [lbpHist] = codegenExtractLBPFeatures(I, numNeighbors, radius, interpolation, uniform, upright, cellSize, normalization)
            coder.extrinsic('eml_try_catch');
            I = im2uint8(I);
            
            [x, y] = vision.internal.LBPImpl.generateNeighborLocations(numNeighbors, radius);
            
            if strncmpi(interpolation, 'l',1)
                [offsets, weights] = vision.internal.LBPImpl.createBilinearOffsets(x, y, numNeighbors);
            else
                [offsets, weights] =  vision.internal.LBPImpl.createNearestOffsets(x, y);
            end
            
            % look-up table only used for 8 or 16 neighbors. Otherwise
            % in-line computation is used.
            if numNeighbors == 8 || numNeighbors == 16
                useLUT = true;
            else
                useLUT = false;
            end
            
            if ~uniform && upright                
                numBins = uint32(2^numNeighbors);
            elseif useLUT
                % force look-up table to be generated inline.
                myfun = 'vision.internal.LBPImpl.makeLUT';              
                [errid,errmsg,lut,numBins] = eml_const(eml_try_catch(myfun,numNeighbors,uniform,upright));
                eml_lib_assert(isempty(errmsg),errid,errmsg);               
            else
                
                % on-the-fly LBP computations                
                if uniform && ~upright
                    % uniform and rotated                    
                    numBins = uint32(numNeighbors + 2);
                    index   = uint32(0:numNeighbors);                    
                else
                    numBins = uint32(numNeighbors*(numNeighbors-1) + 3);
                    index   = uint32([0 1:numNeighbors:(numNeighbors*(numNeighbors-1)+1)]);
                end
            end
            
            [M, N] = size(I);
            
            lbpHist = vision.internal.LBPImpl.initializeHist(cellSize, numBins, M, N);
            
            [xmax, ymax] = vision.internal.LBPImpl.computeRange(cellSize, radius, M, N);
            
            invCellSize = 1./cellSize;
            
            numBytes = ceil(numNeighbors/8);
            
            % Scaling to convert N bytes to a float. MSB is at elem 1, LSB
            % is at end.
            scaling  = 2.^((8*numBytes-8):-8:0); % to store multi-byte LBP
            
            % start at Radius+1 to process full Radius+1-by-Radius+1 block
            for x = ((radius+1):xmax)
                
                cx = floor((x-0.5) * invCellSize(2) - 0.5);
                x0 = cellSize(2) * (cx + 0.5);
                
                wx2 = ((x-0.5) - x0) * invCellSize(2);
                wx1 = 1 - wx2;
                
                cx = cx + 2; % 1-based
                
                for y = ((radius+1):ymax)
                    
                    lbp = vision.internal.LBPImpl.computeMultibyteLBP(I, x, y, numNeighbors, interpolation, numBytes, offsets, weights);
                    
                    if ~uniform && upright
                        bin =  vision.internal.LBPImpl.getLBPCodePlain(lbp, scaling);
                    elseif useLUT
                        % uniform, uniform+rotated
                        bin = vision.internal.LBPImpl.getLBPCodeLUT(lbp, lut, scaling);
                    else
                        % on-the-fly LBP computations
                        if uniform && ~upright
                            % uniform and rotated
                            bin = vision.internal.LBPImpl.getUniformRotatedLBPCode(lbp, index, numNeighbors, scaling, numBins, upright);
                        else
                            bin = vision.internal.LBPImpl.getUniformLBPCode(lbp, index, numNeighbors, scaling, numBins, upright);
                        end
                    end
                    
                    % spatial weights for cell bins
                    cy = floor((y-0.5) * invCellSize(1) - 0.5);
                    y0 = cellSize(1) * (cy + 0.5);
                    
                    wy2 = ((y-0.5) - y0) * invCellSize(1);
                    wy1 = 1 - wy2;
                    cy = cy + 2; % 1 - based
                    
                    wx1y1 = wx1 * wy1;
                    wx2y2 = wx2 * wy2;
                    wx1y2 = wx1 * wy2;
                    wx2y1 = wx2 * wy1;
                    
                    lbpHist(bin, cy      , cx)     = lbpHist(bin, cy     , cx)     + wx1y1;
                    lbpHist(bin, cy + 1  , cx)     = lbpHist(bin, cy + 1 , cx)     + wx1y2;
                    lbpHist(bin, cy      , cx + 1) = lbpHist(bin, cy     , cx + 1) + wx2y1;
                    lbpHist(bin, cy + 1  , cx + 1) = lbpHist(bin, cy + 1 , cx + 1) + wx2y2;
                    
                end
            end
            
            % remove border cells
            lbpHist = lbpHist(:, 2:end-1, 2:end-1);
            
            % normalize
            if strcmpi(normalization, 'l2')
                lbpHist = bsxfun(@rdivide, lbpHist, sqrt(sum(lbpHist.^2)) + eps('single'));
            end
            
            % output features as 1-by-N
            lbpHist = reshape(lbpHist, 1, []);
        end
        
        % -----------------------------------------------------------------
        % Create offsets plus bilinear interp weights
        function [offsets, weights] = createBilinearOffsets(x, y, numNeighbors)
            
            % Pre-compute offsets to neighbors of pixel,px
            % f(0,0) -- f(1,0)
            % |      px    |
            % f(0,1) -- f(1,1)
            floorX = floor(x);
            floorY = floor(y);
            ceilX  = ceil(x);
            ceilY  = ceil(y);
            
            offsets = ...
                [floorX; floorY   % f(0,0)
                ceilX ; floorY    % f(1,0)
                floorX; ceilY     % f(0,1)
                ceilX ; ceilY];   % f(1,1)
            
            % Pre-compute interp weights, dx, dy, dx*dy for bilinear interp
            %
            %  dx and dy are distances from f(0,0) to the pixel, px, to be
            %  interpolated.
            %
            %  f(0,0)---->
            %         dx  |
            %             | dy
            %             v
            %            px
            %
            weights      = coder.nullcopy(zeros(3, numNeighbors, 'single'));
            weights(1,:) = x - offsets(1,:);               % x
            weights(2,:) = y - offsets(2,:);               % y
            weights(3,:) = weights(1,:) .* weights(2,:);   % xy
            
            % 2-by-4-by-N storage to simplify indexing during interp.
            offsets = reshape(offsets, 2, 4, []);
            
            offsets = int32(offsets);
            
        end
        
        % -----------------------------------------------------------------
        function [offsets, weights] = createNearestOffsets(x, y)
            offsets = int32(round([x;y]));
            weights = zeros(1,1,'single');
        end
        
        % -----------------------------------------------------------------
        function [x, y] = generateNeighborLocations(numNeighbors, radius)
            % generate locations for circular symmetric neighbors
            theta = single((360/numNeighbors) * (0:numNeighbors-1));
            
            x =  radius * cosd(theta);
            y = -radius * sind(theta);
        end
    end
    
    
    % ---------------------------------------------------------------------
    % Protected methods.
    % ---------------------------------------------------------------------
    methods(Access = protected, Hidden)
        function this = LBPImpl(params)
            
            setParams(this, params);
            
            initialize(this);
        end
        
        % -----------------------------------------------------------------
        function initialize(this)
            
            this.computeNeighborInfo();
                       
            this.updateNeighborDependentParameters();      
            
            if this.UseLUT   
                [this.LUT, this.NumBins] = vision.internal.LBPImpl.makeLUT(this.NumNeighbors, this.Uniform, this.Upright);
            end
                  
        end
        
        % -----------------------------------------------------------------
        function updateNeighborDependentParameters(this)
            
            if ~this.Uniform && this.Upright
                % internal testing only
                this.NumBins = 2^this.NumNeighbors;
                this.UseLUT  = false;
            else
                if this.Uniform && ~this.Upright
                    % uniform and rotated
                    this.NumBins = this.NumNeighbors + 2;
                elseif this.Uniform
                    this.NumBins = this.NumNeighbors*(this.NumNeighbors-1) + 3;
                end
            end
        end
        
        % -----------------------------------------------------------------
        % Update implementation parameters. Called when cached
        % implementations in getImpl require updated parameters.
        % -----------------------------------------------------------------
        function update(this, params)
            
            radiusChanged        = this.Radius ~= params.Radius;
            interpMethodChanged  = ~strcmpi(this.Interpolation(1), params.Interpolation(1));
            numNeighborsChanged  = this.NumNeighbors ~= params.NumNeighbors;
            uprightChanged       = this.Upright ~= params.Upright;
            
            setParams(this, params);
                        
            if radiusChanged || interpMethodChanged || numNeighborsChanged
                this.computeNeighborInfo();                                    
            end
            
            if numNeighborsChanged || uprightChanged           
                this.updateNeighborDependentParameters();                               
            end                        
        end
        
        % -----------------------------------------------------------------
        function setParams(this, params)
            this.NumNeighbors  = single(params.NumNeighbors);
            this.Radius        = single(params.Radius);
            this.Uniform       = params.Uniform;
            this.Upright       = params.Upright;
            this.CellSize      = params.CellSize;
            this.Interpolation = params.Interpolation;
            this.Normalization = params.Normalization;
            this.UseLUT        = params.UseLUT;            
        end
        
        % -----------------------------------------------------------------
        function params = getParams(this)
            params.NumNeighbors  = uint32(this.NumNeighbors);
            params.Radius        = uint32(this.Radius);
            params.Uniform       = this.Uniform;
            params.Upright       = this.Upright;
            params.CellSize      = uint32(this.CellSize);
            params.Interpolation = strcmpi(this.Interpolation(1),'l');
            params.Normalization = this.Normalization;
            params.UseLUT        = this.UseLUT;
            params.NumBins       = uint32(this.NumBins);
        end
        
        % -----------------------------------------------------------------
        % Pre-compute offsets for neighbor access and weights for bilinear
        % interpolation.
        function computeNeighborInfo(this)
            
            [x, y] = vision.internal.LBPImpl.generateNeighborLocations(this.NumNeighbors, this.Radius);
            
            if strncmpi(this.Interpolation, 'l',1)
                [this.Offsets, this.BilinearWeights] = vision.internal.LBPImpl.createBilinearOffsets(x, y, this.NumNeighbors);
            else
                [this.Offsets, this.BilinearWeights] = vision.internal.LBPImpl.createNearestOffsets(x, y);
            end
        end
    end
    
    % ---------------------------------------------------------------------
    % Static method to configure the LBP algorithm.
    % ---------------------------------------------------------------------
    methods(Static)
        % -----------------------------------------------------------------
        % Returns cached LBPImpl. Used to speed up look-up table based
        % implementation. Only 8 and 16 bit patterns use look-up tables.
        % Others use on-the-fly computations.
        % -----------------------------------------------------------------
        function impl = getImpl(params)
            persistent ...
                lbpImpl8u ...  % 8-bit  uniform LUT
                lbpImpl8ur ... % 8-bit  uniform+rotated LUT
                lbpImpl16u ... % 16-bit uniform LUT
                lbpImpl16ur ...% 16-bit uniform+rotated LUT
                lbpImpl        % on-the-fly computation
            
            % Set internal parameters
            params.Uniform = true;
            
            if params.NumNeighbors == 8
                
                params.UseLUT = true;
                
                if params.Upright
                    % 8-bit uniform
                    if isempty(lbpImpl8u)
                        lbpImpl8u = vision.internal.LBPImpl(params);
                    else
                        update(lbpImpl8u, params);
                    end
                    impl = lbpImpl8u;
                else
                    % 8-bit uniform + rotated
                    if isempty(lbpImpl8ur)
                        lbpImpl8ur = vision.internal.LBPImpl(params);
                    else
                        update(lbpImpl8ur, params);
                    end
                    impl = lbpImpl8ur;
                end
                
            elseif params.NumNeighbors == 16
                
                params.UseLUT = true;
                
                if params.Upright
                    % 16-bit uniform
                    if isempty(lbpImpl16u)
                        lbpImpl16u = vision.internal.LBPImpl(params);
                    else
                        update(lbpImpl16u, params);
                    end
                    impl = lbpImpl16u;
                else
                    % 16-bit uniform + rotated
                    if isempty(lbpImpl16ur)
                        lbpImpl16ur = vision.internal.LBPImpl(params);
                    else
                        update(lbpImpl16ur, params);
                    end
                    impl = lbpImpl16ur;
                end
            else
                params.UseLUT = false;
                if isempty(lbpImpl)
                    lbpImpl = vision.internal.LBPImpl(params);
                else
                    update(lbpImpl, params)
                end
                impl = lbpImpl;
            end
            
        end
        % -----------------------------------------------------------------
        % Used for internal testing.
        % -----------------------------------------------------------------
        function impl = configure(params)
            impl = vision.internal.LBPImpl(params);
        end
    end
    
    % ---------------------------------------------------------------------
    % Static methods for generating LBP look-up tables.
    % ---------------------------------------------------------------------
    methods(Static, Hidden)
        
        function [lut, nbins] = makeLUT(numNeighbors, uniform, upright)
            % Return LUT for uniform LBP patterns and/or rotated LBP.
            %   1) uniform means at most 2 0/1 or 1/0 transitions in LBP code.
            %   2) rotated patterns are circularly shifted until minimized.
            %   3) Max number of neighbors for LUT is 32, i.e. only 32-bit codes.
            
            if nargin == 2
                upright = true;
            end
            
            assert(uniform || ~upright);
            
            lut = visionMakeLBPLUT(uint32(numNeighbors), uniform, upright);
            
            if uniform && ~upright
                nbins = (numNeighbors+1) + 1; % +1 for non-uniform patterns
            else
                nbins = numNeighbors*(numNeighbors-1) + 2 + 1;
            end
            
        end        

        % -----------------------------------------------------------------
        function n = getNumSetBits(value, NumNeighbors)
            n = uint32(0);
            for i = 1:NumNeighbors
                n = n + cast(bitget(value, i),'uint32');
            end
        end
        
        % -----------------------------------------------------------------
        function u = uniformLBP(lbp, NumNeighbors)
            % Returns the number of transitions in a binary lbp code.
            % lbp may be stored in multi-byte format; elem 1 is MSB, end is LSB
            
            numBytes = int32(numel(lbp));
            
            % init
            n = int32(8) - int32((8*numBytes)) + int32(NumNeighbors); % position in the byte to start at
            % initialize with transition from MSB to LSB
            u = bitxor(bitget(lbp(1),n), bitget(lbp(end), 1));
            a = bitget(lbp(1), n);
            n = n - 1;
            
            for j = 1:numBytes  % MSB [xxxx] LSB
                while n
                    b = bitget(lbp(j), n);
                    u = u + bitxor(a,b);
                    a = b;
                    n = n - 1;
                end
                n = int32(8);
            end
        end
        
        % -----------------------------------------------------------------
        function [rotated, count] = rotateLBP(lbp, NumNeighbors)
            % Return minimized lbp pattern computed by rotating the input
            % lbp code to the right until it is minimized. Also return the
            % number of shifts needed to reach minimum.
            rotated = lbp;
            count = uint32(0);
            for k = 1:uint32(NumNeighbors)
                lbp  = vision.internal.LBPImpl.rotr(lbp, 1, NumNeighbors);
                if lbp < rotated
                    rotated = lbp;
                    count  = k;
                end
            end
        end
        
        % -----------------------------------------------------------------
        % circular right shift: x >> K | x << (NumNeighbors-K)
        function out = rotr(in, K, NumNeighbors)
            coder.inline('always');
            
            a = bitshift(in, -K);
            b = bitshift(in, NumNeighbors - K);
            mask = cast(2^NumNeighbors-1, 'like', in); % required for partial bytes
            b = bitand(b, mask);            % mask out upper 8-NumNeighbors bits
            out = bitor(a,b);
        end
    end
    
end