
You are now following this question
- You will see updates in your followed content feed.
- You may receive emails, depending on your communication preferences.
How do I remove a white background and only keep certain objects in a binary image on MATLAB?
7 views (last 30 days)
Show older comments
I would like the remove the white pixels in the background, but somehow keep the white lines. How would I go about doing that.
Sorry I didn't mean to remove the question. I was trying to comment on my phone, but I must have unknowingly modified the question.

7 Comments
John BG
on 1 Aug 2017
Hi Justin and IA
I completely agree that IA's answer is the answer to be accepted for this question so far, but with the current approach there are areas where the lines should be preserved but they are lost, for instance the top right corner

.
A fill-up approach, start from the image frame inwards, would catch all lines of interest, as long as they are connected, and then whatever single or small clusters of white pixels left behind would be easily discarded.
John BG
Image Analyst
on 1 Aug 2017
Well, not technically since the "lines" there are broken apart into smaller blobs, but I think that people kind of mentally fill in the breaks to make complete lines. But I'm not sure what a "fill up" approach is -- region growing?
Image Analyst
on 2 Aug 2017
I didn't see that grayscale image on the right. Not sure where you got it. I thought John was referring to the binary image in the original question, which did have cracks in the upper right. Now, after inspecting both binary images, it's clear the one John referred to is not the one in the original question that I was looking at.
John BG
on 2 Aug 2017
IA thanks for pointing out that the original image had white contents on the top right corner.
Jan
on 3 Aug 2017
@Image Analyst: The grey image is the image posted by Justin in his comment https://www.mathworks.com/matlabcentral/answers/345253-how-do-i-remove-a-white-background-and-only-keep-certain-objects-in-a-binary-image-on-matlab#comment_470979 .
Accepted Answer
Image Analyst
on 22 Jun 2017
Justin - are you still there? Perhaps you're still struggling over this so I made a complete and fancy demo for you. Here it is:
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
clear; % Erase all existing variables. Or clearvars if you want.
workspace; % Make sure the workspace panel is showing.
format long g;
format compact;
fontSize = 25;
%===============================================================================
% Get the name of the image the user wants to use.
baseFileName = 'concrete_inverted.png';
% Get the full filename, with path prepended.
folder = []; % Determine where demo folder is (works with all versions).
fullFileName = fullfile(folder, baseFileName);
%===============================================================================
% Read in a demo image.
grayImage = imread(fullFileName);
% Get the dimensions of the image.
% numberOfColorChannels should be = 1 for a gray scale image, and 3 for an RGB color image.
[rows, columns, numberOfColorChannels] = size(grayImage)
if numberOfColorChannels > 1
% It's not really gray scale like we expected - it's color.
% Use weighted sum of ALL channels to create a gray scale image.
grayImage = rgb2gray(grayImage);
% ALTERNATE METHOD: Convert it to gray scale by taking only the green channel,
% which in a typical snapshot will be the least noisy channel.
% grayImage = grayImage(:, :, 2); % Take green channel.
end
% Display the image.
subplot(2, 2, 1);
imshow(grayImage, []);
axis on;
axis image;
caption = sprintf('Original Gray Scale Image');
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
drawnow;
hp = impixelinfo();
% Set up figure properties:
% Enlarge figure to full screen.
set(gcf, 'Units', 'Normalized', 'OuterPosition', [0 0 1 1]);
% Get rid of tool bar and pulldown menus that are along top of figure.
% set(gcf, 'Toolbar', 'none', 'Menu', 'none');
% Give a name to the title bar.
set(gcf, 'Name', 'Demo by ImageAnalyst', 'NumberTitle', 'Off')
drawnow;
% Binarize the image by thresholding.
mask = grayImage > 128;
% For some reason, the top two lines of his image are all white. Blacken those 2 lines:
mask(1:2, :) = false;
% Display the mask image.
subplot(2, 2, 2);
imshow(mask);
axis on;
axis image; % Make sure image is not artificially stretched because of screen's aspect ratio.
title('Binary Image Mask', 'fontSize', fontSize);
drawnow;
% Get rid of small blobs.
mask = bwareaopen(mask, 500);
% Label the image.
labeledImage = bwlabel(mask);
% Find the areas and perimeters
props = regionprops(labeledImage, 'Area', 'Perimeter');
allAreas = [props.Area];
sortedAreas = sort(allAreas, 'descend')
allPerimeters = [props.Perimeter];
% Compute circularities
circularities = allPerimeters .^ 2 ./ (4 * pi * allAreas)
sortedC = sort(circularities, 'descend')
% Keep conly blobs that are nowhere close to circular or compact.
minAllowableCircularity = 10;
keeperIndexes = find(circularities >= minAllowableCircularity);
mask = ismember(labeledImage, keeperIndexes);
% Display the mask image.
subplot(2, 2, 3);
imshow(mask);
axis on;
axis image; % Make sure image is not artificially stretched because of screen's aspect ratio.
title('Intermediate Cleaned Mask', 'FontSize', fontSize);
drawnow;
% Get rid of black islands (holes) in struts without filling large black areas.
subplot(2, 2, 4);
mask = ~bwareaopen(~mask, 1000);
imshow(mask);
axis on;
axis image; % Make sure image is not artificially stretched because of screen's aspect ratio.
title('Final Cleaned Mask', 'FontSize', fontSize);
drawnow;

