how to count number of color in an image in matlab - matlab

Actually, I am new in Matlab. I don't know much about it.
How can I count number of color in am image?
For an example I have an image of a DNA microarray (which is an RGB image) and I want to count how many yellow dot, red dot and green dot it has.

If I is your image you can use this:
size(unique(reshape(I, [], 3), 'rows'), 1)
First reshape the image to an nx3, where n stands for the number of pixels in your image, the apply the unique function to collapse equal rows (pixel with same colour) and finally the size of the resulting matrix will give you the number of unique colours.

If you are using RGB then it too depends on whether it's 24, 32 or 64 bit.
e.g. for 24 bit image, there are 256*256*256 = 16,777,216 of possible combinations of RGB. ;)
Wouldn't it be easier to separate the colours R, G, B, then you count each of those separated subplots ? ;)
Try this out:
get each R, G, B channels out. Then do a histogram on it to count each R, G, B pixels.
DNA = imread('peppers.png');
redChannel = DNA(:, :, 1);
greenChannel = DNA(:, :, 2);
blueChannel = DNA(:, :, 3);
For red:
[redCount grayCount] = imhist(redPlane);
subplot(2, 2, 2);
imshow( DNA(:,:,2));
>> redcount
ans = 38385 // SAMPLE count
You may use a loop from 1:3 if you prefer so.

If you have single pixel dots and the "red", "yellow" and "green" means exactly [255 0 0], [0 255 255] and [0 255 0], then your question can be answered quite simple:
red_dots = (DNA(:,:,1)==255);
n_red_dots = sum(red_dots(:));
yellow_dots = (DNA(:,:,2)==255 & DNA(:,:,3)==255);
n_yellow_dots = sum(yellow_dots(:));
green_dots = (DNA(:,:,2)==255);
n_green_dots = sum(green_dots(:));
if your colors are not that exact, but still single-pixel (or they are around the same size and you are rather interested in rough proportions), you can do thresholding:
redish_dots = (DNA(:,:,1)>200 & DNA(:,:,2)<40 & DNA(:,:,3)<40);
n_redish_dots = sum(redish_dots(:));
and so on...

Related

Accurately detect color regions in an image using K-means clustering

