How to discard non-rectangular closed regions in image in MATLAB?

221 Views Asked by At

I want to extract the rectangular-like shapes (some may have triangular extensions on one side) with an image like this;

enter image description here

What I have done in MATLAB is;

BW=imread('Capture2.JPG');
BW=~im2bw(BW);

SE = strel('rectangle',[1 6]);
BW = ~imclose(BW,SE);
BW2 = imfill(~BW,'holes');

figure
imshow(~BW)

figure
imshow(BW2);

s = regionprops(BW,'BoundingBox','Area','PixelIdxList');
s = s(2:end);
[lab, numberOfClosedRegions] = bwlabel(BW);


figure
imshow(BW)
for i=1:numel(s)
    rec = s(i);
    ratio = rec.BoundingBox(4)/rec.BoundingBox(3);%height/width

%     ratio > 0.3 && ratio < 51.6 && rec.Area > 1100 && rec.Area < 22500
%     if ratio > 0.16

       rectangle('Position',s(i).BoundingBox,'EdgeColor','r','LineWidth',2);
       text(rec.BoundingBox(1),rec.BoundingBox(2),num2str(i),'fontsize',16);
%     end
end

What I have come up is;

enter image description here

As it is seen, there are regions find as part of texts, shape inside of a block(index = 3) and non-block region(index = 11). I need to discard the inside regions and non-block areas.

The other issue is since regions are defined by white areas I need to get the black borders of the blocks so that I can capture the block itself, not the inner white region. How can I solve these issues?

I both tried inverting the image and using the methods but no success.

EDIT: Code improvement & additional image

One of the images can be like this including non-rectangular but object of interest shapes (leftmost).

enter image description here

Another issue if image not as good as it should be some, line considered as open especially diagonal and 1px wide ones which causes regionprops misses them.

Code improvement;

close all;

image=imread('Capture1.JPG');
BW = image;
BW = ~im2bw(BW);


SE = strel('rectangle',[3 6]);
BW = ~imclose(BW,SE); % closes some caps to be closed region

r = regionprops(BW,'PixelIdxList'); 
BW(r(1).PixelIdxList) = 0; %removes outermost white space allowing to connection lines disapear

se = strel('rectangle',[6 1]);
BW = imclose(BW,se);% closes some caps to be closed region
BW = imfill(BW,'holes');

s = regionprops(BW,{'Area', 'ConvexArea', 'BoundingBox','ConvexHull','PixelIdxList'});

%mostly the area and convex area are similar but if convex area is much greater than the area itself it is a complex shape like concave intermediate sections then remove
noidx = [];
for i=1:numel(s)
    rec = s(i);
    if rec.Area*1.5 < rec.ConvexArea 
        BW(rec.PixelIdxList) = 0;
        noidx(end+1)=i;
    end
end

s(noidx)=[];

%no condition for remaining regions figure imshow(BW)

for i=1:numel(s)
    rec = s(i);
    ratio = rec.BoundingBox(4)/rec.BoundingBox(3);%height/width    
%     ratio > 0.3 && ratio < 51.6 && rec.Area > 1100 && rec.Area < 22500
%     if ratio > 0.16
        rectangle('Position',s(i).BoundingBox,'EdgeColor','r','LineWidth',2);
        text(rec.BoundingBox(1),rec.BoundingBox(2),num2str(i),'fontsize',16,'color','red');
%     end
end

Result is;

enter image description here

Advantage is all the remaining regions are region if interest no exception and no condition for area constraint etc. because image size can be different thus the area.

But even this doesn't work on second image. Because of the text below the blocks (which is always the case -> first image was cleared to be uploaded) and the diagonal tips of the leftmost blocks considered open lines.

2

There are 2 best solutions below

11
Piglet On

How can you discard non-rectangular regions? Well I'm sure you can come up with some mathematical properties which are pretty unique to rectangles.

  • The area is the product of width and heigth.

  • The perimeter is the twice the sum of width and height.

  • It obviously has 4 rectangular corners.

I guess the first property would be sufficient and that you can come up with more rules if you need to.

You can get rid of unwanted rectangles and other small stuff with a minimum size constraint or check if they are enclosed by a rectangle.

This should be pretty straight forward.

1
obchardon On

By adding two conditions I got some good results:

  • The rectangle need to be fully closed
  • The area need to be bigger than x pixels (1100 in this case)

In order to check if the rectangle is closed or not, I created an index for each polygon. Those index have the same shape as the rectangles. So if sum(~BW(index)) == sum(index(:)) it mean that the polygon is closed.

The updated code:

warning off

BW=imread('test.jpg');
BW=~im2bw(BW);

SE = strel('rectangle',[1 6]);
BW = ~imclose(BW,SE);
BW2 = imfill(~BW,'holes');


s = regionprops(BW,'BoundingBox','Area','PixelIdxList');
s = s(2:end);
[lab, numberOfClosedRegions] = bwlabel(BW);


figure
imshow(imread('test.jpg'))
inc = 1;
for i=1:numel(s)
    rec = s(i);
    s(i).BoundingBox = floor(s(i).BoundingBox + [-1,-1,2,2]);

    %Creation of the index
    clear ind
    ind = zeros(size(BW));
    y = s(i).BoundingBox(1);
    x = s(i).BoundingBox(2);
    h = s(i).BoundingBox(3);
    w = s(i).BoundingBox(4);
    ind(x:x+w,[y,y+h]) = 1;
    ind([x,x+w],y:y+h) = 1;
    ind = logical(ind);

    if sum(~BW(ind)) == sum(ind(:)) && rec.Area > 1100
       rectangle('Position',s(i).BoundingBox,'EdgeColor','r','LineWidth',1);
       text(rec.BoundingBox(1),rec.BoundingBox(2),num2str(inc),'fontsize',16);
       inc = inc + 1;
    end
end

RESULT

enter image description here