I think the results look pretty good, like what I think you're expecting. Don't you? If not, let me know what I could do to improve it. Basically the same original crack lines are in the image (no change in exterior shape), except that the small black islands in them are filled in, all all the little isolated white island are removed. Is that what you want? There are a few parameters/variables in there, with obvious names, that you can adjust if you want to. Please get back to me.
71 Comments
Image Analyst
on 9 Jul 2017
I'd probably skeletonize the thing, then call bwmorph() to get endpoints and branchpoints, and count them.
Justin Johnson
on 15 Jul 2017
Hello Image Analyst,
I tried the method above on another imagine, and I got the following image attached. What why am I getting black images on this one? I tried everything I could to figure it out, but I don't know what is going on. Thanks!
Justin Johnson
on 15 Jul 2017
I used the image attached. Was that code you give for that specific image or would it work for others?
Image Analyst
on 15 Jul 2017
It should work with other images. Please attach the actual PNG image, not the screenshot.
Image Analyst
on 16 Jul 2017
Edited: Image Analyst
on 16 Jul 2017
That image has a huge white surround around it that I suspect may be messing up the result. Because the perimeter is the outside edge of the image, the circularity is only 3 instead of some high number. Plus, you have only 1 blob. Have you tried it without that huge surrounding white rectangle (crop it away)?
Image Analyst
on 16 Jul 2017
Edited: Image Analyst
on 16 Jul 2017
So, I'm guessing that you tried it and still got the same answer? Please attach the actual image in a standard format like PNG, not a MATLAB proprietary fig file.
Justin Johnson
on 16 Jul 2017
Ok, so here is the problem. Take the attached image above(inverted_cracking3). When I run it though the code you gave me, it automatically creates the white space you said that ruins the result(final_inverted_cracking3.png)
Justin Johnson
on 16 Jul 2017
The original image before I inverted the binary pixels is the one attached above
Image Analyst
on 16 Jul 2017
Alright, fine - when I get home later tonight, if I have time, I'll figure out where the white rectangular border is and crop out the center crock-only portion and crop away the white surround. Looks like the good part is from rows 37-804 and columns 109-1132. Not sure how you got that white box around your image, and why you didn't just use imwrite() to save the binary image. I bet if you just did
% Binarize the image by thresholding.
mask = grayImage(37:804, 109:1132) > 128;
you'd get something reasonable.
I'll work on it later tonight if I have time but I need to hit the road now to go back home.
Image Analyst
on 16 Jul 2017
Edited: Image Analyst
on 17 Jul 2017
Look at gray image in the variable inspector, and find out where the values at the edge quit being 255.
Image Analyst
on 17 Jul 2017
I hope you took my advice and used the code change I suggested and didn't wait for me, but I didn't hear back from you so I did it. Like I thought, once you make that code change to get rid of the huge white frame, it works fine. See below image and attached test2.m file