I'm using K-means clustering in color-based image segmentation. I have a 2D image which has 3 colors, black, white, and green. Here is the image,
I want K-means to produce 3 clusters, one represents the green color region, the second one represents the white region, and the last one represents the black region.
Here is the code I used,
%Clustering color regions in an image.
%Step 1: read the image using imread, and show it using imshow.
img = (imread('img.jpg'));
figure, imshow(img), title('X axis rock cut'); %figure is for creating a figure window.
text(size(img,2),size(img,1)+15,...
'Unconventional shale x axis cut', ...
'FontSize',7,'HorizontalAlignment','right');
%Step 2: Convert Image from RGB Color Space to L*a*b* Color Space
conversionform = makecform('srgb2lab'); %the form of the conversion is defined as from rgb to l a b
lab_img = applycform(img,conversionform); %converting the rgb image to l a b image using the conversion form defined above.
%Step 3: Classify the Colors in 'a*b*' Space Using K-Means Clustering
ab = double(lab_img(:,:,2:3));
nrows = size(ab,1);
ncols = size(ab,2);
ab = reshape(ab,nrows*ncols,2);
nColors = 3;
% repeat the clustering 3 times to avoid local minima
[cluster_idx, cluster_center] = kmeans(ab,nColors,'distance','sqEuclidean', ...
'Replicates',3);
%Step 4: Label Every Pixel in the Image Using the Results from KMEANS
%For every object in your input, kmeans returns an index corresponding to a cluster. The cluster_center output from kmeans will be used later in the example. Label every pixel in the image with its cluster_index.
pixel_labels = reshape(cluster_idx,nrows,ncols);
figure, imshow(pixel_labels,[]), title('image labeled by cluster index');
segmented_images = cell(1,3);
rgb_label = repmat(pixel_labels,[1 1 3]);
for k = 1:nColors
color = img;
color(rgb_label ~= k) = 0;
segmented_images{k} = color;
end
figure, imshow(segmented_images{1}), title('objects in cluster 1');
figure, imshow(segmented_images{2}), title('objects in cluster 2');
figure, imshow(segmented_images{3}), title('objects in cluster 3');
But I'm not getting the results as required. I get one cluster with green regions, one cluster with green region boundaries, and one with gray, black, and white colors. Here are the resulting clusters.
The aim of doing this is that after getting the correct clustering results, I want to count the number of pixels in every region using the concept of connected components.
So, my aim is to know how many pixels there are in every color region. I tried another simpler way by getting the matrix of the 2D image and trying to figure out the number of pixels for every color. However, I found more than 3 RGB colors in the matrix, maybe because pixels of the same color have a slightly different color levels. That's why I went to image segmentation.
Can anyone please tell me how to fix the code above in order to get the required results?
I would also appreciate it if you give me hints on how to do this in an easier way, if there is any.
EDIT: Here is a code I made to iterate over every pixel in the image. Please notice I use 4 colors red, yellow, blue, and white instead of green, white, and black, but the idea is the same. rgb2name is the function that returns the color name given RGB color.
im= imread ('img.jpg');
[a b c] = size (im);
%disp ([a b]);
yellow=0;
blue=0;
white=0;
red=0;
for i=1:a
for j=1:b
x= impixel(im, i, j)/255 ;
color= rgb2name (x);
if (~isempty (strfind (color, 'yellow')))
yellow= yellow+1;
elseif (~isempty (strfind(color, 'red')))
red= red+1;
elseif (~isempty (strfind (color, 'blue')))
blue= blue+1;
elseif (~isempty (strfind (color, 'white')))
white= white+1;
else
%disp ('warning'); break;
end
disp (color);
disp (i);
end
end
disp (yellow)
disp (red)
disp (blue)
disp (white)
Thank You.
I thought this problem was very interesting, so I apologize ahead of time if the answer is a little overboard. In short, k-means is the right strategy, in general, for problems where you want to segment an image into a discrete color space. But, your example image, which contains primarily only three colors, each of which is well separated in color space, is easily segmented using only a histogram. See below for segmenting using thresholds.
You can easily get the pixel counts by summing each matrix. e.g., bCount = sum(blackPixels(:))
filename = '379NJ.png';
x = imread(filename);
x = double(x); % cast to floating point
x = x/max(max(max(x))); % normalize
% take histogram of green dimension
g = x(:, :, 2);
c = hist(g(:), 2^8);
% smooth the hist count
c = [zeros(1, 10), c, zeros(1, 10)];
N = 4;
for i = N+1:length(c) - N;
d(i - N) = mean(c(i -N:i));
end
d = circshift(d, [1, N/2]);
% as seen in histogram, the three colors fall nicely into 3 peaks
figure, plot(c, '.-');
[~, clusterCenters] = findpeaks(d, 'MinPeakHeight', 1e3);
% set the threshold halfway between peaks
boundaries = [floor((clusterCenters(2) - clusterCenters(1))/2), ...
clusterCenters(2) + floor((clusterCenters(3) - clusterCenters(2))/2)];
thresh1 = boundaries(1)*ones(size(g))/255;
thresh2 = boundaries(2)*ones(size(g))/255;
% categorize based on threshold
blackPixels = g < thresh1;
greenPixels = g >= thresh1 & g < thresh2;
whitePixels = g >= thresh2;
This is my approach to count the number of pixels in every region. Given that (as discussed in the comments):
the value (RGB) and the number (K) of colors are known a priori
compression artifacts and anti-aliasing generated additional colors, that must be considered as the nearest-neighbor among the K know colors.
Since you know a priori the colors, you don't need k-means. It could actually lead to bad results as in your question. The approach of #crowdedComputeeer take care of this aspect.
You can compute nearest neighbor with pdist2 directly on the pixel values. There's no need to use the really slow function that looks for the color name.
Here is the code. You can change the number and values of colors simply modifying the variable colors. This will compute the number of pixels in each color, and output the masks.
img = (imread('path_to_image'));
colors = [ 0 0 0; % black
0 1 0; % green
1 1 1]; % white
% % You can change the colors
% colors = [ 0 0 1; % red
% 1 1 0; % yellow
% 1 0 0; % blue
% 1 1 1]; % white
% Find nearest neighbour color
list = double(reshape(img, [], 3)) / 255;
[~, IDX] = pdist2(colors, list, 'euclidean', 'Smallest', 1);
% IDX contains the indices to the nearest element
N = zeros(size(colors, 1), 1);
for i = 1 : size(colors, 1)
% Count the number of pixels for each color
N(i) = sum( IDX == i );
end
% This will display the number of pixels for each color
disp(N);
% Eventually build the masks
indices = reshape(IDX, [size(img,1), size(img,2)]);
figure();
szc = size(colors,1);
for i = 1 : szc
subplot(1,szc,i);
imagesc(indices == i);
end
Resulting counts:
97554 % black
16894 % green
31852 % white
Resulting masks:
Maybe this project could help, please take a try.

How to convert RGB images to grayscale in matlab without using rgb2gray

I'm currently using code:
i = imread('/usr/share/icons/matlab.png');
for k=1:1:m
for l=1:1:n
%a(k,l)=m*n;
a(k,l) = (.299*i(k,l,1))+(.587*i(k,l,2))+(.114*i(k,l,3));
end
end
imshow(a);
It shows only a white screen. Also the newly generated dimensions are n x m x 3 whereas it should be only m x n x 1.
If I use mat2gray it display the image like this
Since the image is a PNG, imread() is returning an integer image, with intensity values in the range [0 255] or equivalent, depending on the original bit depth. The conversion formula makes a a double image, which is expected to have intensities in the range [0 1]. Since all the pixel values in a are probably much greater than 1, they get clipped to 1 (white) by imshow().
The best option is to explicitly convert the image format before you start - this will take care of scaling things correctly:
i = imread('/usr/share/icons/matlab.png');
i = im2double(i);
a = .299*i(:,:,1) + .587*i(:,:,2) + .114*i(:,:,3); % no need for loops
imshow(a);
input=imread('test.jpg');
subplot(1,2,1), imshow(input), title('RGB Scale image');
[x,y,~] = size(input);
for i = 1:1:x
for j = 1:1:y
output(i,j) = 0.40*input(i,j,1) + 0.50*input(i,j,2) + 0.30*input(i,j,3);
end
end
subplot(1,2,2), imshow(output), title('Gray Scale image');

