在Matlab中从图像中提取对象

时间:2022-02-06 21:21:16

I'm currently learning image processing with Matlab. What I'm trying to do is to find in the following image all the letter a's and remove all the rest.

我目前正在使用Matlab学习图像处理。我要做的是在下图中找到所有字母a并删除所有其余字母。

在Matlab中从图像中提取对象

First of all I convert the image to a binary one. Then I try to get rid of the noise by using a median filter. There are some gaps in some of tha a's borders, which I fill by opening the image. But then I get stuck, I found some things on the internet (which I do not completely understand) with which I am able to select the gaps from all the a's.

首先,我将图像转换为二进制图像。然后我尝试使用中值滤波器去除噪声。在某些边界中存在一些间隙,我通过打开图像来填充。但后来我陷入困境,我在互联网上找到了一些东西(我并不完全理解),我可以从中选择所有a的差距。

What should I do next?

接下来我该怎么办?

My code is as follows:

我的代码如下:

text = imread('http://i.stack.imgur.com/N4nCm.png');
text = mat2gray(text);

% find threshold and chance to binary image
border = graythresh(text);
textbw = im2bw(text, border);
imshow(textbw);

% remove noise with median filter
textfilt = medfilt2(textbw);

% remove small holes in the border of the a
se = strel('disk', 4);
texter = imopen(textfilt, se);

% find holes
s = regionprops(texter, 'BoundingBox');
bb = round(reshape([s.BoundingBox], 4, []).');

% show original image with holes
imshow(textbw);

for idx = 1 : numel(s)
    rectangle('Position', bb(idx,:), 'edgecolor', 'red');
end

1 个解决方案

#1


That is a nice problem to tackle. Here is an approach you could use, but I admit that it is by no means perfect and might not be that robust. Hopefully it will give you ideas...

这是一个很好的解决问题。这是一种你可以使用的方法,但我承认它绝不是完美的,也可能不那么健壮。希望它能带给你创意......

What I did is basically filter the image with a median filter (as you did) and removed small elements using bwareaopen. Then I called regionprops to get a bunch of properties, among which the most important are the area and eccentricity. The idea was that all letters "a" should have a somewhat similar eccentricity, therefore once we know the eccentricity of one letter we can find the other letters that have about the same. You could probably make the code more robust using additional properties that make the letters stand out from the rest; maybe the ratio MajorAxisLength/MinorAxisLength for instance. I'll leave that part up to you :)

我所做的基本上是用中值滤波器过滤图像(就像你做的那样)并使用bwareaopen删除小元素。然后我打电话给regionprops来获得一堆属性,其中最重要的是区域和偏心。这个想法是所有字母“a”应该有一个类似的偏心,因此一旦我们知道一个字母的怪癖,我们就可以找到其他大致相同的字母。你可以使用额外的属性使代码更加健壮,这些属性可以使字母从其他字母中脱颖而出;也许比例为MajorAxisLength / MinorAxisLength。我会把那部分留给你:)

So the easiest way to pick a letter in this case was to select the object with the largest area, which is the big a at the center of your image. Once we have its eccentricity, we can apply some threshold and select only those objects found using regionprops that have an eccentricity about similar. The median filter and call to bwareaopen applied earlier are important here because the kind of noise in the 4 boxes on the right can complicate things if they are not removed, as a few of the random spots are likely to have an eccentricity similar to our dear letter "a".

因此,在这种情况下选择字母的最简单方法是选择面积最大的对象,即图像中心的大对象。一旦我们有了它的偏心率,我们可以应用一些阈值并仅选择那些使用具有相似偏心率的regionprops找到的对象。之前应用的中值滤波器和对bwareaopen的调用在这里很重要,因为右边4个方框中的噪声可能会使事情变得复杂,如果它们没有被删除,因为一些随机点可能有一个类似于我们亲爱的怪癖字母“a”。