Go ahead and tweak the parameters if you want to improve the performance.
Justin Johnson
on 17 Jul 2017
Edited: Justin Johnson
on 17 Jul 2017
Yes, it worked. Thank you so much!!!!
Justin Johnson
on 22 Jul 2017
Edited: Justin Johnson
on 22 Jul 2017
So i'm stuck again. I'm trying to create a GUI that will do the same thing (auto detect cracks), but thing time with the option to change certain parameters.Because this is my first time creating a GUI, I though it would be best to start off with one button that will do everything before having multiple buttons that will transfer values. Anyways, I can't see what is going on. I avoid asking you, but I'm really stuck. This is my result: The picture attached is layout of my current GUI. As stated earlier, the finished product should come up with a click of a button after uploading the picture. I will upload the original image as well. This is what I keep getting:

Anyways, this is what I have as of right now:
For Importing the image:
function pushbutton2_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton2 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
a=uigetfile('*.jpg')
filename=a;
setappdata (0,'filename',filename);
a=imread(a);
invIm(a);
axes(handles.axes1);
imshow(a);
setappdata(0,'a',a)
setappdata(0,'filename',a);
plot(handles.axes1,'a')
For Detection:
function pushbutton1_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% numberOfColorChannels should be = 1 for a gray scale image, and 3 for an RGB color image.
a=getappdata(0,'a');
[rows, columns, numberOfColorChannels] = size(a)
if numberOfColorChannels > 1
% If not gray scale then color.
% Uses the weighted sum of all channels to create a gray scale image.
grayImage = rgb2gray(a);
end
% Display the image.
axes(handles.axes1);
imshow(grayImage);
axis on;
axis image;
hp = impixelinfo();
% Binarize the image by thresholding.
mask = grayImage > 128;
% For some reason, the top two lines of his image are all white. Blacken those 2 lines:
mask(1:2, :) = false;
% Display the mask image.
% Get rid of small blobs.
mask = bwareaopen(mask, 500);
% Label the image.
labeledImage = bwlabel(mask);
% Find the areas and perimeters
props = regionprops(labeledImage, 'Area', 'Perimeter');
allAreas = [props.Area];
sortedAreas = sort(allAreas, 'descend')
allPerimeters = [props.Perimeter];
% Compute circularities
circularities = allPerimeters .^ 2 ./ (4 * pi * allAreas)
sortedC = sort(circularities, 'descend')
% Keep conly blobs that are nowhere close to circular or compact.
minAllowableCircularity = 10;
keeperIndexes = find(circularities >= minAllowableCircularity);
mask = ismember(labeledImage, keeperIndexes);
mask = ~bwareaopen(~mask, 1000);
imshow(mask);
axis on;
axis image; % Make sure image is not artificially stretched because of screen's aspect ratio.
drawnow;
Justin Johnson
on 22 Jul 2017
Edited: Justin Johnson
on 22 Jul 2017
Thank you so much! I will have to dissect the code to learn how to do it.However, is it possible to see how the GUI I tried to create isn't working. It is going to bug me if I don't know and knowing will only help me learn this better.
Justin Johnson
on 24 Jul 2017
Can you explain to me why it was important to calculate the area, perimeter, and circularities? I don't understand how to plays a big part in the code.
Image Analyst
on 24 Jul 2017
Objects with high circularities are line-like or crack-like. You want to retain those blobs. Blobs that have low circularity are roundish, non crack-like, and you want to get rid of those.
Justin Johnson
on 26 Jul 2017
I believe this is my last question. Can you explain this part of the code to me:
Find the areas and perimeters
props = regionprops(labeledImage, 'Area', 'Perimeter');
allAreas = [props.Area];
sortedAreas = sort(allAreas, 'descend')
allPerimeters = [props.Perimeter];
% Compute circularities
circularities = allPerimeters .^ 2 ./ (4 * pi * allAreas)
sortedC = sort(circularities, 'descend')
% Keep conly blobs that are nowhere close to circular or compact.
minAllowableCircularity = 10;
keeperIndexes = find(circularities >= minAllowableCircularity);
mask = ismember(labeledImage, keeperIndexes);
Also, would the noise in the background be circles too? Why is it not the other way around and have the cracks more line like and the noise circular like. I'm not challenging you i'm just trying to understand. Thanks!
Image Analyst
on 27 Jul 2017
I thought I already explained it pretty well though my prior comment and the comments in the code, but let me try again:
% Each blob is labeled - in other words, it has an "ID" number.
% Find the areas and perimeters for each blob.
% The structure, props, has all the measurements of all the blobs.
% Each index of props is just one structure and has the measurements of only one single blob.
props = regionprops(labeledImage, 'Area', 'Perimeter');
% Extract all the area and perimeter measurements from the structure array
% into individual double arrays for convenience.
allAreas = [props.Area];
sortedAreas = sort(allAreas, 'descend') % Just for interest (not used)
allPerimeters = [props.Perimeter];
% Compute circularities according to the formula.
% Perfect circles will have a value of 1
% and the less circular it is, the higher the circularity value.
circularities = allPerimeters .^ 2 ./ (4 * pi * allAreas)
% Sort them so we can see how the circularities progress
% from the high values of the major crack lattice
% and major isolated stick-like cracks, down towards more compact and circular blobs.
sortedC = sort(circularities, 'descend')
% Keep only blobs that are nowhere close to circular or compact.
minAllowableCircularity = 10;
% Blobs with a circularity less than 10 will be thrown out.
% circularities >= minAllowableCircularity is a logical vector
% of what blobs are or are not above the circularity threshold.
% in other words, a logical vector of whether it's a crack or not.
% Using find() turns it into a list ob blob indexes that
% we can use to identify "labels" in the labeled image
% that we want to keep/extract with ismember().
keeperIndexes = find(circularities >= minAllowableCircularity);
% Get a new binary image by asking ismember to return a
% binary image with only those indexes that we found
% that are crack-like.
mask = ismember(labeledImage, keeperIndexes);
Well there is is in extreme detail, with more lines of comments than lines of code. If you still don't understand it then you should look up the help for the individual functions like bwlabel(), regionprops(), ismember(), etc.
Justin Johnson
on 1 Aug 2017
Hey Image Analyst, Can you tell me what is wrong with my code. I'm trying to figure out my my local function aren't communicating with each other. I'm trying to create a GUI that can do what we did above, but make it easy to change parameters. Using Matlab's GUIDE, I want to change the value of circularities. As you can see in the code, you use the function setappdata() and getappdata() to transfer information, but for some reason it's not working. What is going on?
Importing the image:
function pushbutton2_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton2 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
a=uigetfile('*.png')
filename=a;
setappdata (0,'filename',filename);
a=imread(a);
invIm(a);
axes(handles.axes1);
imshow(a);
setappdata(0,'a',a)
setappdata(0,'filename',a);
plot(handles.axes1,'a')
The noise reduction code:
a=getappdata(0,'a');
[rows, columns, numberOfColorChannels] = size(a)
if numberOfColorChannels > 1
% If not gray scale then color.
% Uses the weighted sum of all channels to create a gray scale image.
grayImage = rgb2gray(a);
end
% Display the image.
axes(handles.axes1);
imshow(grayImage);
axis on;
axis image;
hp = impixelinfo();
% Binarize the image by thresholding.
mask = grayImage > 128;
% For some reason, the top two lines of his image are all white. Blacken those 2 lines:
mask(1:2, :) = false;
% Display the mask image.
% Get rid of small blobs.
mask = bwareaopen(mask, 500);
% Label the image.
labeledImage = bwlabel(mask);
% Find the areas and perimeters
props = regionprops(labeledImage, 'Area', 'Perimeter');
allAreas = [props.Area];
sortedAreas = sort(allAreas, 'descend')
allPerimeters = [props.Perimeter];
% Compute circularities
b=getappdata(0,'circle')
circularities = allPerimeters .^ 2 ./ (4 * pi * allAreas)
sortedC = sort(circularities, 'descend')
% Keep conly blobs that are nowhere close to circular or compact.
minAllowableCircularity = b;
keeperIndexes = find(circularities >= minAllowableCircularity);
mask = ismember(labeledImage, keeperIndexes);
mask = ~bwareaopen(~mask,100);
imshow(mask);
axis on;
axis image; % Make sure image is not artificially stretched because of screen's aspect ratio.
drawnow;
Using input for circularity value change:
b=str2num(get(handles.circle,'string'));
setappdata(0,'circle',b);
end
Image Analyst
on 1 Aug 2017
I don't know where you're setting circularity, but why don't you just have a slider control named sldCircularity and set the min to 0 and the max to 50 or something. Then just retrieve it directly from the slider
circularityThreshold = handles.sldCircularity.Value;
That way you don't ever need to worry about setting it with setappdata() and retrieving it with getappdata().
Jan
on 1 Aug 2017
@Justin Johnson: Is it legal to post the image "concrete1.jpg" attached to your comment? The image is watermarked, but the copyright note is missing.
Justin Johnson
on 7 Aug 2017
Edited: Justin Johnson
on 7 Aug 2017
bold Ok so something still isn't right. So I decided change the gui so that I can change the parameter of the pixels in in the function bwareaopen. Below is the layout of how I did it:
% Binarize the image by thresholding.
mask = grayImage > 128;
% For some reason, the top two lines of his image are all white. Blacken those 2 lines:
mask(1:2, :) = false;
% Display the mask image.
% Get rid of small blobs.
->mask = bwareaopen(mask,handles.edit2.Value); <-THIS IS THE EDITED PART!
% Label the image.
labeledImage = bwlabel(mask);
% Find the areas and perimeters
props = regionprops(labeledImage, 'Area', 'Perimeter');
allAreas = [props.Area];
sortedAreas = sort(allAreas, 'descend')
allPerimeters = [props.Perimeter];
% Compute circularities
b=getappdata(0,'circle')
circularities = allPerimeters .^ 2 ./ (4 * pi * allAreas)
sortedC = sort(circularities, 'descend')
% Keep conly blobs that are nowhere close to circular or compact.
minAllowableCircularity = 10;
keeperIndexes = find(circularities >= minAllowableCircularity);
mask = ismember(labeledImage, keeperIndexes);
For some reason, when I change the value directly in the code, it works on the image, but when I put in my input in the exit text bar, it doesn't seem to work. The image gets rid of all the noise instead of amount depending on the inputted value. Attached above is an image of the layout of my GUI.

