Code covered by the BSD License  

Highlights from
ISLANDS

image thumbnail
from ISLANDS by Matt Fig
Finds all "islands" which are 4-connected components in a matrix.

islands(a)
function [AG,NSV,G] = islands(a)
%ISLANDS finds all islands of four-connected elements in a matrix.
% ISLANDS returns a matrix the same size as the input matrix, with all of 
% the four-connected elements assigned an arbitrary island number. A second
% return argument is an nx3 matrix.  Each row of this matrix has 
% information about a particular island:  the first column is the island    
% number which corresponds to the numbers in the first return argument, the     
% second column is the number of elements in the island, the third column   
% is the value of the elements in that island (the elements of the input
% matrix).  ISLANDS will also return a binary matrix with ones in the 
% positions of the elements of the largest four-connected island.  The 
% largest four-connected island is determined first by value; for example  
% if there are 2 islands each with 5 elements, one of which is made up of   
% 6's and the other of which is made up of 4's, the island with the 6's  
% will be returned.  In the case where there are 2 islands with the same    
% number of elements and the same value, an arbitrary choice will be 
% returned.  ISLANDS also works with character arrays, although the array
% is first converted to it's ascii numerical equivalent.
% 
% Examples:  
%           a = floor(rand(10,50)*3);
%           [allislands,numsizeval,biggestisland] = islands(a); 
%           % allislands(allislands~=nsv(1))=0 %shows the first island.
%           spy(biggestisland)
%        
%           a = floor(rand(1000)*9);
%           tic,AG = islands(a);toc
%
% Notes:
%        If it is desired to exclude a certain value from being
%        considered as part of an island, set that element to NaN.
%        For example:
%                      a = [0 2 3; 0 0 4; 5 6 4];
%                      AG = islands(a)  % Include zero island.
%                      a(a==0) = NaN;
%                      AG = islands(a)  % Exclude zero island.
%
%        Exact equality is tested in ISLANDS, so the usual floating-point
%        comparison warning applies!  It is up to the user to decide how to
%        deal with situations for which this may pose a problem.  As one
%        example, say the user is interested in a matrix which has random 
%        elements on [0,1].  Say one wishes to group elements into islands 
%        such that elements on [0,.25) can group together, elements on
%        [.25,.5) can group together, elements on [.5,.75), and elements on 
%        [.75,1] can group together.  The elements in the matrix must first 
%        be assigned to a specific value.  To illustrate this:
%
%                A = rand(5);  % Example data.
%                B = A;         % Preserve A.
%                B(B>=0.0 & B <.25) = 2;  % 2 is outside the range of B.
%                B(B>=.25 & B<.50) = 3;
%                B(B>=.50 & B<.75) = 4;
%                B(B>=.75 & B<=1.0) = 5; 
%                % Alternatively, one can use histc to make B.
%
%        Then ISLANDS can be called on matrix B.
%
%        As the number and size of islands increase, ISLANDS can slow down
%        dramatically.  To see this, replace the second example above with:
%        
%        a = floor(rand(1000)*2); % Larger islands than floor(rand(1000)*9)
%        [G,G,G] = islands(a);spy(G) % Interested in the largest island.
%        % This might be a good time to use the mex version!
%
%        Holy For-Loops, Batman!
%        Every effort was made to make the code compatible with the Matlab
%        JIT/Accelerator.  If this someone finds that there is an error
%        in this regard, please contact me and tell me where an improvement 
%        can be made.  Thanks.
% 
% Author:  Matt Fig
% Contact: popkenai@yahoo.com
% Date:  08/21/2008

if nargin~=1
    error('Error using ISLANDS:  One input argument is required.')
end

[row,col] = size(a);  % Get the size of the input matrix.

if row*col==1 || isempty(a)
    AG = [];  % All islands.
    G = [];  % The largest island.
    NSV = [];  % Number, Size, Value.
    return
end

