diff --git a/MainScripts/InputSegmentation.m b/MainScripts/InputSegmentation.m new file mode 100755 index 0000000000000000000000000000000000000000..6a5e1626a32175d717715bfe6049f80bbe5b17b6 --- /dev/null +++ b/MainScripts/InputSegmentation.m @@ -0,0 +1,30 @@ + + +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' ) diff --git a/MainScripts/mask_to_objects.m b/MainScripts/mask_to_objects.m new file mode 100755 index 0000000000000000000000000000000000000000..5efc3aec84d185b20b5e3e4bf9357c2291a812db --- /dev/null +++ b/MainScripts/mask_to_objects.m @@ -0,0 +1,135 @@ +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