As you can see from the image above, I wanted an input of 40 for the function mask = bwareaopen(mask,500), and there is still an absence of noise in the picture. How do I fix this to easily change the parameters using the gui?
Image Analyst
on 7 Aug 2017
Edited: Image Analyst
on 7 Aug 2017
That's not right. The Value property is not the string value converted to a number. Basically, don't use the value property for edit text boxes. Use the String property and convert that to double. Do this:
smallestAllowableArea = str2double(handles.edit2.String);
mask = bwareaopen(mask, smallestAllowableArea);
Justin Johnson
on 8 Aug 2017
@jan simon: I got the image from google images so I didn't think it was a big deal.
Justin Johnson
on 8 Aug 2017
Ok, maybe i'm confused by the code here. Where in the code allows for me to adjust how much noise I would like to eliminate? I thought it was the
mask = bwareaopen(mask, 500);
but no matter what I change to numeric value to, I still get a perfect picture with no noise. I tried playing around with the code, but it still comes out to be perfect.
Jan
on 8 Aug 2017
Edited: Jan
on 8 Aug 2017
@Justin: This is a bold interpretation of the copy right. Not everything offer by Google "for free" can be copied without charges.
See https://de.123rf.com/photo_16528025_grunge-textured-concrete-sidewalk-with-cracks.html: This is a commercial service. As far as I can see, the download in the posted resolution costs 3,90€ (3 credits), but then without the visible watermark.
Image Analyst
on 8 Aug 2017
Justin, maybe you're mistakenly displaying the original image instead of the "fixed" one. Who is sponsoring your research? Can you get some images from your sponsor?
Justin Johnson
on 25 Aug 2017
@jan Simon: My apologies. I will definitely keep that in mind. Image Analyst: I figured it out. I forgot that there were two thresholds that I needed to satisfy in the code.
Justin Johnson
on 25 Aug 2017
Ok, one more thing I would like to do. How can I "manually" modify a certain area within the image? I was thinking about cropping the image proportion and strategically placing it back, but i know that there is definitely a convenient way to choose which area I want to work with.I'm not doing any real research. I'm personally interested in how deformation and cracking of ceramics and I though what cool way to learn more about coding than this. I'm really enjoying my little project because I am defiantly learning a lot.
Justin Johnson
on 25 Aug 2017
Guess i'm asking is there a function that will allow me to change a particular region of interest within a certain image? If so, what is it called so I can look into it?
Image Analyst
on 25 Aug 2017
Yes you can use indexing or masking. For indexing just operate on grayImage(row1:row2, col1:col2). If you have a non-rectangular area (a mask), operate on grayImage(mask).
Justin Johnson
on 1 Sep 2017
Yes, but I would like to manually erase noise from a particular area in the picture.How would grayImage(row1:row2, col1:col2) would allow me to do that?
Justin Johnson
on 1 Sep 2017
Also, I wold like to thank you for your help. I am going through everything i'm learning and it is really helping me understand this!
Image Analyst
on 1 Sep 2017
To manually erase some region, use imfreehand to zero out that region. I attach a demo.
Justin Johnson
on 1 Sep 2017
Am I suppose to upload an image because when I try to run it I get the error "Error using cd Cannot CD to (No directory specified). "
Image Analyst
on 1 Sep 2017
Edited: Image Analyst
on 1 Sep 2017
You can use the green and brown frame icon to the left of the paper clip icon.
Justin Johnson
on 1 Sep 2017
Edited: Justin Johnson
on 1 Sep 2017

