Разделение объектов на изображении

Я пытаюсь сосчитать объекты на изображении, и написанный мной скрипт MATLAB работает хорошо, но некоторые объекты соприкасаются (на другом изображении они даже перекрываются), и он учитывает только один объект вместо двух. Я пытаюсь использовать bwdist функция, связанная с функцией водораздела, чтобы отделить эти объекты, но она не работает хорошо. Он режет мои объекты во многих местах, где не должен, и счет намного хуже.

Если бы кто-то мог объяснить мне, как действовать другим, чтобы отделить хорошие частицы (2 касаются справа от моего изображения), я был бы благодарен.

Вот изображение:

введите описание изображения здесь


Вот мой код (если вы запустите его, появится много цифр):

Извините за написание комментариев на французском =P

clear all;

k=1; % indice pour la numérotation des images
I=imread('Images particules/DSC_0037.jpg');
figure(k)
k=k+1;
imshow(I);

I_hsv=rgb2hsv(I);
figure(k)
k=k+1;
imshow(I_hsv);

I_h=I_hsv(:,:,1);
I_s=I_hsv(:,:,2);
I_v=I_hsv(:,:,3);

figure(k)
k=k+1;
imshow(I_h)
figure(k)
k=k+1;
imshow(I_s)
figure(k)
k=k+1;
imshow(I_v)

%% Hue

[Gx, Gy] = imgradientxy(I_h);
[Gmag, Gdir] = imgradient(Gx, Gy);
% figure(k);
% k=k+1;
% imshowpair(Gmag, Gdir, 'montage');

I_bw1=Gmag>mean(quantile(Gmag,0.99));
figure(k);
k=k+1;
imshowpair(Gmag,I_bw1,'montage');

%% Saturation

[Gx, Gy] = imgradientxy(I_s);
[Gmag, Gdir] = imgradient(Gx, Gy);
% figure(k);
% k=k+1;
% imshowpair(Gmag, Gdir, 'montage');

I_bw2=Gmag>mean(quantile(Gmag,0.99));
figure(k)
k=k+1;
imshowpair(Gmag,I_bw2,'montage');


%% Variance

[Gx, Gy] = imgradientxy(I_v);
[Gmag, Gdir] = imgradient(Gx, Gy);
% figure(k);
% k=k+1;
% imshowpair(Gmag, Gdir, 'montage');

I_bw3=Gmag>mean(quantile(Gmag,0.99)); % choisir le bon quantile
figure(k)
k=k+1;
imshowpair(Gmag,I_bw3,'montage');

%% Addition images du gradient

I_recomp=I_bw1+I_bw2+I_bw3;

figure(k);
k=k+1;
imshow(I_recomp);

%% Dilatation - fill - erosion

% Element structurant diamond
% Dilatation
SE=strel('octagon',3); % doit être un multiple de 3 !
I_dil=imdilate(I_recomp,SE);
% figure(k)
% k=k+1;
% imshow(I_dil);

% Fill
I_fill=imfill(I_dil,'holes');

% Erosion
I_er=imerode(I_fill,SE);
% figure(k)
% k=k+1;
% imshow(I_er);

%% Elimination du bruit en appliquant un imerode <taille minimale des plastiques en pixels
% Erosion - dilatation

SE=strel('octagon',6); % mesurer la taille maximale d'un plastic en pixel avec imdistline !
I_bruit=imdilate(imerode(I_er,SE),SE);
figure(k)
k=k+1;
imshow(I_bruit);

%% Séparation des particules avec watershed

I_bwdist=-bwdist(~I_bruit);
figure(k);
k=k+1;
imshow(I_bwdist,[]);

I_water=watershed(I_bwdist);
I_bruit(I_water==0)=0;
figure(k);
k=k+1;
imshow(I_bruit);

%% Comptage des particules

cc=bwconncomp(I_bruit);
cc.NumObjects
L=labelmatrix(cc);
RGB_label=label2rgb(L);
figure(k);
k=k+1;
imshow(RGB_label);

2 ответа