MATLAB: How to 'color in' pixels of a grayscale image sequence?

Supposing I have the linear indices of the pixels which I would like to 'color in'.
If I wanted to set all of those pixels to some specific value in my grayscale image sequence, I could do this:
gryscl_imSeq(LinearPixIndx) = 0.7;
edit: where 'LinearPixIndx' is a p-element vector containing the linear indices of the pixels in the image sequence.
If however, I want to introduce some color, I would first need to convert my grayscale image sequence into an m x n x 3 x p matrix, which I could do like this:
RGBvideo = reshape(repmat(reshape(gryscl_imSeq,[],size(gryscl_imSeq,3)),3,1),...
[size(gryscl_imSeq,1) size(gryscl_imSeq,2) 3 size(gryscl_imSeq,3)]);
If somebody could tell me how to convert my grayscale linear pixels indices into corresponding indices for the RGBvideo I would really appreciate it.
Or if there is a better way to go about what I'm trying to do, I would be very grateful for any suggestions.
Thanks in Advance,
N
The question isn't really clear, but I guess I understand what you want. Here's one way to do it.
First I think there's a slightly more elegant way to convert from grayscale to RGB:
% determine image sequence dimensions
[m, n, N] = size(gryscl_imSeq);
% expand to RGB
rgb_imSeq = reshape(gryscl_imSeq, [m, n, 1, N]);
rgb_imSeq = repmat(rgb_imSeq, [1, 1, 3, 1]);
After that, in order to be able to apply your linear indices to all color channels and all frames, you need to explicitly linearize the pixel index by reshaping. Then you can color the pixels, and transform back into the form width x height x color channels x frames:
% linearize image dimensions
rgb_imSeq = reshape(rgb_imSeq, [m * n, 3, N]);
% color pixels
rgb_imSeq(LinearPixIndx, :, :) = repmat([1 0 0], [numel(LinearPixIndx), 1, N]);
% original shape
rgb_imSeq = reshape(rgb_imSeq, [m, n, 3, N]);
Here [1 0 0] is the RGB representation of the color you want to use, in this case red.

MATLAB: Find indices/count of specific rgb pixel values in image

I'm using matlab to get counts of specific pixel values in an image.
Images are RGBA <512x512x4 uint8> when read into matlab (although we can disregard the alpha channel).
Other than;
[width, height, depth] = size(im);
for x = 1 : width;
for y = 1: height;
r = im(x,y,1);
g = im(x,y,2);
b = im(x,y,3);
...
end
end
Is there a way I can do this using matrix operations? Something along the lines of:
X = find(im(:,:,1) == 255 && im(:,:,2) == 255 && im(:,:,3) == 255);
count = length(X);
% Count being the number of pixels with RGB value (255,255,255) in the image.
I'm guessing there's more than a few ways to do this (looking at intersect, unique functions) but I'm not clever enough with matlab to do this yet. Any help?
It's actually pretty simple. Something like this
count = sum(im(:, :, 1) == 255 & im(:, :, 2) == 255 & im(:, :, 3) == 255);
will give you the count of such pixels. Replace sum with find to get the indices of those pixels if you need that.
You can do it with many ways.
One way is this.
Let's say your image is HxWx3
create a HxW table with the r value you want to search for, one HxW for the g and one for the blue. You can combine all thos tables as dimensions in a HxWx3 table F.
Substract F from im and use the find() function to get the indexes of zeroed values.
F(:,:,1)=R*ones(H,W); F(:,:,2)=G*ones(H,W); F(:,:,3)=B*ones(H,W);
then if you do im-F you get zeroes on the wanted positions
d=F-im; [r,c]=find((d(:,:,1))==0)
That way you can input also a threshold of how close you want the rgb set to be.

rgb of an image

To get RGB values:
RGB = imread('C:\Documents and Settings\student2\Desktop\Water lilies.jpg');
R = RGB(:, :, 1);
G = RGB(:, :, 2);
B = RGB(:, :, 3);
Can someone tell me why we use 1, 2, and 3 to get Red, Green, and Blue matrices respectively?
If you look at the size of RBG: size(RGB), you will see that it is width X height X 3. Each pixel is represented by 3 values - red, green, and blue; the actual colour of the pixel is a mixture of these primary colour - Wikipedia.
If you want to know the reason why R is 1, G is 2 and B is 3, rather than R being 3 and B being 1 or something, it is just convention. I assume because red light is a lower frequency of light, blue is higher and green is in between - Wikipedia.