I guess we can continue to use this image as an example.
Image Analyst
on 2 Sep 2017
That looks like the image we've already been using all along. Isn't the problem solved yet? If not, why not?
Justin Johnson
on 2 Sep 2017
No no, it is. I just wanted to know how could I take this image and instead of clearing the noise of the whole image, I just clear it at a particular section of it.
Image Analyst
on 2 Sep 2017
Create a mask, like with poly2mask() or some other way. Then you can erase non-mask regions:
maskedImage = grayImage; % Initialize
maskedImage(~mask) = 0; % Erase outside of the mask.
Then do your cleaning on maskedImage to get a new maskedImage. Then replace the masked portions of the original with the cleaned image
grayImage(mask) = maskedImage(mask);
Justin Johnson
on 5 Sep 2017
How would you upload an image? I don't understand this part of the code: "
Change the current folder to the folder of this m-file.
if(~isdeployed)
cd(fileparts(which(mfilename)));
Image Analyst
on 5 Sep 2017
Don't worry about it - delete it. Just start with the code starting at the line:
% Ask user to draw freehand mask.
Change variable name from grayImage to whatever you have.
Justin Johnson
on 5 Sep 2017
I keep getting 'Undefined function or variable 'msgbox'.' as an error message
Image Analyst
on 5 Sep 2017
msgbox() is a built-in function. You should have it unless your installation is messed up. What happens if you do this
>> which -all msgbox
>> msgbox('test');
Justin Johnson
on 5 Sep 2017
I get the error "'msgbox' not found. Undefined function or variable 'msgbox'. "
Image Analyst
on 5 Sep 2017
Your MATLAB installation is screwed up. You should definitely have msgbox() just like everyone else. Try reinstalling. If you still don't have it, then call the Mathworks for fast and free telephone help.
Justin Johnson
on 8 Sep 2017
Edited: Justin Johnson
on 8 Sep 2017
Ok so I got it to work. I was just a matter of resetting the software. Is there a way I can set grayImage equal to the file I want? I tried tried it with
'grayImage = imread(‘concrete_inverted.png’);'
and I get:
"grayImage = imread(‘concrete_inverted.png’);
↑
Error: The input character is not valid in MATLAB statements or expressions."
Image Analyst
on 8 Sep 2017
Try no double quote at the beginning of the line and standard single quotes (not fancy "smart"/slanted/curly ones) around the filename:
grayImage = imread('concrete_inverted.png');
Justin Johnson
on 9 Sep 2017
I like the nice interface it gives me, but is there a way for me to just apply the noise reduction code so just a particular region of the image?
Justin Johnson
on 12 Sep 2017
Say I just want to apply the code on a particular section of the image. How could I apply the first code you gave me to a section of my choosing?
Image Analyst
on 12 Sep 2017
I already told you and showed you above. Use masking. Fix the entire image, then combine the original image with the masked image to produce an image in which only the masked area is fixed:
grayImage(mask) = filteredImage(mask);
Justin Johnson
on 13 Sep 2017
Edited: Justin Johnson
on 15 Sep 2017
Ah ok I understand now!So in this case I apply the code to a particular area in the image and then mask it over using:
grayImage(mask) = filteredImage(mask);
If that is the case, how would I restrict the original code you gave me to just an area of the image? Which function would I use for that?
Justin Johnson
on 13 Sep 2017
I'm sorry I don't know why i'm in a state of confusion. Let me see if I can sum up my question and i'll be out of your way. I know your probably tired of me haha. 1. The free hand masking attachment you gave me doesn't seem to work or at least i'm just confused on how i'm suppose to manually remove noise from it. When apply it to my image, i just get the image back on an interface that won't let me do anything to it. Here is what I get:

