how to crop a binary mask to remove all black pixels while maintaining the largest area of white pixels?
10 views (last 30 days)
Show older comments
how to crop a binary mask to remove all black pixels while maintaining the largest area of white pixels?
I need a code to crop all the black pixels in the border, this is the most general case:
but black pixels sometimes onle in one corner.
here is an example: 

1 Comment
DGM
on 26 Dec 2024
Edited: DGM
on 26 Dec 2024
I should point out that the given image is not binarized. It's actually an RGB image and has nonzero pixels extending all the way to the image border. It will need to be reduced to a 2D binary mask first, otherwise there's nothing to crop.
If your goal is to preserve the original nonbinarized RGB content, then:
% this is an RGB image
inpict = imread('image.png');
% create a 2D mask
mask = im2gray(inpict)>200;
% use the mask to crop the original image
vertproj = any(mask,1);
horzproj = any(mask,2);
minx = find(vertproj,1,'first');
maxx = find(vertproj,1,'last');
miny = find(horzproj,1,'first');
maxy = find(horzproj,1,'last');
outpict = inpict(miny:maxy, minx:maxx, :); % address all channels
% show it
imshow(outpict)
... or using Image Analyst's example:
% this is an RGB image
inpict = imread('image.png');
% create a 2D mask
mask = im2gray(inpict)>200;
% use the bounding box of the mask to crop the original image
S = regionprops(mask,'boundingbox');
outpict = imcrop(inpict,S.BoundingBox-[0 0 1 1]); % to match behavior
% show it
imshow(outpict)
Otherwise, if you expect the output to be a logical image, apply the cropping technique to the derived mask itself.
Answers (5)
Walter Roberson
on 25 Dec 2024
Edited: Walter Roberson
on 26 Dec 2024
vertproj = any(YourMask,1);
horzproj = any(YourMask,2);
minx = find(vertproj,1,'first');
maxx = find(vertproj,1,'last');
miny = find(horzproj,1,'first');
maxy = find(horzproj,1,'last');
CroppedMask = YourMask(miny:maxy, minx:maxx);
0 Comments
Image Analyst
on 26 Dec 2024
With a shape that is not rectangular you cannot "remove" ALL black pixels. However you can crop the image if you want (like to magnify the image and see a larger version of the blob). Other than the way Walter showed you, here is an alternative way.
mask = bwareafilt(mask, 1); % Take largest blob only - throw away smaller ones.
props = regionprops(mask, 'BoundingBox'); % Find bounding box
mask = imcrop(mask, props.BoundingBox); % Crop the image.
This will crop it to the bounding box but as I said, if the white blob is not rectangular, you will still have black pixels. But that is no problem. You can in fact do anything you need to do with the black pixels still in there, and in fact you probably don't even need to crop it to do whatever it is you want to do after that.
1 Comment
DGM
on 26 Dec 2024
Alternatively, if we just want the cropped mask, we can use the 'Image' property from regionprops() and avoid the need to set up imcrop(). If we want to crop the original image though, the bounding box would work.
% this is an RGB image
inpict = imread('image.png');
% create a 2D mask
mask = im2gray(inpict)>200;
% use regionprops to get the bounding box and cropped mask
% use the bounding box to crop the original image
S = regionprops(mask,'boundingbox','image');
outmask = S.Image;
outpict = imcrop(inpict,S.BoundingBox-[0 0 1 1]); % avoid trailing overselection
DGM
on 26 Dec 2024
Another way. This is essentially the same as Walter's example, but it's rolled up as a convenient function. It can crop simple masks, or it can return the ROI extents for direct addressing of related arrays.
% this is an RGB image
inpict = imread('image.png');
% create a 2D mask
mask = im2gray(inpict)>200;
% use the mask to crop both the mask and original image
[outmask,rows,cols] = crop2box(mask);
outpict = inpict(rows,cols,:);
% show it
imshow(outpict)
% show sizes
size(inpict)
size(mask)
size(outpict)
size(outmask)
3 Comments
DGM
on 26 Dec 2024
Edited: DGM
on 26 Dec 2024
Are there any constraints on the geometry of the output (e.g. the aspect ratio, its rotation)?
Also, I don't really see how it would help to crop the image to the largest inscribed true rectangle. The cropped image would just be a solid true logical array. What might be important is the size and location from which it was cropped.
Either way, it doesn't seem like a trivial problem anymore, especially if rotations are allowed.
Image Analyst
on 26 Dec 2024
"The needed crop function should remove any black pixels." <== Here you go 🙄
outmask(:,:) = true;
Add that to the end of @DGM's code that gets outpict. Now tell us WHY that is desired. You lose nearly all information about the shape of the blob.
DGM
on 26 Dec 2024
Edited: DGM
on 26 Dec 2024
Using this tool from the FEX:
% this is an RGB image
inpict = imread('image.png');
% create a 2D mask
mask = im2gray(inpict)>200;
% get the maximum-area inscribed rectangle
% this tool automatically plots everything, but that can optionally be disabled.
LRout = LargestRectangle(mask); % use default parameters
% the output is a composite of a bunch of things crammed into one array
% i'm going to discard the first two packed variables and close the polygon
LRout = LRout([2:end 2],:)
You can adjust the number of angle steps and other parameters as needed.
You might ask how to crop out a rotated part of the mask. I would suggest to not bother. If you really want the "cropped" mask, just create it from scratch instead. We know the rectangle is 369x343, so:
% get the rectangle geometry and create a true image of the same size
cropsz = sqrt(sum(diff(LRout,1,1).^2,2));
cropsz = unique(round(cropsz),'stable');
croppedmask = true(cropsz);
... of course, it could be 343x369 too.
1 Comment
DGM
on 26 Dec 2024
Edited: DGM
on 26 Dec 2024
Looking back at the early answers about cropping to a bounding box, we can do similar here. That is, we can take this information derived from a binary mask and use it to crop a region from a grayscale or RGB image.
% this is an RGB image
inpict = imread('textblob2.png');
imshow(inpict)
% create a 2D mask
mask = im2gray(inpict)>160;
% get the maximum-area inscribed rectangle
% this tool automatically plots everything, but that can optionally be disabled.
LRout = LargestRectangle(mask); % use default parameters
% trim packed variables out of vertex list
phi = 90 - LRout(1,2);
LRout = LRout([2:end 2],:);
% get the rectangle geometry
cropsz = sqrt(sum(diff(LRout(1:3,:),1,1).^2,2));
% extract the region
movingPoints = LRout([4 3],:); % coordinate of rotated ROI edge [x y]
fixedPoints = [0 0; 0 cropsz(1)]; % coordinate of output edge [x y]
TFORM = fitgeotrans(movingPoints,fixedPoints,'nonreflectivesimilarity');
R = imref2d(cropsz,[1 cropsz(2)],[1 cropsz(1)]);
outpict = imwarp(inpict,TFORM,'OutputView',R);
% pick whether to rotate CW or CCW based on which angle is smaller
if phi > 45
outpict = rot90(outpict,1);
end
% show it
figure
imshow(outpict);
I haven't sussed out whether the vertex ordering behavior of LargestRectangle() is consistent, but I'm assuming it is. Given this transformation, the cropped area is rotated by the smallest angle needed to make it grid-aligned. I'm sure it could be simplified, but that's how I did it.
This seems to work fine with the other test image I made:


See Also
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!