分离图像中的对象

时间:2014-08-22 09:38:58

标签: matlab image-processing counting image-segmentation object-detection

我正在尝试计算图像中的对象并且我编写的MATLAB脚本运行良好但有些对象正在触摸(在其他图像中它们甚至是重叠的)并且它只计算一个对象而不是两个。我试图使用与分水岭函数关联的bwdist函数来分隔这些对象,但它不能很好地工作。它会在不应该的地方切割我的物体,而且计数会更糟。

如果有人可以解释我如何继续分离好的粒子(我的图像右侧的2个触摸),我将不胜感激。

这是图片:

enter image description here


这是我的代码(如果你运行它会出现很多数字):

很抱歉用法语= 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 个答案:

答案 0 :(得分:3)

这是一个不小的问题,有很多可能的解决方案。 注意:我的代码中没有使用imgradientxyimgradient,因为我的matlab版本太旧了(R2011b)。

在我看来,至少有两件必要的事情要做:

我个人首先执行imerode / imdilate个操作阶段来切片初始对象。

我认为最好擦除太小的子对象:

  • 带有额外的侵蚀通道,使用小结构元素
  • bwareaopen占初始对象总面积的一小部分。

如果仍然存在任何子对象,则应该可以使用上面列出的技术之一更好地分离子对象。 我使用数学形态学(侵蚀有点慢),Otsu分割,图像差异,最终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)

这里是我对Otsu函数的修改,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));

当然可以执行不同的选择。 为了提高准确性,可能需要尝试使用少量样本图像的算法,并使用不同图像的对照组进行测试。

这是我的输出。

灰度bwlabel(彩色版):

color version

灰度bwlabel(matlab原始图片):

bwlabel, matlab raw image

<强>更新: 我的代码需要改进。在“immelate after imerode”阶段,我认为二进制对象应该分为两部分。为了改进算法,还可以使用边缘检测:

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

答案 1 :(得分:0)

细分始终是一个难题。一般来说很难回答什么是解决问题的最佳方法,我想在每个图像中重叠的粒子看起来会有所不同。您可以使用形状,颜色,纹理,先验知识(训练)等。您的数据可能会决定最佳效果

我将在这里描述一种根据粒子形状分离触摸粒子的简单方法,称为watershed separation。 该过程以二进制图像(您已通过阈值处理获得)开始。然后你会计算距离图(bwdist)。然后,一个选项是逐步dilate ultimately eroded points,直到它们遇到用于形成分离片段的黑色像素。