I attached an image below so you can try it for yourself.
2. Could it be possible to apply the original noise reduction code to a given region of the image and mask it using
grayImage(mask) = filteredImage(mask);
if so, what function would I use to restrict the code to a given region.
I believe this is all I would like to do for this. Once again thank you for your help.
Justin Johnson
on 15 Sep 2017
Could I create a binary mask using imfreehand and then mask that with the original image?
Justin Johnson
on 15 Sep 2017
Edited: Justin Johnson
on 16 Sep 2017
Ok, so I create this code that will allow the user to free hand mask a region of interest.
grayImage = imread('concrete_inverted.png');
BW = im2bw(grayImage);
imshow(grayImage)
hFH = imfreehand();
binaryImage = createMask(hFH);
totalBinary = IInvIm(grayImage);
for k = 1:5
totalBinary = totalBinary;
imshow(totalBinary); drawnow
hFH = imfreehand();
binaryImage = createMask(hFH);
end
"IInvIm()" is a built in function I created. This function is show below:
function inverted = IInvIm(im)
grayImage = imread('concrete_inverted.png');
[rows, columns, numberOfColorChannels] = size(grayImage)
if numberOfColorChannels > 1
% If not gray scale then color.
% Uses the weighted sum of all channels to create a gray scale image.
end
% Display the image.
imshow(grayImage);
axis on;
axis image;
hp = impixelinfo();
% Binarize the image by thresholding.
BW = im2bw(grayImage);
mask = grayImage > 128;
BW = grayImage < 100;
invIm(grayImage);
% For some reason, the top two lines of his image are all white. Blacken those 2 lines:
mask(1:2, :) = false;
% Display the mask image.
% Get rid of small blobs.
mask = bwareaopen(mask, 10);
% Label the image.
labeledImage = bwlabel(mask);
% Find the areas and perimeters
props = regionprops(labeledImage, 'Area', 'Perimeter');
allAreas = [props.Area];
sortedAreas = sort(allAreas, 'descend')
allPerimeters = [props.Perimeter];
% Compute circularities
circularities = allPerimeters .^ 2 ./ (4 * pi * allAreas)
sortedC = sort(circularities, 'descend')
% Keep conly blobs that are nowhere close to circular or compact.
minAllowableCircularity = 10;
keeperIndexes = find(circularities >= minAllowableCircularity);
mask = ismember(labeledImage, keeperIndexes);
mask = ~bwareaopen(~mask,100);
imshow(mask);
axis on;
axis image; % Make sure image is not artificially stretched because of screen's aspect ratio.
drawnow;
end
____________________________________________________
Although I am able to draw my region around the area of interest, the code still will not excuse because of the error:
Error using bwlabel
Expected input number 1, BW, to be two-dimensional.
Error in bwlabel (line 65)
validateattributes(BW, {'logical' 'numeric'}, {'real', '2d', 'nonsparse'}, ...
I'm not understanding what is going on.
Justin Johnson
on 16 Sep 2017
I even tried installing another function to convert the image into a binary one and it still gives me the same error.
Justin Johnson
on 16 Sep 2017
Ok after follow your demo and modifying my code, I believe I figured it out, I just can't seem to understand why I keep getting this error message after I changed the image to binary:
Error using bwlabel
Expected input number 1, BW, to be two-dimensional.
Error in bwlabel (line 65)
validateattributes(BW, {'logical' 'numeric'}, {'real', '2d', 'nonsparse'}, ...
Error in IInvIm (line 28)
labeledImage = bwlabel(mask);
More Answers (2)
Image Analyst
on 19 Jun 2017
Threshold it to find a binary image
binaryImage = grayImage > 128;
Then take blobs only, say, 50 pixels or larger, then fill holes.
binaryImage = bwareaopen(binaryImage, 50);
binaryImage = imfill(binaryImage, 'holes');
If you then want to deal with the binary image, fine. If you want to use it to mask (blacken) the black and salty regions from your gray scale image, do this:
grayImage(~binaryImage) = 0;
John BG
on 21 Jun 2017
Edited: John BG
on 23 Jun 2017
Hi Justin Johnson
1.
to threshold the image, in order to get a binary use command imbinarize on only one RGB layer, for instance red:
clear all;close all;clc
A=imread('petri_123.jpg');
%figure(1);imshow(A);
B=imbinarize(A(:,:,1))

.
2.
B2=areaopen(B,50);figure(2);imshow(B2)
the suggested
B3=imfill(B,'holes');figure(3);imshow(B3)

.
fills up large areas that should remain black.
3.
another way is using imoverlay
Alab = rgb2lab(A);
[L,N] = superpixels(Alab,20000,'isInputLab',true);
BW = boundarymask(L);
figure(4);imshow(imoverlay(A,BW,'black'))

figure(5);imshow(imoverlay(A,BW,'red'))

% figure(10);imshow(imoverlay(A,BW,'white')) % doesn't work
pixelIdxList = label2idx(L);
meanColor = zeros(N,3);
[m,n] = size(L);
for i = 1:N
meanColor(i,1) = mean(Alab(pixelIdxList{i}));
meanColor(i,2) = mean(Alab(pixelIdxList{i}+m*n));
meanColor(i,3) = mean(Alab(pixelIdxList{i}+2*m*n));
end
numColors = 3;
[idx,cmap] = kmeans(meanColor,numColors,'replicates',2);
cmap = lab2rgb(cmap);
Lout = zeros(size(A,1),size(A,2));
for i = 1:N
Lout(pixelIdxList{i}) = idx(i);
end
B=label2rgb(Lout);
figure(6);imshow(B)

.
4.
Since the boundaries are cyan, extracting cyan
B1=B(:,:,1);B2=B(:,:,2);B3=B(:,:,3);
Lb2=find(B2);
Lb3=find(B3);
L23=intersect(Lb2,Lb3);
[Lx,Ly]=ind2sub(size(B2),L23);
D=zeros(size(B2));
for k=1:1:numel(Lx)
D(Lx(k),Ly(k))=255;
end
figure(7);imshow(D)
B13=B(:,:,1)-B(:,:,3);B23=B(:,:,2)-B(:,:,3);
figure(8);imshow(B13)
figure(9);imshow(B23) % still some noise left

.
5.
the boundaries of interest have the highest pixel values, all RGB closest to 255, so setting the threshold at for instance 252
A1=A(:,:,1);A2=A(:,:,2);A3=A(:,:,3);
B=zeros(size(A1));
ct=0;
thr=252; % threshold
for k=1:1:numel(A1)
if A1(k)>thr && A2(k)>thr && A3(k)>thr
ct=[ct k];
end
end
ct(1)=[];
[nx,ny]=ind2sub(size(B),ct);
for k=1:1:numel(nx)
B(nx(k),ny(k))=255;
end
figure(5);imshow(B)

.
if you find this answer useful would you please be so kind to consider marking my answer as Accepted Answer?
To any other reader, if you find this answer useful please consider clicking on the thumbs-up vote link
thanks in advance
John BG
3 Comments
Image Analyst
on 24 Jun 2017
Edited: Image Analyst
on 24 Jun 2017
You're right - imfill() ends up filling some of the huge black regions that are connected all the way around, instead of only the small black holes in the white struts as I intended (as I found out when I tried it). That is why I changed it from imfill() to
mask = ~bwareaopen(~mask, 1000);
in my actual solution. That only fills in the tiny black holes. It would be a nice feature if imfill() had a range that you could specify, like bwareafilt() does, so one would not have to use the above trick (which I'm sure most people wouldn't know).
Unfortunately, I think Justin has long since forgotten about his post.
John BG
on 25 Jun 2017
Image Analyst, your answer is far more accurate.
The question originator should give you the Accepted answer.
However I'd like to mention that when presenting this kind of image processing, larger images show more detail than the 4 subplots you have used.
subplot saves uploads, but showing each image in a separate figures helps spot details and choose.
Regards
John BG
Image Analyst
on 26 Jun 2017
John, yes that is true, but then I'd have to post 4 images instead of one. I'm just used to writing code to show images in a 2 by 2 array. But sometimes small details get subsampled away.
See Also
Categories
Find more on Deep Learning Toolbox in Help Center and File Exchange
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!An Error Occurred
Unable to complete the action because of changes made to the page. Reload the page to see its updated state.
Select a Web Site
Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .
You can also select a web site from the following list
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom(English)
Asia Pacific
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)