SLICSUPERPIX_BASE - Base function for SLICSUPERPIX.
Contents
Syntax
[Q, Ck, LabCk] = SLICSUPERPIX_BASE(I, K, isLab, T, win, m, k, maxiter);
See also
Ressembles: SLICSUPERPIX, AMOEBASUPERPIX_BASE, GEOSUPERPIX_BASE. Requires: RGB2LAB, SCOMPONENTS, EUCLIDKERNEL, NEIPOSKERNEL.
Function implementation
function [Q, Ck, LabCk, D] = slicsuperpix_base(I, K, isLab, T, n, m, k, maxiter)
checking/setting parameters
if nargin<7, maxiter = Inf; if nargin<6, k = 3; if nargin<5, m = 10; if nargin<4, n = 2; end end end end [X,Y,C] = size(I); if C~=3 warning('slicsuperpix_base:inputwarning','RGB image required in input'); if C==1, I = repmat(I, [1 1 3]); else I = I(:,:,1:3); end end
for an image with N=X*Y pixels, the approximate size of each superpixel is N/K pixels; therefore, for roughly equally sized superpixels there would be a superpixel center at every grid interval S
S = floor(sqrt(X*Y/K)); if mod(n,2)~=0, n = n+1; end; win = S*n; pad = win / 2;
possibly convert the image to RGB
if isLab Lab = RGB2Lab(I(:,:,1), I(:,:,2), I(:,:,3)); else Lab = I; end
distance functions
% L1 distance: see function UPDATERRROR below distL1 = @(v0, v1) sum(abs(v1-v0), 2); %#ok % L2 distance: see functions UPDATERRROR and CALCLABSPACE below distL2 = @(v0, v1) sqrt(sum((v1-v0).^2, 2)); % %---------------------------------------------------------------------- function Ds = calclabspace(p0, p1, Lab0, Lab1, S, m) % see Eq.(1) of [ASSLFS10] ddxy = distL2(p0, p1); ddlab = distL2(Lab0, Lab1); % Ds is the sum of the lab distance and the xy plane distance normalized by % the grid interval S; the variable m is introduced in Ds that enables to % control the compactness of a superpixel. Ds = ddlab + m * ddxy / S; % note: the greater the value of m, the more spatial proximity is emphasized % and the more compact the cluster. end % %---------------------------------------------------------------------- % function dlab = distlab(p0, p1, Lab) % dlab = sqrt((Lab(p0(:,1),p0(:,2),3) - Lab(p1(:,1),p1(:,2),3)).^2 + ... % (Lab(p0(:,1),p0(:,2),2) - Lab(p1(:,1),p1(:,2),2)).^2 + ... % (Lab(p0(:,1),p0(:,2),1) - Lab(p1(:,1),p1(:,2),1)).^2); % end % %----------------------------------------------------------------------
main calculation
we begin by sampling K regularly spaced cluster centers
[Ck, LabCk] = initcenters(Lab, S);
since the spatial extent of any superpixel is approximately S^2 (the area of a superpixel), we can safely assume that pixels that are associated with this cluster center lie within a [nS × nS] window around the superpixel center on the xy plane
nk = size(Ck,1); % %---------------------------------------------------------------------- function [Ck, LabCk] = initcenters(Lab, S) % [X,Y] = size(Lab(:,:,1)); xk = round(X / S); if xk==0, xk = floor(X/2); else xk = floor(S * (0:xk-1) + S/2); end yk = round(Y / S); if yk==0, yk = floor(X/2); else yk = floor(S * (0:yk-1) + S/2); end LabCk = reshape(Lab(xk,yk,:), [length(xk)*length(yk) 3]); [xk yk] = meshgrid(xk, yk); Ck = [xk(:), yk(:)]; % Ck = sub2ind([X,Y], xk(:), yk(:)); end % %---------------------------------------------------------------------- % function [Ck, LabCk, S] = initcenters(I, K) % [Ck, S] = gridblk(I, K); % % LabCk = zeros(length(Ck),C); % for ic=1:C % x = blkproc(I(:,:,ic), S, @(x)mean(x(:))); % LabCk(:,ic) = x(:); % end % % end % % %--------------------------------------------------------------------
we move the centers to seed locations corresponding to the lowest gradient position in a [k x k] neighborhood.
if k>0 [Ck, LabCk] = movecenters(Lab, Ck, k); end % %---------------------------------------------------------------------- function [Ck, LabCk] = movecenters(Lab, Ck, k) % note that in Eq.(2), the L2 norm is used for computing the % distance in Lab space, not the pseudo norm defined in Eq.(1) Lab0 = Lab([2:X X], (1:Y)); Lab1 = Lab([1 1:(X-1)], (1:Y)); % p0 = [[2:X X]', (1:Y)']; Lab0 = Lab(p0(:,1),p0(:,2)); % p1 = [[1 1:(X-1)]', (1:Y)']; Lab1 = Lab(p1(:,1),p1(:,2)); % [i,j] = ind2sub([X,Y], meshgrid(p0(:,1),p0(:,2))); % p0 = [i(:), j(:)]; % [i,j] = ind2sub([X,Y], meshgrid(p1(:,1),p1(:,2))); % p1 = [i(:), j(:)]; G = sum((Lab0(:)-Lab1(:)).^2, 2); % distL2(Lab0(:), Lab1(:)).^2; % we avoid calling sqrt then (^2) % calclabspace(p0, p1, Lab0(:), Lab1(:), S, m); Lab0 = Lab((1:X), [2:Y Y]); Lab1 = Lab((1:X), [1 1:(Y-1)]); % p0 = [(1:X)' [2:Y Y]']; Lab0 = Lab(p0(:,1),p0(:,2)); % p1 = [(1:X)' [1 1:(Y-1)]']; Lab1 = Lab(p1(:,1),p1(:,2)); % [i,j] = ind2sub([X,Y], meshgrid(p0(:,1),p0(:,2))); % p0 = [i(:), j(:)]; % [i,j] = ind2sub([X,Y], meshgrid(p1(:,1),p1(:,2))); % p1 = [i(:), j(:)]; G = G + sum((Lab0(:)-Lab1(:)).^2, 2); % distL2(Lab0(:), Lab1(:)).^2; % calclabspace(p0, p1, Lab0(:), Lab1(:), S, m); G = reshape(G,[X,Y]); % see Eq.(2) of [ASSLFS10] [ind, ix, iy] = neiposkernel(k,X); ind = ind(:); ix = ix(:); iy = iy(:); cind = sub2ind([X,Y], Ck(:,1), Ck(:,2)); ind = repmat(cind, [1 numel(ind)]) + repmat(ind', [length(cind) 1]); ind(ind>X*Y) = X*Y; ind(ind<=0) = 1; [~,pos] = min(G(ind), [], 2); Ck = [Ck(:,1) + ix(pos), Ck(:,2) + iy(pos)]; % check that the Ck are not outside the limit of the image Ck(:,1) = min([max([Ck(:,1), ones(nk,1)],[],2), X*ones(nk,1)], [], 2); Ck(:,2) = min([max([Ck(:,2), ones(nk,1)],[],2), Y*ones(nk,1)], [], 2); cind = sub2ind([X,Y], Ck(:,1), Ck(:,2)); cind = [cind cind+X*Y cind+2*X*Y]; LabCk = reshape(Lab(cind), [nk 3]); end % %----------------------------------------------------------------------
% define the spatial distance: it is calculated once for all: WRONG!!!
dxy = euclidkernel(win + 1); dxy = dxy(:); % w = numel(dxy); w = (win+1)^2; A = padarray(Lab, [pad pad 0], 'both', 'symmetric'); [M,N] = size(A(:,:,1));
initial labels and distances matrices
Q0 = zeros(M,N); D0 = Inf(M,N);
define some utility indices
% position indices pixindex = reshape(1:M*N,M,N); pixin = pixindex(pad+1:pad+X,pad+1:pad+Y); % iindex of the centered neighbour window of analysis [x,y] = ndgrid(-pad:pad); ind = x + M*y; % ind = repmat(-pad:pad,[2*pad+1 1]); ind = ind' + M*ind; ind = ind(:);
start 'looping'
niter = 1; err = Inf(nk,1); ierr = 1:nk; % ierr = find(err>T); while niter<=maxiter
if ~any(err>T), break; end
update labels and distance
[D0,Q0] = updatedistance(ierr, D0, Q0, Ck, LabCk ); Q = Q0(pixin);
update labels' centroids
[nCk, nLabCk] = updatecentroids(ierr, Q);
merge regions
closeness = 10; [Q, i, j] = mergeregions(ierr, Q, nCk, nLabCk, closeness); if ~isempty(j) [nCk(i,:), nLabCk(i,:)] = updatecentroids(i, Q); nCk(j,:) = []; nLabCk(j,:) = []; Ck(j,:) = []; LabCk(j,:) = []; nk = size(Ck,1); Q = compressregions(Q, unique(Q(:))); Q0(pixin) = Q; end
update errors
ierr = 1:nk; % we update all of them everytime
err = updaterror(ierr, Ck, nCk, LabCk, nLabCk);
update labels
Ck = nCk; LabCk = nLabCk; niter = niter + 1;
end % %---------------------------------------------------------------------- function [D,Q] = updatedistance(ierr, D, Q, Ck, LabCk) for l=1:length(ierr) %nk % "assign the best matching pixels from a (2S × 2S) neighborhood % around the cluster center according to the distance measure" ll = ierr(l); % ll = l; % we recompute all of them, even those unchanged % coordinates of the neighbourhood centered around the cluster % center % if any(isnan([Ck(ll,1) Ck(ll,2)])), continue; end c = pixin(Ck(ll,1),Ck(ll,2)); c3 = [c, c+N*M, c+2*N*M]; cind = c + ind; cind3 = repmat(c3,[w 1]) + repmat(ind,[1 3]); q = Q(cind); d = D(cind); dlab = distL2(A(cind3), repmat(LabCk(ll,:),[size(cind3,1) 1])); Ds = dlab + m * dxy; % see remark in function UPDATERROR [d, r] = min(cat(2,d,Ds(:)),[],2); q(r==2) = ll; % each pixel in the image is associated with the nearest cluster % center whose (nS × nS) search area overlaps this pixel Q(cind) = q; D(cind) = d; end end % %---------------------------------------------------------------------- function [Ck, LabCk] = updatecentroids(ierr, Q) labels = cellfun(@(k) find(Q==k), num2cell(ierr(:)), 'UniformOutput', false); % if we sum the Lab values over the component with same label % LabCk = cellfun(@(x) sum(Lab([x x+X*Y x+2*X*Y]),1) / length(x), ... % labels, 'UniformOutput', false); labels = cellfun(@(x) [ind2cr(X,x,'r'), ind2cr(X,x,'c')], labels, ... 'UniformOutput', false); Ck = cellfun(@(x) floor([sum(x(:,1)), sum(x(:,2))]/length(x(:,1))), ... labels, 'UniformOutput', false); % note that instead we choose the Lab value of the (point closest to % the) centroid point as a representative value LabCk = cellfun(@(x) squeeze(Lab(floor(x(1)),floor(x(2)),:))', ... Ck, 'UniformOutput', false); Ck = cell2mat(Ck); LabCk = cell2mat(LabCk); % if we wanted to use the Image Processing toolbox instead: no loop % props = regionprops(Q,'centroid', 'PixelIdxList'); % Ck = floor(cat(1, props.Centroid)); Ck = fliplr(Ck(ierr,:)); % LabCk = 'loop on numel(props)...'; end % %---------------------------------------------------------------------- function err = updaterror(ierr, Ck, nCk, LabCk, nLabCk) % algorithm 1 in [ASSLFS10] : residual error = L1 distance between % previous centers and recomputed centers % err = distL1(Ck(ierr,:), nCk); % instead we choose: err = calclabspace(Ck(ierr,:), nCk, LabCk(ierr,:), nLabCk, 1 , m); % note that here, by setting S=1, we replace the original expression % Ds = ddlab + m * ddxy / S with Ds = ddlab + m * ddxy for clarity % as it is easier to control the compactness of the clusters using % m only end % %---------------------------------------------------------------------- function [Q, i, j] = mergeregions(ierr, Q, Ck, LabCk, closeness) [i,j] = find(regionadjacency_base(Q, ierr, 8)); % note: it is symmetric i = unique(sort([i j],2),'rows'); j = i(:,2); i = i(:,1); dist = calclabspace(Ck(ierr(i),:), Ck(ierr(j),:), ... LabCk(ierr(i),:), LabCk(ierr(j),:), 1, m); l = dist < m*closeness; % naive merging if any(l) i = ierr(i(l)); j = ierr(j(l)); V = sparse(i, j, ones(length(i),1), max([i(:);j(:)]), max([i(:);j(:)])); V = V | V'; [~, comp] = scomponents(V, find(sum(V,2)==1)); i = cellfun(@(x) x(1), comp); j = cellfun(@(x) x(2:end), comp, 'UniformOutput', false); for l=1:length(i), Q(ismember(Q,j{l})) = i(l); end j = cell2mat(cellfun(@(x) x(:), j, 'UniformOutput', false)); else i = []; j = []; end end % %---------------------------------------------------------------------- function L = compressregions(L, labels) if labels(1)~=0, labels = [0; labels]; end V = cumsum(diff(labels)-1); labels = labels(2:end); V = labels - V; for l=1:length(labels), L(L==labels(l)) = V(l); end end % %---------------------------------------------------------------------- if nargout==4, D = D0(pixin); end
end % end of slicsuperpix_base