AG = zeros(row,col);  % This matrix will hold all of the islands found.
V = zeros(1,ceil(numel(a)/2));  % Hold the value of each island.
L = V;  % Hold the number of elements in each island.
cntr = 1;  % Label the individual islands by the order they are found.
vct = zeros(1,col); %#ok Used in center of loops below.
% Notes on comments in the loops:  CRNT is the element of the matrix we are
% currently looking at.  RGHT is the element to the immediate right of 
% CRNT.  RUD is the element to the right and up from the CRNT.
% The RUD element is directly above RGHT.

for gg = 1:col-1  % Look along the first row.
    if a(1,gg)==a(1,gg+1)  % CRNT matches RGHT.
        if ~AG(1,gg)  % CRNT does not have an island number.
            AG(1,gg) = cntr;  % Assign an island number to CRNT.
            AG(1,gg+1) = cntr;  % Assign an island number to RGHT.
            V(cntr) = a(1,gg);  % Assign the value of the island.
            L(cntr) = 2;  % Add to the island count.
            cntr = cntr + 1;  % Increment the counter.
        else  % CRNT does have an island number.
            AG(1,gg+1) = AG(1,gg);  % Assign the island number to RGHT.
            L(AG(1,gg)) = L(AG(1,gg)) + 1;  % Add to the island count.
        end
    end
end

for hh = 1:row-1  % Look down the first column.
    if a(hh,1)==a(hh+1,1)  % CRNT matches 'RGHT'.
        if ~AG(hh,1)  % CRNT does not have an island number.
            AG(hh,1) = cntr;  % Assign an island number to CRNT.
            AG(hh+1,1) = cntr;  % Assign an island number to 'RGHT'.
            V(cntr) = a(hh,1);  % Assign the value of the island.
            L(cntr) = 2;  % Add to the island count.
            cntr = cntr + 1;
        else  % CRNT does have an island number.
            AG(hh+1,1) = AG(hh,1);  % Assign an island number to 'RGHT'.
            L(AG(hh,1)) = L(AG(hh,1)) + 1;  % Add to the island count.
        end
    end
end