Это нетривиальная проблема, и есть много возможных решений. Примечание: я не использовал imgradientxy а также imgradient как в вашем коде, потому что моя версия matlab слишком старая (R2011b).

На мой взгляд, нужно сделать как минимум две вещи:

  • отделение фона от других объектов. В этом случае я использую эту реализацию определения порога Otsu (выполняется на изображении, преобразованном в каналах HSV с пороговым уровнем четырех классов):

    http://www.mathworks.com/matlabcentral/fileexchange/26532-image-segmentation-using-otsu-thresholding

    Может быть полезен другой цвет фона.

  • анализировать каждый объект в одиночку, чтобы избежать помех. Можно выполнить другой тип анализа:

    • математические морфологические операции: imerode/imdilate

    • сегментация текстуры (фильтр Габора, энтропия, однородность или уровень интенсивности). Вот несколько примеров:

    http://it.mathworks.com/help/images/image-segmentation-1.html

    Также ознакомьтесь с руководством по сегментации с помощью фильтра Габора и страниц с примерами и концепциями.

Лично я сначала выполняю этап imerode/imdilate операции по нарезке начальных объектов.

Я думаю, что лучше стирать подобъекты слишком маленькие:

  • с дополнительным эрозионным проходом, используя небольшой элемент struct
  • bwareaopen с небольшим процентом от общей площади исходного объекта.

Если какие-либо подобъекты все еще существуют, должна быть возможность лучше отделить подобъекты с помощью одного из методов, перечисленных выше. Я использую математическую морфологию (эрозия немного медленная), снова сегментацию Оцу, разницу в изображении и в конечном итоге bwselect контролировать результат.

Вот мой код и вывод.

clear all;
close all;
clc;

n_level = 4;
channel_n = 2;
R1_perc = 1 / 8;
R2_perc = 5 / 100;
area2_perc = 2 / 100;
R3_small = 1;
I = imread('~/Downloads/25443957.jpg');
I_hsv = rgb2hsv(I);

% % indentify image background as the object with greatest area
% original Otsu implementation
Ihsv_otsu = otsu(I_hsv, n_level);

areas_extension = zeros(1, n_level);
for x = 1:n_level
    img_area_object = Ihsv_otsu == x; % img_zona = (Ihsv_otsu_bwl==n);
    areas_extension(1, x) = bwarea(img_area_object);
end
[background_val, background_idx] = max(areas_extension);
Ihsv_otsu_filled = imfill(Ihsv_otsu ~= background_idx, 'holes');

% % divide et impera
% analyze the objects one by one
Ihsv_otsu_bwl = bwlabel(Ihsv_otsu_filled);
img_morph = zeros(size(Ihsv_otsu_bwl));
img_eroded = zeros(size(Ihsv_otsu_bwl));

SE1px1 = strel('disk', 1);
SE3small = strel('disk', R3_small);

fprintf('start loop MORPH, Operations:: erosion & dilate...\n')

for n = 1:max(unique(Ihsv_otsu_bwl))
    fprintf('morph loop... iter:%d\n', n)
    img_zona = Ihsv_otsu_bwl == n;

    temp = regionprops(img_zona, 'MinorAxisLength');
    minoraxis = temp.MinorAxisLength;

    img_zona_out = img_zona;
    R = ceil(minoraxis * R1_perc); % use 'ceil', not 'round', to avoid R<1 processing small regions
    SE2R1 = strel('disk', R);

    CC = bwconncomp(img_zona_out, 8);
    k = 0;
    while CC.NumObjects == 1
        img_zona_out = imerode(img_zona_out, SE2R1);
        CC = bwconncomp(img_zona_out, 8);
        k = k + 1;

        % %% it is possible to erode with size~1-pixel object or not
        %         if CC.NumObjects>1
        %             fprintf('FOUND! iter:%d, c_EROSION:%d, n_Obj:%d\n', n, k, CC.NumObjects)
        %         elseif CC.NumObjects==0
        %             fprintf('BREAK! iter:%d, c_EROSION:%d, n_Obj:%d\n', n, k, CC.NumObjects)
        %             break
        %         %elseif CC.NumObjects==1
        %         %    fprintf('keep carry on... iter:%d, c_EROSION:%d, n_Obj:%d\n', n, k, CC.NumObjects)
        %         end
        if CC.NumObjects > 1
            img_zona_out = imerode(img_zona_out, SE3small);
            CC = bwconncomp(img_zona_out, 8);
            if CC.NumObjects > 1
                 fprintf('FOUND! iter:%d, c_EROSION:%d, n_Obj:%d\n', n, k, CC.NumObjects)
            elseif CC.NumObjects == 0
                % fprintf('BREAK! iter:%d, c_EROSION:%d, n_Obj:%d\n', n, k, CC.NumObjects)
                break
            end
        end
    end

    % %% post-erosion:
    % if the object number is 0, drop the eroded image and mantain the pre-eroded image
    if CC.NumObjects == 0
        img_morph = imadd(img_morph > 0, img_zona);
        img_eroded = imadd(img_eroded > 0, img_zona);
        continue
        % if the object number is greater than 1,  dilate the objects until
        % they touch, when the CC.NumObjects is 1
    elseif CC.NumObjects > 1

        k = 0;
        img_zona_dilate = img_zona_out;
        while CC.NumObjects > 1
            k = k + 1;
            img_zona_old = img_zona_dilate;
            % a small radius is better for a uniform expansion
            img_zona_dilate = imdilate(img_zona_dilate, SE3small);
            CC = bwconncomp(img_zona_dilate > 0, 8);
            if CC.NumObjects == 1
                % %% results the last objects immediatly before they touch
                img_eroded = imadd(img_eroded > 0, img_zona_old);
                fprintf('UNITED! iter:%d, c_DILATE:%d, n_n_Obj:%d\n\n', n, k, CC.NumObjects)
            end
        end

        % modified Otsu function (otsuSeparation.m)
        img_splitted = otsuSeparation(I_hsv, img_zona, channel_n, R2_perc, area2_perc);
        img_morph = imadd(img_morph > 0, img_splitted > 0);
    elseif CC.NumObjects == 1
        fprintf('# only one object... strange at this point.\n#')
        fprintf('# iter:%d, c_DILATE:%d, CC.NumObjects:%g\n', n, k, CC.NumObjects)
    end
end

% %
fprintf('start loop BWSELECT:: img_morph & img_eroded...\n')

img_eroded_bwl = bwlabel(img_eroded);

img_result = zeros(size(img_eroded_bwl));

for X = 1:max(unique(img_eroded_bwl))
    fprintf('# BWSELECT, iter:%d\n', X)
    obj2select = img_eroded_bwl == X;
    centr = regionprops(obj2select, 'centroid');
    xc = round(centr.Centroid(1));
    yc = round(centr.Centroid(2));

    temp = bwselect(img_morph, xc, yc);
    img_result = imadd(img_result > 0, temp);
end

img_result = img_result > 0;

%%
close all

figure('Name', 'morph', 'NumberTitle', 'off', 'WindowStyle', 'docked');
imagesc(img_morph)

figure('Name', 'erodeed', 'NumberTitle', 'off', 'WindowStyle', 'docked');
imagesc(img_eroded)

figure('Name', 'hsv', 'NumberTitle', 'off', 'WindowStyle', 'docked');
imagesc(I_hsv)

figure('Name', 'image', 'NumberTitle', 'off', 'WindowStyle', 'docked');
imagesc(I)

figure('Name', 'result', 'NumberTitle', 'off', 'WindowStyle', 'docked');
imagesc(img_result)

Вот моя модификация функции Оцу, OtsuSeparation.m,

function [img_splitted] = otsuSeparation(I, bw, channel_n, R2_perc, area2_perc)
    img_splitted = zeros(size(bw));

    SE1px1 = strel('disk', 1);
    % fprintf('start loop: Otsu segmentation...\n')

    % for Y=1:max(unique(img_bwl))     %for Y=41:41
    % fprintf('# OtsuSep: iter:%d\n', Y)
    % bw = img_bwl==Y;
    img_channel = I(:, :, channel_n);
    img_channel(bw == 0) = 0;
    % image segmentation by intensity
    img_channel_otsu = otsu(img_channel);

    % processing "BW" to avoid multiple objects and use only one valor for MinorAxisLength
    temp2 = regionprops(bw, 'MinorAxisLength', 'Area');
    R2 = ceil(temp2.MinorAxisLength * R2_perc);
    area2 = ceil(temp2.Area * area2_perc);
    SE3R2 = strel('disk', R2);

    % %% PART1: cleaning principal object selected by Otsu segmentation
    % removing small objects external to main selection with bwareaopen to
    % avoid modification at object contours
    img_part1 = bwareaopen(img_channel_otsu > 1, area2, 8);
    img_part1 = imfill(img_part1, 'holes');
    % execting imdilate to avoid intersection between PART1 and PART2 complements
    img_part1 = imdilate(img_part1, SE3R2);
    img_complement1 = imcomplement(img_part1);

    % %% PART2: cleaning external area around PART1 object
    % execting imerode to avoid intersection between PART1 and PART2 complements
    img_bw_eroded = imerode(bw, SE3R2);
    img_complement2 = imcomplement(img_bw_eroded);
    img_part2 = img_complement1 - img_complement2;

    img_part2 = bwareaopen(img_part2 > 0, area2, 8);
    % dilate (after erosion) to restore original object size and morphology
    img_part2 = imdilate(img_part2, SE3R2);

    % securing a well-done separation between objects
    img_intersez = imadd(img_part1, img_part2) > 1;
    img_intersez = imdilate(img_intersez, SE1px1);
    % use manual substraction here instead of imabsdiff for a good separation
    img_part1 = (img_part1 - img_intersez) > 0;
    img_part2 = (img_part2 - img_intersez) > 0;

    img_splitted = imadd(img_splitted, imadd(img_part1, img_part2));

Конечно, можно сделать разные выборы. Чтобы повысить точность, может потребоваться попробовать алгоритм с несколькими дополнительными образцами изображений и проверить его с помощью контрольной группы различных изображений.

Здесь мой вывод.

Оттенки серого (цветная версия):

цветная версия

Оттенки серого (изображение в формате Matlab):

bwlabel, matlab raw image


ОБНОВЛЕНИЕ: мой код нуждается в улучшении. Во время фазы "ИДИЛАТ ПО ИМЕРОДЕ" я предполагаю, что бинарный объект должен быть разделен на две части. Для улучшения алгоритма можно использовать также обнаружение краев:

http://robotics.eecs.berkeley.edu/~sastry/ee20/index.html

http://it.mathworks.com/help/coder/examples/edge-detection-on-images.html

http://www.mathworks.com/matlabcentral/fileexchange/45459-canny-edge-detection-in-2-d-and-3-d

Другие общие ресурсы о сегментации изображения:

https://en.wikipedia.org/wiki/Outline_of_object_recognition

http://it.mathworks.com/help/images/object-analysis.html

http://it.mathworks.com/help/images/texture-analysis-1.html

http://it.mathworks.com/help/images/image-segmentation-1.html

Сегментация - это всегда сложная проблема. В общем, довольно сложно ответить, каков наилучший способ решения вашей проблемы, я думаю, что на каждом изображении перекрывающиеся частицы будут выглядеть по-разному. Вы можете использовать форму, цвет, текстуру, предварительные знания (обучение) и т. Д. Ваши данные, вероятно, будут определять, что будет работать лучше всего

Я просто опишу здесь простой метод разделения соприкасающихся частиц, основанный на форме частиц и известный как разделение на водоразделе. Процесс начинается с двоичного изображения (которое вы уже получили, установив порог). Затем вы бы вычислили карту расстояний ( bwdist). Тогда одним из вариантов является постепенное расширение конечных размытых точек до тех пор, пока они не встретятся с черным пикселем, который будет использоваться для формирования вашего сегмента разделения.

Другие вопросы по тегам