Unverified Commit aa4621a8 authored by Jean-Yves TINEVEZ's avatar Jean-Yves TINEVEZ Committed by GitHub
Browse files

Merge pull request #8 from sebherbert/mask-to-objects

Mask to objects: Accept B&W contour images as input for cells.
parents f0bff128 71cce598
close all
clear
clc
mask_path = 'GT.tif';
% ImageJ mask.
I = imread( mask_path );
tic
[ objects, junction_graph ] = mask_to_objects( I );
fprintf('Analyzed a %d x %d mask in %.1f seconds.\n', size(I,1), size(I,2), toc )
%% Plot everything.
figure
imshow( ~I , [ 0 2 ], 'Border', 'tight' )
hold on
plot( junction_graph, ...
'XData', junction_graph.Nodes.Centroid(:,1), ...
'YData', junction_graph.Nodes.Centroid(:,2), ...
'LineWidth', 2, ...
'EdgeColor', 'b', ...
'EdgeAlpha', 1, ...
'Marker', 'o', ...
'MarkerSize', 4, ...
'NodeColor', 'r' )
function [ objects, junction_graph ] = mask_to_objects( I )
%% MASK_TO_CELL Returns the cells from a BW image with ridges.
% This function takes as input an image that was generated e.g. with the
% morphological segmentation technique, where each object is white and
% separated from its neighbor by a black ridge. It then returns the a
% struct array, 1 element per object, containing the object boundary, the
% junction id this object touches and its center.
% As a second output it returns the junction graph.
%
% This function only works for object in white with a ridge in black. The
% ridge must be connected using 8-connectivity.
%
% Jean-Yves Tinevez - Institut Pasteur - Nov 2019.
%% Create mask.
% Boundary mask.
M = I == 0;
%% Get dilated bounds.
% Bounds like we would not have the black ridge in between cells.
CC = bwconncomp( ~M, 4 );
n_cells = CC.NumObjects;
B = cell( n_cells, 1 );
% 4-connectivity strel.
se = strel( 'diamond', 1 );
for i = 1 : n_cells
temp = false( CC.ImageSize );
temp( CC.PixelIdxList{ i } ) = true;
temp = imdilate( temp, se, 'same' );
bounds = bwboundaries( temp, 4, 'noholes' );
B{ i } = bounds{ 1 };
end
%% Remove cells touching the border.
[ width, height ] = size( M );
touching_border = cellfun( @(x) ...
any( x(:) == 1 ) ...
|| any(x(:,1) == width ) ...
|| any(x(:,2) == height ),...
B );
B( touching_border ) = [];
n_cells = numel( B );
%% Get the position of junction points.
% Define a function that only returns true for middle pixel of a 3x3
% neighborhood is >0 and with number of white neighbors >= 4.
f = @(x) (x(5) > 0 & sum(x(:)) >= 4);
% Make it a binary LUT for 3x3 neighborhood:
lut = makelut(f, 3);
% Apply the LUT to the mask image:
J = bwlookup( M, lut );
% Make label image with the junctions.
LJ = bwlabel( J );
% Measure junction centroids.
junction_table = regionprops('table', LJ, 'Centroid');
n_junctions = size(junction_table, 1 );
% Add junction id.
junction_table.ID = ( 1 : n_junctions )';
%% Link junctions together and create the output struct.
% Struct, one per cell.
% Junction graph.
junction_graph = graph( false( n_junctions, n_junctions ), junction_table );
% Loop over cells.
for i = n_cells : -1 : 1
boundary = B{ i };
n_pixels = size( boundary, 1 );
previous_junction_id = -1;
visited_junctions = [];
for k = 1 : n_pixels
x = boundary( k , 1 );
y = boundary( k , 2 );
junction_id = LJ( x, y );
if junction_id > 0
visited_junctions = [
visited_junctions
junction_id ]; %#ok<AGROW>
if previous_junction_id < 0
first_junction_id = junction_id;
end
if previous_junction_id > 0 && previous_junction_id ~= junction_id
% Link this junction to the previous one.
idx = findedge( junction_graph, previous_junction_id, junction_id );
if numel( idx ) <= 1 && idx <= 0
junction_graph = addedge( junction_graph, previous_junction_id, junction_id );
end
end
previous_junction_id = junction_id;
end
end
% Loop.
if previous_junction_id > 0 && previous_junction_id ~= first_junction_id
% Link this junction to the previous one.
idx = findedge( junction_graph, previous_junction_id, first_junction_id );
if numel( idx ) <= 1 && idx <= 0
junction_graph = addedge( junction_graph, previous_junction_id, first_junction_id );
end
end
objects( i ).junctions = unique( visited_junctions );
objects( i ).boundary = boundary;
objects( i ).center = mean( boundary );
end
end
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment