I am trying to plot the percent area of certain color pixels in an image. Need some assistance.

7 views (last 30 days)
Given this image, what is the easiest way to determine the fractional area covered by red, green, and black pixels? I am going to run this through a for loop, so that I can produce a plot like this one:
Any help would be much appreciated.
  1 Comment
John BG
John BG on 11 Jan 2017
Joe
please try my answer, attached here, and I have refurbished the explanation given earlier on as answer.
I have also added a comment regarding the use of CData.
CData is the field containing the pixel values in MATLAB class image.
we grab the image handle with
handle1=imshow(..)
and then access the pixel values with CData
handle1.CData
if you find my answer useful, would you please mark below the explanation as Accepted Answer?
Appreciating time and attention, awaiting answer
regards
John BG

Sign in to comment.

Accepted Answer

John BG
John BG on 25 Dec 2016
Edited: John BG on 11 Jan 2017
Joe Lilek
the following solves your question
% capture initial image
A=imread('im1.jpg');imshow(A)
[sz1 sz2 sz3]=size(A)
N=256;
h0=figure(1);imshow(A);title('initial image');
ColorList={'Red' 'Green' 'Blue'};
gr=0:1/(N-1):1;
% checking how many different values pixels take in this picture
P=unique(impixel(A,sz2,sz1))
% split RGB layers
A1=A(:,:,1);A2=A(:,:,2);A3=A(:,:,3);
% filtering reds
cMap=zeros(N,3);cMap(:,1)=gr;
figure(2);hr=imshow(ind2rgb(A(:,:,1),cMap));title(ColorList{1});
% filtering greens
cMap=zeros(N,3);cMap(:,2)=gr;
figure(3);hg=imshow(ind2rgb(A(:,:,2),cMap));title(ColorList{2});
% filtering blues
cMap=zeros(N,3);cMap(:,3)=gr;
figure(4);hb=imshow(ind2rgb(A(:,:,3),cMap));title(ColorList{3});
% accessing pixel values in field CData of image handle hr, for reds
R=hr.CData;
R1=R(:,:,1);R2=R(:,:,2);R3=R(:,:,3);
% nonzeros(R2) % verifying this layer should be empty
% nonzeros(R3) % verifying this layer should be empty
amount_red_pixels=numel(find(R1>=0.5));
fprintf('\n amount red pixels: %s \n', num2str(amount_red_pixels));
% sz1*sz2 % total amount of pixels in initial image
pc_red_pixels=round(100*numel(find(R1>=0.5))/(sz1*sz2)); % percentage of red pixels
fprintf('\n percentage red pixels: %s %%\n', num2str(pc_red_pixels));
% accessing pixel values in field CData of image handle hr, for greens
G=hg.CData;
amount_green_pixels=numel(find(G>=0.5));
fprintf('\n amount green pixels: %s \n', num2str(amount_green_pixels));
pc_green_pixels=round(100*numel(find(G>=0.5))/(sz1*sz2));
fprintf('\n percentage green pixels: %s %%\n', num2str(pc_green_pixels));
% accessing pixel values in field CData of image handle hr, for blues
B=hb.CData;
B1=B(:,:,1);B2=B(:,:,2);B3=B(:,:,3);
amount_blue_pixels=numel(find(B1>=0.5));
fprintf('\n amount red pixels: %s \n', num2str(amount_blue_pixels));
pc_blue_pixels=round(100*numel(find(B1>=0.5))/(sz1*sz2));
fprintf('\n percentage blue pixels: %s %%\n', num2str(pc_blue_pixels));
EXPLANATION:
1.
the image is not saturated.
  • it does not use exclusively RGB pixels: there are other pixel values than [1 0 0] [0 1 0] [0 0 1]
  • the majority of pixels are not saturated: there are all sorts of levels between 0 and 1.
By this I mean that when reading a few pixels
A=imread('im1.jpg');% imshow(A)
[sz1 sz2 sz3]=size(A)
P=impixel(A)
P =
221 19 5
68 0 0
68 219 16
208 18 18
204 28 15
43 0 0
155 98 17
128 0 0
81 201 52
1 8 1
243 6 0
4 0 0
3 1 4
7 2 0
one realises that when asking 'how many greens?' one is asking for how many pixels are [0 0 1] or more according to what human eye perceives as green, how pixels comply with
(r<0.1) && (g<0.1) && (b>0.9)
2.
splitting the 3 layers of RGB and then counting pixels >0 in each layer throughout an image that has the majority of pixels Red Green or Black is going to count as green pixels that should be accounted as black. For instance pixel in position [125 65] has value [0 5 0], it's perceived by eye as pitch black yet the >0 counting will put in the green count.
3.
to address this I suggest:
N=256;
h0=figure(1);imshow(A);title('initial image');
ColorList={'Red' 'Green' 'Blue'};
gr=0:1/(N-1):1;
cMap=zeros(N,3);cMap(:,1)=gr;figure(2);hr=imshow(ind2rgb(A(:,:,1),cMap));title(ColorList{1});
cMap=zeros(N,3);cMap(:,2)=gr;figure(3);hg=imshow(ind2rgb(A(:,:,2),cMap));title(ColorList{2});
cMap=zeros(N,3);cMap(:,3)=gr;figure(4);hb=imshow(ind2rgb(A(:,:,3),cMap));title(ColorList{3});
.
.
4.
now the colours are separated yet many pixel components are not entirely 1 or 0
.
moving the cursor, my subjective perception would be that a reasonable threshold is 0.5 feel free to set such threshold on any other value you consider convenient to discern whether a pixel is black or red, and for the case, green or black, and blue or black.
The blue case for this particular picture is simple because there are no blue pixels.
5.
So, how many reds?
R=hr.CData;R1=R(:,:,1);R2=R(:,:,2);R3=R(:,:,3);
as expected R2 and R3 are empty
nonzeros(R2)
=
Empty matrix: 0-by-1
nonzeros(R3)
=
Empty matrix: 0-by-1
with the chosen threshold the amount of red pixels is
numel(find(R1>=0.5))
=
32765
out of
sz1*sz2
=
93328
with such criteria the percentage of red pixels is:
round(100*numel(find(R1>=0.5))/(sz1*sz2))
=
35
.
6.
How many greens?
G=hg.CData;
numel(find(G>=0.5))
sz1*sz2
round(100*numel(find(G>=0.5))/(sz1*sz2))
=
14859
=
93328
=
16
note that G is slightly different than R, this is because I have reused code. Have a look to Mr Rosson function imColorSep.m available from https://uk.mathworks.com/matlabcentral/fileexchange/18125-rgb-image-decomposition/content/imColorSep.m Rosson's function does not return handles to each colour layer, and uses subplot, I rather different figure windows.
.
7.
And how many blues
B=hb.CData;B1=B(:,:,1);B2=B(:,:,2);B3=B(:,:,3);
numel(find(B1>=0.5))
sz1*sz2
round(100*numel(find(B1>=0.5))/(sz1*sz2))
=
0
ans =
93328
ans =
0
confirmed, black is not blue, there are no blue pixels in this image.
if you find my answer useful would you please mark it as Accepted Answer by clicking on the ACCEPT ANSWER button?
thanks in advance for time and attention
John BG
  4 Comments
Image Analyst
Image Analyst on 11 Jan 2017
Where did you get that image? When I downloaded Joe's image from his original question, it was PNG format and called example.png, not JPEG format and called im1.jpg. And since it was PNG there were no jpeg artifacts and all the pixels were pure red, black, or green, not blended colors like you showed. How did you get im1.jpg? It looks like the same image but someone jpegged the heck out of it - doesn't look like what I downloaded which is nice and sharp. How did that happen?
John BG
John BG on 11 Jan 2017
Edited: John BG on 11 Jan 2017
the code you supply should be able to stand the whole range different pixel values, not just 0 or 255.
Yes, the image supplied may be all saturated, yet the human eye perceives as saturated pixel values that are close to 255, like 250, or even almost half way, for instance a pixel 140 may be perceived as 'quite red'.
It would be a weak solution if it only works for [0 255].
Then pixels with values 254 or even 140 would be excluded yet they are clearly perceived as 'almost' saturated, therefore here it's better to take advantage of the blurring caused by jpeg coding to show a more robust solution.
For this question the image format is irrelevant, as long as you can access the RGB components of course.
It's understandable that an expert like you chooses to use .png as often as possible, good to avoid losing a bit of detail, that may be critical in clinic imaging, for instance the outcome of an X-ray.
But for the perception of saturated colors, having 100% accuracy is not that important.
Please try the attached code and example image, it works perfectly fine.
And the code you have provided mixes pure reds with pixels that happen to have red saturated but also have another primary 'quite' or 'enough' saturated.
Rewording: Your code would take browns as reds and yellows as reds. Red and only red pixels should be accounted for in the red pixels count, yet your code puts in red any pixels only considering 'high enough' red component of the pixel, which is erroneous.
Regards
John BG

