function [wseg, edges, nseg] = pb2watershed(pbim, min_area, do_fill) % [wseg, edges, nseg] = pb2watershed(pbim, min_area) % % Computes a watershed oversegmentation from the soft boundary map given by pbim % and removes isolated regions smaller than min_area. Information about the % boundaries between regions is also returned. Fills boundaries if do_fill is set. % % INPUT % pbim(imh, imw): pixel indices provide boundary confidence % min_area: pixel area of smallest allowed isolated region % do_fill: set if watershed boundary pixels should be filled % % OUTPUT % wseg(imh, imw): pixel indices of regions (uint16) % edges.(ne, spLR(ne, 1:2), index{ne,1}, ...): number of edges, % regions on each side of boundary, image indices of boundaries, etc. % nseg: number of image regions % % License: This software comes with no warrantees. Anyone (commercial or not) may % use pb2watershed.m free of charge and may make any changes to it at your own risk. % This software is provided by Derek Hoiem. No restrictions are attached to its use. % if ~exist('min_area', 'var') || isempty(min_area) min_area = 25; end wseg = watershed(pbim); %figure(2), hold off, imagesc(wseg==0), axis image, colormap gray % Compute edge information and remove small regions [edges, nseg] = getEdgeInfo(wseg); if min_area > 0 [wseg, edges, nseg] = removeIsolatedRegions(wseg, edges, min_area); end edges = addGradientInfo(edges, pbim, etheta); do_display = false; if do_display tmpim1 = zeros(size(wseg)); tmpim2 = zeros(size(wseg)); for e = 1:edges.ne tmpim1(edges.index{e}) = edges.mag_mean(e); tmpim2(edges.index{e}) = edges.magdir_mean(e); end figure(1), hold off, imagesc(tmpim1, [0 1]), axis image, colormap gray figure(2), hold off, imagesc(tmpim2, [0 1]), axis image, colormap gray figure(4), hold off, imagesc(wseg), axis image, colormap jet disp(num2str([nseg edges.ne])) end % fill in edge pixels with neighboring region of most similar color if do_fill while (any(wseg(:)==0)) wseg = fill_in_segmentation(im, wseg, 0, 8); end end wseg = uint16(wseg); %%%%%%%%% removeIsolatedRegions %%%%%%%%%%% function [wseg, edges, nseg] = removeIsolatedRegions(wseg, edges, min_area) stats = regionprops(wseg, 'Area', 'PixelIdxList'); area = [stats.Area]'; indices = {stats.PixelIdxList}; nsurround = zeros(numel(area), 1); for e = 1:edges.ne nsurround(edges.spLR(e, :)) = nsurround(edges.spLR(e, :))+1; end rkeep = (area >= min_area) | (nsurround>1); ekeep = true(edges.ne, 1); for r = find(~rkeep(:))' ind = edges.spLR(:, 1)==r; if any(ind), r2 = edges.spLR(ind, 2); else ind = edges.spLR(:, 2)==r; r2 = edges.spLR(ind, 1); end wseg(indices{r}) = r2; ekeep(ind) = false; end edges.npix = edges.npix(ekeep); edges.ne = sum(ekeep); edges.index = edges.index(ekeep); edges.theta = edges.theta(ekeep); rnums = cumsum(rkeep); wseg(wseg>0) = rnums(wseg(wseg>0)); edges.spLR = rnums(edges.spLR); nseg = rnums(end); %%%%%%%%% addGradientInfo %%%%%%%%%%% function edges = addGradientInfo(edges, esoft, etheta); edges.mag_max = zeros(edges.ne, 1); edges.mag_mean = zeros(edges.ne, 1); edges.magdir_max = zeros(edges.ne, 1); edges.magdir_mean = zeros(edges.ne, 1); for e = 1:edges.ne emag = esoft(edges.index{e}); edges.mag_max(e) = max(emag); % / edges.npix(e); edges.magdir_max(e) = max(emag.*cos(etheta(edges.index{e})-edges.theta(e))); %/edges.npix(e); edges.mag_mean(e) = sum(emag) / edges.npix(e); edges.magdir_mean(e) = sum(emag.*cos(etheta(edges.index{e})-edges.theta(e)))/edges.npix(e); end edges.magdir_max = abs(edges.magdir_max); edges.magdir_mean = abs(edges.magdir_mean); %%%%%%%%% getEdgeInfo %%%%%%%%%%% function [edges, nseg] = getEdgeInfo(seg) nseg = max(seg(:)); seg = negpad(seg); [nrows,ncols] = size(seg); eind = find(seg==0); ne = numel(eind); %neighbor_offset = [-1 1 -nrows nrows]; %neighbor_index = eind*ones(1,4) + ones(ne,1)*neighbor_offset; neighbor_offset = [-1 1 -nrows nrows -1-nrows -1+nrows 1-nrows 1+nrows]; neighbor_index = eind*ones(1,8) + ones(ne,1)*neighbor_offset; neighbors = seg(neighbor_index); % set neighbors to the neighboring region with the smallest and largest % indices (in case there are multiple or multiple neighboring pixels from % the same region) tmpn = neighbors; tmpn(tmpn<=0) = Inf; n1 = min(tmpn, [], 2); n2 = max(neighbors, [], 2); [eval, sind] = sort(n1+(n2-1)*nseg); % tmpim = zeros(size(seg)); % tmpim(eind(sind)) = n2; % figure(2), hold off, imagesc(tmpim); axis image, colormap jet; % pause; %eind = eind(sind)-nrows-1; [y, x] = ind2sub([nrows ncols], eind); eind = (y-1) + (x-2)*(nrows-2); %%% Get the indices for each edge (this code is complicated but fast) deval = [double(eval(2:end)~=eval(1:end-1)) ; 1]; % marks the last index of each edge ne=sum(deval); %sum(deval)+1; nepix = zeros(ne, 1); e = 1; c = 0; % count number of pixels in each edge for k = 1:numel(eval) c = c+1; % increment count of number of pixels per edge nepix(e) = c; % set npix to current count c = c*(1-deval(k)); % reset c if dsval(k) = 1; (new edge) e = e + deval(k); % increment e if dval(k)=1; end edges.npix = nepix; edges.ne = ne; % get left and right side superpixels for each boundary deval = logical(deval); edges.spLR = [n1(sind(deval)) n2(sind(deval))]; % assign edge indices to cell array and get angles eind = uint32(eind(sind)); %(sval)); edges.index = cell(edges.ne, 1); edges.theta = zeros(edges.ne, 1); k = 0; [imx, imy] = meshgrid(1:ncols, 1:nrows); for e = 1:edges.ne % assign indices edges.index{e} = eind(k+(1:nepix(e))); k = k + nepix(e); % get angles y = imy(edges.index{e}); x = imx(edges.index{e}); zmx = (x-sum(x)/nepix(e)); zmy = -(y-sum(y)/nepix(e)); D = [sum(zmx.*zmx) sum(zmx.*zmy); sum(zmx.*zmy) sum(zmy.*zmy)]; [v, lambda] = eig(D); edges.theta(e, 1) = atan2(v(2, 2) , v(1, 2)); end %%%% This is a much simpler, but 400x slower version of getting edge indices % uval = unique(eval); % ne = numel(uval); % edges = cell(ne, 1); % for k = 1:ne % edges{k} = uint32(eind(eval==uval(k))); % end % tmpim = -seg(2:end-1, 2:end-1); % rp = randperm(ne); % for k = 1:ne % tmpim(edges{k}) = k; % end % figure(4), hold off, imagesc(tmpim), axis image, colormap jet %%%%%%%%% negpad %%%%%%%%%%% function B = negpad(A) [nrows,ncols,nchannels] = size(A); B = -ones(nrows+2,ncols+2,nchannels, class(A)); B(2:end-1,2:end-1,:) = A;