That being said, here is the commented code. Note that I changed the name of your text variable to textIm since text is a Matlab function.

话虽如此,这是评论的代码。请注意,我将文本变量的名称更改为textIm,因为text是Matlab函数。

clc
clear
close all

textIm = imread('http://i.stack.imgur.com/N4nCm.png');


%// find threshold and change to binary image
border = graythresh(textIm);

%// =========== NEW \\ ===========
%// NOTICE the use of ~im2bw(...)
textbw = ~im2bw(textIm, border);

%// remove noise with median filter

%// =========== NEW \\ ===========
textfilt = medfilt2(textbw,[7 7]);
textfilt = bwareaopen(textfilt,8);

%// =========== NEW \\ ===========
%// Use an absurdely large line structuring element oriented at 25 degrees
%// to make the a's stand out

se = strel('line', 20 ,25);
textfilt = imclose(textfilt, se);

%// Get a couple properties. Note the "Eccentricity"
S = regionprops(textfilt, 'Area','Eccentricity','Centroid','BoundingBox');

All_areas = vertcat(S.Area);

%// Find the largest element (i.e. the big a). We will use it to get its
%// eccentricity and fetch other a's.

[MaxArea, MaxAreaIdx] = (max(All_areas(:)));

%// Get eccentricity of largest letter.
RefEcc = S(MaxAreaIdx).Eccentricity

Here the eccentricity of the large "a" is 0.6654. An eccentricity of 0 means a circle and an eccentricity of 1 means a line.

这里大“a”的偏心率是0.6654。偏心率为0表示圆,偏心率为1表示直线。

%// Just concatenate everything. Easier to work with.
All_Ecc = vertcat(S.Eccentricity);
All_Centroids = vertcat(S.Centroid);
All_BB = vertcat(S.BoundingBox)

%// Find elements that have the approximate eccentricity of the large a
%// found earlier. You can be more/less stringent and add more conditions here.

PotA = find(All_Ecc > RefEcc*.8 & All_Ecc < RefEcc*1.2)

%// Display output with centroids and bounding boxes.
imshow(textIm)
hold on

scatter(All_Centroids(PotA,1),All_Centroids(PotA,2),60,'r','filled');

for k = 1:numel(PotA)

rectangle('Position',All_BB(PotA(k),:),'EdgeColor','y','LineWidth',2)
end

And output, with centroids as red dots and bounding boxes as yellow rectangles:

输出,质心为红点,边界框为黄色矩形:

在Matlab中从图像中提取对象

That was fun! Hope I could help somehow. You might need to adapt the code for other letters or if you have other circle-ish objects in your image, but I guess that's a start.

那很有趣!希望我能以某种方式提供帮助。您可能需要调整其他字母的代码,或者如果图像中有其他圆形对象,但我想这是一个开始。

#1


That is a nice problem to tackle. Here is an approach you could use, but I admit that it is by no means perfect and might not be that robust. Hopefully it will give you ideas...

这是一个很好的解决问题。这是一种你可以使用的方法,但我承认它绝不是完美的,也可能不那么健壮。希望它能带给你创意......

What I did is basically filter the image with a median filter (as you did) and removed small elements using bwareaopen. Then I called regionprops to get a bunch of properties, among which the most important are the area and eccentricity. The idea was that all letters "a" should have a somewhat similar eccentricity, therefore once we know the eccentricity of one letter we can find the other letters that have about the same. You could probably make the code more robust using additional properties that make the letters stand out from the rest; maybe the ratio MajorAxisLength/MinorAxisLength for instance. I'll leave that part up to you :)

我所做的基本上是用中值滤波器过滤图像(就像你做的那样)并使用bwareaopen删除小元素。然后我打电话给regionprops来获得一堆属性,其中最重要的是区域和偏心。这个想法是所有字母“a”应该有一个类似的偏心,因此一旦我们知道一个字母的怪癖,我们就可以找到其他大致相同的字母。你可以使用额外的属性使代码更加健壮,这些属性可以使字母从其他字母中脱颖而出;也许比例为MajorAxisLength / MinorAxisLength。我会把那部分留给你:)

