REGIONCLEAN_BASE - Base function for REGIONCLEAN.
Contents
Syntax
[S, Ck, ColCk] = ...
REGIONCLEAN_BASE(L, Ck, ColCk, I, conn, m, features, thresholds);
See also
Ressembles: REGIONCLEAN, REGIONPROPS, IMLABEL. Requires: REGIONADJACENCY_BASE, REGIONPROPS, BWLABEL.
Function implementation
function [S, Ck, ColCk] = ... regionclean_base(S, Ck, ColCk, I, conn, m, features, thresholds)
check/set parameters
if ~isequal(length(features),length(thresholds)) error('regionclean_base:inputerror', ... 'vectors of feature names and threshold values must have same length' ); elseif ~all(ismember(features,{'Area','Solidity','Extent'})) % 'Perimeter','Eccentricity' error('regionclean_base:inputerror', ... 'unknown feature - see REGIONCLEAN and REGIONPROPS helps'); end [X,Y] = size(S);
initialize the labels
% if there is a label of 0 ensure we do not renumber that region by removing % it from the list of labels to be renumbered labels = unique(S(:)); % labels = sort(S(:)); labels(labels(1:end-1)==labels(2:end)) = []; % inline UNIQUE labels = setdiff(labels,0); nlabels = max(labels); % % cc = imlabel(S,8); % we can extract the connected components % % ncc = max(cc(:)); % if ~isequal(nlabels,length(labels)) % S = compressregions(S, labels); % labels = setdiff(unique(S(:)),0); % update % end % % %---------------------------------------------------------------------- % function L = compressregions(L, labels) % % this function ensures that the labels are set in sequential order % % 0 values in the original image are left with the label 0 % if labels(1)~=0, labels = [0; labels]; end % A = cumsum(diff(labels)-1); % labels = labels(2:end); % A = labels - A; % % do the relabeling: compress % for l=1:length(labels), L(L==labels(l)) = A(l); end % end % % %----------------------------------------------------------------------
break regions by assigning a new label to unconnected sub-regions checking that there is only one region for each segment label; if there is more than one region they are given unique labels
[S, ischanged] = splitregions(S, conn, labels, nlabels); % %---------------------------------------------------------------------- function [S, ischanged] = splitregions(S, conn, labels, nlabels) nl = nlabels; ischanged = []; for l = labels' % define the number of connected objects found for each label [bl,num] = bwlabel(S==l, conn); % use given connectedness if num > 1 % more than one region with the same label Ibl = bl>1; % therefore bl(Ibl)>=2 S(Ibl) = bl(Ibl) + nl - 1; ischanged = [ischanged; l]; %#ok nl = nl + (num - 1); % updated nlabels end end if nl>nlabels, ischanged = [ischanged; (nlabels+1:nl)']; end % note that we do keep the initial labelling of most of the regions % as we add new regions with labels starting after the larger label % found in the input image end % %---------------------------------------------------------------------- if ~isempty(ischanged) % possibly update labels = setdiff(unique(S(:)),0); nlabels = max(labels); end
update (or not) the centroids of the regions
if isempty(Ck) || ~isempty(ischanged) % here, we recompute all centroids for simplicity... Ck = nan(nlabels,2); ColCk = nan(nlabels,3); [Ck(labels,:), ColCk(labels,:)] = updatecentroids(S, I, labels); % elseif ~isempty(ischanged) % [Ck(ischanged,:), ColCk(ischanged,:)] = updatecentroids(S, I, ischanged); end % %---------------------------------------------------------------------- function [Ck, ColCk] = updatecentroids(Q, I, k) if nargin==3 && ~isempty(k) % get segments' centroids props = regionprops(Q .* ismember(Q,k),'centroid'); else % perform for all props = regionprops(Q,'centroid'); end % note in calling REGIONPROPS: non represented labels in the input % matrix Q are assigned NaN values into the corresponding cells of % the calculated feature(s) Ck = floor(cat(1, props.Centroid)); if nargin<3 || isempty(k), k = 1:size(Ck,1); end Ck = fliplr(Ck(k,:)); % keep only those we are interested in % Ck = fliplr(floor(cat(1, props.Centroid))); % keep even NaN values cind = Ck(:,1) + ((Ck(:,2)-1)*Y); % A = isnan(cind); cind = [cind cind+X*Y cind+2*X*Y]; % cind(A,:) = []; ColCk = nan(size(Ck,1),3); % default output if ~isempty(I), ColCk = reshape(I(cind), [size(Ck,1) 3]); % ColCk(~A,:) = reshape(I(cind), [sum(~A) 3]); end end % %----------------------------------------------------------------------
get the sparse adjacency matrix for all represented labels
G = regionadjacency_base(S, labels, conn);
% figure, imagesc(label2rgb(S))
find regions whose features do not respect the given thresholds
R = criteriaregions(S, features, thresholds);
simple case: get rid of completely isolated regions
isdeleted = find(R); if isempty(isdeleted) % nothing more to do: none of the current regions desobey the given % criterion return; end % %---------------------------------------------------------------------- function C = criteriaregions(S, features, thresholds, k) if nargin==4 && ~isempty(k) % get segment(s)' area(s) props = regionprops(S.*ismember(S,k),features); else props = regionprops(S,features); end % initialize with the first criterion C = cat(1,props.(features{1})); % C = C<thresholds(1) & ~isnan(C); if nargin<4 || isempty(k), k = 1:length(C); end C = C(k,:)<thresholds(1) & ~isnan(C(k,:)); % complete with other possible criteria for ip=2:numel(features) %if ~isfield(props,features{ip}) % eval([genvarname(features{ip}) '= cat(1,props.(features{ip}));']); A = cat(1,props.(features{ip})); C = C | (A(k,:)<thresholds(ip) & ~isnan(A(k,:))); % end end end % %----------------------------------------------------------------------
fill holes by cleaning completely surrounded regions
[S, ischanged] = fillregions(S, G, isdeleted); % %---------------------------------------------------------------------- function [S, ischanged] = fillregions(S, G, isolated) ischanged = []; % remove isolated regions embodied in another (unique) region for l=isolated' ind = find(full(G(l,:))); if length(ind)==1 ischanged = [ischanged; ind]; %#ok S(S==l) = ind; end end end % %---------------------------------------------------------------------- % function S = fillholes(S) % % remove isolated pixels embodied in another (unique) region % isolated = ismember(S,find(area==1 & ~isnan(area))); % [ic,icd] = ixneighbours(S,isolated); % P = accumarray(ic, S(icd), [], @(x){x}); % A = cellfun(@(p) length(p)<1,P); % P(A) = []; % A = find(~A); % I = cellfun(@(x) sum(diff([x(end);x(:)],1))==0, P); % S(A(I)) = P{I}(1); % end % % %----------------------------------------------------------------------
update if any change
if ~isempty(ischanged) % % recompress % S = compressregions(S, unique(S(:))); % update the list of available labels labels = setdiff(unique(S(:)),0); nlabels = max(labels); % update all the representative centroids Ck = nan(nlabels,2); ColCk = nan(nlabels,3); [Ck(labels,:), ColCk(labels,:)] = updatecentroids(S, I, labels); % update the sparse adjacency matrix G = regionadjacency_base(S, labels, conn); % update the area vector R = criteriaregions(S, features, thresholds); isdeleted = find(R); end s = floor(sqrt(X*Y/length(labels))); % kind of arbitrary...
assign those regions the label of the adjacent region whose centroid is closest to the centroid of the current region
for k = isdeleted'
% k is a label index of a region we will have to merge to another one r = true; % as said, we will merge it for sure % find regions adjacent to k ind = find(full(G(k,:)));
keep merging with the closest element in the adjacency matrix until we obtain an area >= tharea, or we run out of regions to merge
while ~isempty(ind) && r
compute the distances between the centroid of the current region and the centroids of all its neighbours
if length(ind)>1 ds = calclabspace(Ck(repmat(k,size(ind)),:), Ck(ind,:), ... ColCk(repmat(k,size(ind)),:), ColCk(ind,:), s, m); [~,i] = min(ds); else i = 1; % one neighbour only end
merge both regions k and ind(i) and store the new merged region in k; also update the connecting graph
[S, G] = updateregions(S, G, k, ind(i));
update regions' centroids for the merged region k only
[Ck(k,:), ColCk(k,:)] = updatecentroids(S, I, k);
update the criteria on the merged region k only
r = criteriaregions(S, features, thresholds, k);
ind = find(full(G(k,:))); % the adjacency matrix has changed
end
end
note that this is an arbitrary merging, depending in particular of the order the different regions are merged to others...
% %---------------------------------------------------------------------- function [S, G] = updateregions(S, G, s1, s2) % function to merge segment s2 into s1: the segmentation image, the % adjacency matrix and the area vector are updated. % update the label image: relabel s2 with s1 in the segment image S(S==s2) = s1; % update the connecting graph: s1 inherits the adjacancy matrix % entries of s2 G(s1,:) = G(s1,:) | G(s2,:); G(:,s1) = G(:,s1) | G(:,s2); G(s1,s1) = 0; % ensure s1 is not connected to itself % disconnect s2 from the adjacency matrix G(s2,:) = 0; G(:,s2) = 0; end % %---------------------------------------------------------------------- function Ds = calclabspace(p0, p1, Lab0, Lab1, s, m) distL2 = @(v0, v1) sqrt(sum(abs(v1-v0).^2, 2)); dxy = distL2(p0, p1); dlab = distL2(Lab0, Lab1); % Ds is the sum of the lab distance and the xy plane distance % normalized by the grid interval S. if isempty(dlab), dlab=0; end if isempty(dxy), dxy=0; end Ds = dlab + m * dxy / s; % note: the greater the value of m, the more spatial proximity is % emphasized and the more compact the cluster. end % %---------------------------------------------------------------------- % % as some regions will have been absorbed into others and no longer exist % % we now renumber the regions so that they sequentially increase from 1 % labels = unique(S(:)); % sorted list of unique labels % S = compressregions(S, labels); % % Ck and ColCk unchanged
end % end of regionclean_base