% Now we can look at the rest of the matrix.
for ii = 2:row;  % Start on the second row.
    for jj = 1:col-1;  % Start on the first column.
        if a(ii,jj)==a(ii,jj+1)  % CRNT matches RGHT.
            if a(ii,jj)==a(ii-1,jj+1)  % CRNT matches RUD too.
                if ~AG(ii,jj) && ~AG(ii-1,jj+1)  % Both aren't yet grouped.
                    AG(ii,jj) = cntr;  % Give CRNT a new island number.
                    AG(ii-1,jj+1) = cntr;  % Give RUD the new island num.
                    AG(ii,jj+1) = cntr;  % Give RGHT the new island number.
                    V(cntr) = a(ii,jj); % Store the value of the island.
                    L(cntr) = 3;  % Number of members in the new island.
                    cntr = cntr + 1;  % Increment the counter.
                elseif ~AG(ii-1,jj+1)  % RUD not yet been grouped, CRNT is.
                    AG(ii-1,jj+1) = AG(ii,jj); % Give RUD CRNTs isl. num.
                    AG(ii,jj+1) = AG(ii,jj);  % And RGHT as well.
                    L(AG(ii,jj)) = L(AG(ii,jj)) + 2;  % Add to island size.
                elseif ~AG(ii,jj)  % CRNT not yet grouped, RUD is grouped.
                    AG(ii,jj) = AG(ii-1,jj+1); % Give CRNT RUDs isl. num.
                    AG(ii,jj+1) = AG(ii-1,jj+1);  % And RGHT as well.
                    L(AG(ii-1,jj+1)) = L(AG(ii-1,jj+1)) + 2;  % Add to cnt.
                else % Both CRNT and RUD have been grouped:  merge islands.
                    % First decide which island has the least members.
                    if L(AG(ii-1,jj+1))<L(AG(ii,jj))
                        idx = AG(ii-1,jj+1);  % Save RUDs island number.
                        IDX = AG(ii,jj); % Used below: new islands.
                    else
                        idx = AG(ii,jj);  % Save CRNTs island number.
                        IDX = AG(ii-1,jj+1); % Used below: new islands.
                    end
                    cnt = 1; % The counter.
                    if idx ~= IDX % They could already match!
                        % These next for loops are faster than using find,
                        % indexing the whole matrix.  We are searching for 
                        % the old island numbers to add to the updated isl.
                        for pp = ii+1:row % Must search first column too.
                            if AG(pp,1)==idx
                                AG(pp,1) = IDX;  % Assign island number.
                                cnt = cnt + 1;  % Increment member counter.
                            else
                                break; % Stop search if one mismatch found. 
                            end
                        end
                        for mm = ii:-1:1  % Start at current row, work up.
                            for kk = 1:col
                                if AG(mm,kk) == idx
                                    AG(mm,kk) = IDX; % Assign new isl. num.
                                    cnt = cnt + 1; % Increment count.
                                    if cnt > L(idx)
                                        break % Stop search for old islnds.
                                    end
                                end
                            end
                            if cnt > L(idx)
                                break % Stop search for old island numbers.
                            end
                        end
                    end
                    AG(ii,jj+1) = IDX;  % Give RGHT the island number.
                    L(IDX) = L(IDX) +  cnt;  % Add to count.
                    L(idx) = L(idx) - cnt + 1;  % subtract from old island.
                end
            elseif ~AG(ii,jj)  % RGHT matches CRNT, not RUD. Need new isl.
                AG(ii,jj) = cntr;  % Give CRNT a new island number.
                AG(ii,jj+1) = cntr;  % Give RGHT the new island number.
                V(cntr) = a(ii,jj);  % Store the new island number.
                L(cntr) = 2;  % The number of members in the new island.
                cntr = cntr + 1;  % Increment the counter.
            else % RGHT matches CRNT, not RUD.  No new island needed.
                AG(ii,jj+1) = AG(ii,jj);  % Give RGHT CRNTs island number.
                L(AG(ii,jj)) = L(AG(ii,jj)) + 1; % Add to island count.
            end
        elseif a(ii,jj+1)==a(ii-1,jj+1) % RUD & RGHT match, not CRNT.
            if ~AG(ii-1,jj+1) % RUD has not yet been grouped.
                AG(ii,jj+1) = cntr;  % Give RGHT new island number.
                AG(ii-1,jj+1) = cntr; % Give RUD new island number.
                V(cntr) = a(ii,jj+1); % Store the value of the island.
                L(cntr) = 2; % Add to island count.
                cntr = cntr + 1;
            else % RUD is already part of a island.
                AG(ii,jj+1) = AG(ii-1,jj+1); % Add RGHT to RUD's island.
                L(AG(ii-1,jj+1)) = L(AG(ii-1,jj+1)) + 1; % Add island cnt.
            end
        end
    end
end

clear a % Free up memory for the rest of the proccessing.

if nargout>1 % User wants at least the 2nd output arg.
    NSV = [(1:cntr-1)',L(1:cntr-1)',V(1:cntr-1)']; % 2nd output.
    NSV(NSV(:,2)==0,:)=[]; % Clear empty islands.
    if nargout==3 % User wants the 3rd output too.
        % User wants matrix of all the islands and also the
        % matrix that has the island numbers, member counts and values.
        idx = L==max(L); % Find the largest number of island members.
        [idx2,idx2] = max(V(idx)); %#ok     And corresponding values.
        grp = find(idx,idx2); % Make selection.
        G = AG;  % G will have only the largest island.
        G(G~=grp(end)) = 0;  % Final matrix, use end to arbitrarily pick if
        G(G==grp(end)) = 1;  % more than one island is found with same val.
    end
end

Contact us at files@mathworks.com