So the easiest way to pick a letter in this case was to select the object with the largest area, which is the big a at the center of your image. Once we have its eccentricity, we can apply some threshold and select only those objects found using regionprops that have an eccentricity about similar. The median filter and call to bwareaopen applied earlier are important here because the kind of noise in the 4 boxes on the right can complicate things if they are not removed, as a few of the random spots are likely to have an eccentricity similar to our dear letter "a".

因此,在这种情况下选择字母的最简单方法是选择面积最大的对象,即图像中心的大对象。一旦我们有了它的偏心率,我们可以应用一些阈值并仅选择那些使用具有相似偏心率的regionprops找到的对象。之前应用的中值滤波器和对bwareaopen的调用在这里很重要,因为右边4个方框中的噪声可能会使事情变得复杂,如果它们没有被删除,因为一些随机点可能有一个类似于我们亲爱的怪癖字母“a”。

That being said, here is the commented code. Note that I changed the name of your text variable to textIm since text is a Matlab function.

话虽如此,这是评论的代码。请注意,我将文本变量的名称更改为textIm,因为text是Matlab函数。

clc
clear
close all

textIm = imread('http://i.stack.imgur.com/N4nCm.png');


%// find threshold and change to binary image
border = graythresh(textIm);

%// =========== NEW \\ ===========
%// NOTICE the use of ~im2bw(...)
textbw = ~im2bw(textIm, border);

%// remove noise with median filter

%// =========== NEW \\ ===========
textfilt = medfilt2(textbw,[7 7]);
textfilt = bwareaopen(textfilt,8);

%// =========== NEW \\ ===========
%// Use an absurdely large line structuring element oriented at 25 degrees
%// to make the a's stand out

se = strel('line', 20 ,25);
textfilt = imclose(textfilt, se);

%// Get a couple properties. Note the "Eccentricity"
S = regionprops(textfilt, 'Area','Eccentricity','Centroid','BoundingBox');

All_areas = vertcat(S.Area);

%// Find the largest element (i.e. the big a). We will use it to get its
%// eccentricity and fetch other a's.

[MaxArea, MaxAreaIdx] = (max(All_areas(:)));

%// Get eccentricity of largest letter.
RefEcc = S(MaxAreaIdx).Eccentricity

Here the eccentricity of the large "a" is 0.6654. An eccentricity of 0 means a circle and an eccentricity of 1 means a line.

这里大“a”的偏心率是0.6654。偏心率为0表示圆,偏心率为1表示直线。

%// Just concatenate everything. Easier to work with.
All_Ecc = vertcat(S.Eccentricity);
All_Centroids = vertcat(S.Centroid);
All_BB = vertcat(S.BoundingBox)

%// Find elements that have the approximate eccentricity of the large a
%// found earlier. You can be more/less stringent and add more conditions here.

PotA = find(All_Ecc > RefEcc*.8 & All_Ecc < RefEcc*1.2)

%// Display output with centroids and bounding boxes.
imshow(textIm)
hold on

scatter(All_Centroids(PotA,1),All_Centroids(PotA,2),60,'r','filled');

for k = 1:numel(PotA)

rectangle('Position',All_BB(PotA(k),:),'EdgeColor','y','LineWidth',2)
end

And output, with centroids as red dots and bounding boxes as yellow rectangles:

输出,质心为红点,边界框为黄色矩形:

在Matlab中从图像中提取对象

That was fun! Hope I could help somehow. You might need to adapt the code for other letters or if you have other circle-ish objects in your image, but I guess that's a start.

那很有趣!希望我能以某种方式提供帮助。您可能需要调整其他字母的代码,或者如果图像中有其他圆形对象,但我想这是一个开始。