【问题标题】:Separating objects in an image分离图像中的对象
【发布时间】:2014-10-16 03:03:09
【问题描述】:

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

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

图片如下:


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

抱歉用法语写了 cmets =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);

【问题讨论】:

  • 1.) 我无法访问您链接到的图像。 2.) 你想通过什么度量来区分重叠的对象? 3.) 为什么切割不起作用?我想象两个重叠的圆圈 - 但我只能看到最上面的一个。因此我不知道下面的形状实际上是什么样子。请澄清这一点。
  • 1) 我不明白你为什么看不懂我的图片?对我来说,点击链接并下载效果很好。 2)什么是度量?通过目测,很明显这两个颗粒是不同的物体。 3)你说的削减是什么意思?您必须想象 2 个随机大小的粒子(可以是圆形、线条、正方形等,但绝不是完美的几何对象……)如果您仍然无法打开我的图像,请告诉我。如果没有,我会尝试将其发布到其他服务器上。
  • @Zoran - 我已经下载了你的图片并将它放在你的帖子中。我也认为你在正确的轨道上。您绝对可以在这里使用 Hue 来帮助您。这些物体具有非常明显的颜色。也许检测背景的色调是什么,然后做一些布尔表达式来去除背景

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


【解决方案1】:

这是一个不简单的问题,有很多可能的解决方案。 注意:我没有在您的代码中使用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(彩色版):

灰度 bwlabel(matlab 原始图像):


更新: 我的代码需要改进。在“imdilate 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


作为替代方案,您可以使用这种方法(python 中的示例)使用具有统计信息的连接组件:

Alternative segmentation techniques other than watershed for soil particles in images


UPDATE2:您还可以使用深度学习改变方法,例如以下示例:

【讨论】:

【解决方案2】:

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

我将在这里仅描述一种简单的方法来分离接触粒子,它依赖于粒子的形状,称为watershed separation。 该过程从二值图像开始(您已经通过阈值化获得)。然后您将计算距离图 (bwdist)。然后一种选择是逐步dilateultimately eroded points,直到它们遇到一个黑色像素,用于形成您的分离段。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-23
    • 1970-01-01
    • 1970-01-01
    • 2021-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-01
    相关资源
    最近更新 更多