Sign in to comment.

More Answers (1)

Image Analyst
Image Analyst on 14 Dec 2014
For each time you must have an image. then you must do color segmentation. For a computer graphic image like this, with pure R, G, or B colors, it's very simple and you can do it in RGB color space.
% Extract the individual red, green, and blue color channels.
redChannel = rgbImage(:, :, 1);
greenChannel = rgbImage(:, :, 2);
blueChannel = rgbImage(:, :, 3);
% Get the dimensions of the image. numberOfColorBands should be = 3.
[rows, columns, numberOfColorBands] = size(rgbImage);
% Count pure red pixels
redCount = sum(redChannel(:) > 0);
greenCount = sum(greenChannel(:) > 0);
% Compute area fractions.
redAreaFraction = redCount / (rows * columns);
greenAreaFraction = greenCount / (rows * columns);
For more challenging colors, see the color segmentation demos in my File Exchange: http://www.mathworks.com/matlabcentral/fileexchange/?term=authorid%3A31862
  5 Comments
srdr57
srdr57 on 23 Dec 2017
I tested your codes on my image, and found unrealistic results. It gives redAreaFraction = 0.91 and greenAreaFraction = 0.87 at the same time. I think the both must be less than 1
Image Analyst
Image Analyst on 23 Dec 2017
The code counted the fraction of pixels that had any amount of red in them, and that had any amount of green in them. So, with that definition, it's correct. Sounds like you might want to know the fraction where the red component has the highest value and where the green component has the highest value. To do that you'd do something different. Try this:
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
workspace; % Make sure the workspace panel is showing.
format long g;
format compact;
fontSize = 20;
%===============================================================================
% Get the name of the first image the user wants to use.
baseFileName = 'peppers.png';
folder = fileparts(which(baseFileName)); % Determine where demo folder is (works with all versions).
fullFileName = fullfile(folder, baseFileName);
% Check if file exists.
if ~exist(fullFileName, 'file')
% The file doesn't exist -- didn't find it there in that folder.
% Check the entire search path (other folders) for the file by stripping off the folder.
fullFileNameOnSearchPath = baseFileName; % No path this time.
if ~exist(fullFileNameOnSearchPath, 'file')
% Still didn't find it. Alert user.
errorMessage = sprintf('Error: %s does not exist in the search path folders.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
%=======================================================================================
% Read in demo image.
rgbImage = imread(fullFileName);
% Get the dimensions of the image.
[rows, columns, numberOfColorChannels] = size(rgbImage);
% Display the original image.
subplot(2, 3, 1);
imshow(rgbImage, []);
axis on;
caption = sprintf('Original Color Image, %s', baseFileName);
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
% Set up figure properties:
% Enlarge figure to full screen.
set(gcf, 'Units', 'Normalized', 'OuterPosition', [0 0.05 1 0.95]);
% 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;
hp = impixelinfo(); % Set up status line to see values when you mouse over the image.
[highestValues, sortOrder] = sort(rgbImage, 3, 'descend');
% Get masks
redMask = sortOrder(:,:,1) == 1;
greenMask = sortOrder(:,:,1) == 2;
blueMask = sortOrder(:,:,1) == 3;
% Display the mask image.
subplot(2, 3, 2);
imshow(redMask, []);
axis on;
caption = sprintf('Red Mask Image');
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
% Display the mask image.
subplot(2, 3, 3);
imshow(greenMask, []);
axis on;
caption = sprintf('Green Mask Image');
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
% Display the mask image.
subplot(2, 3, 4);
imshow(blueMask, []);
axis on;
caption = sprintf('Blue Mask Image');
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
% Display the highest Value image.
subplot(2, 3, 5);
imshow(highestValues(:, :, 1), []);
axis on;
caption = sprintf('Highest Value Image');
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
hp = impixelinfo(); % Set up status line to see values when you mouse over the image.
% Get the area fractions.
redAreaFraction = sum(redMask(:)) / numel(redMask)
greenAreaFraction = sum(greenMask(:)) / numel(greenMask)
blueAreaFraction = sum(blueMask(:)) / numel(blueMask)

Sign in to comment.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!