k-means segmentation of an image - matlab

I came across the following code, which segments an image using num no. of clusters, via the k-means clustering algorithm. However, I could not understand the meaning of the second statement in the first for loop. Please help me understand what the statement is doing and also what ~= means(here).
Also, when I run the code, I get the following error:
??? Attempt to grow array along ambiguous dimension.
Error in ==> kmeansseg at 42
color(rgb_label ~= k) = 0;
It seem that I get this error for every value other than num=3. So, does it mean that I cannot cluster an rgb image into more than 3 colors? The input image has 6 colors that I can tell apart. Can someone suggest a solution for this?
Function call:
>> f=imread('peppers.png');
>> kmeansseg(f,6)
Here is the code:
function kmeansseg(im,num)
figure(1),imshow(im), title('original image');
cform = makecform('srgb2lab');
lab_im = applycform(im,cform);
ab = double(lab_im(:,:,2:3));
nrows = size(ab,1);
ncols = size(ab,2);
ab = reshape(ab,nrows*ncols,2);
nColors = num;
[cluster_idx, cluster_center] = kmeans(ab,nColors,'distance','sqEuclidean', ...
'Replicates',3);
pixel_labels = reshape(cluster_idx,nrows,ncols);
figure(2),imshow(pixel_labels,[]), title('image labeled by cluster index');
segmented_images = cell(1,nColors);
rgb_label = repmat(pixel_labels,[1 1 nColors]);
for k = 1:nColors
color = im;
color(rgb_label ~= k) = 0; %meaning?
segmented_images{k} = color;
end
figure(3),imshow(segmented_images{1}), title('objects in cluster 1');
figure(4),imshow(segmented_images{2}), title('objects in cluster 2');
figure(5),imshow(segmented_images{3}), title('objects in cluster 3');
end
end

It is setting to zero any elements of the image that don't correspond to that particular label. That's how you get a series of segmented images. It gets the segregation labels from the rgb_label variable.
What ~= means there is "for every pixel of the segmentation image is NOT equal to the current segmentation number, set the image pixel to zero, leaving the other image pixels unchanged"
In regards to your edit - it looks like the color and rgb_label matrices do not have the same dimensions.

Related

Assigning colors to segmented pictures in MATLAB

I was dealing with color segmentation with MATLAB. I used k-means clustering based on this document and come this far codewise;
global imgRGB;
global imgLAB;
img = imgRGB;
cform = makecform('srgb2lab');
imgLAB = applycform(img, cform);
ab = double(imgLAB(:,:,2:3));
rows = size(ab,1)
cols = size(ab,2);
ab = reshape(ab, rows*cols, 2);
cluster = 5;
[idx center] = kmeans(ab, cluster, 'distance', 'sqEuclidean', 'Replicates', 5);
label = reshape(idx, rows, cols);
figure; imshow(label, []);
imgSeg = cell(5);
rgb_label = repmat(pixel_labels, [1 1 3]);
for k=1:cluster
color = img;
color(rgb_label ~= k) = 0;
imgSeg{k} = color;
end
figure;
imshow(imgSeg{1});
I take image as input that is why it is defined global.
For a colored image like the one in link, it produces the grayscale output.
I think it assigns gray tones as colors but I need to assign a color to each cluster. I mean not gray tone but a color. How can I achieve that?
You should use a different colormap for your figure, when displaying the labels directly.
Try:
figure;
imshow(label, []);
colormap( rnad(max(imgSeg{1}(:))+1, 3) ); % use random color map
However, if you wish to convert the pixel_labels to an RGB image (3 color channels per pixel), you want to use ind2rgb (instead of replicating the labels to all channels). Replace rgb_label = repmat(pixel_labels, [1 1 3]); with
rgb_label = ind2rgb(pixel_labels, rand(max(pixel_labels(:)),3));

Removing colored lines in Matlab

I am attempting to delete colored lines (specifically a yellow and blue line) within a series of images in Matlab. An example image can be found here:
I am able to segment out the blue line segments using basic thresholding. I am also able to segment out the bright yellow circles within the yellow line segment using thresholding. Finally, I am working on removing the remaining elements of the line segment using a hough transform w/ the houghlines function and a mask.
Is there a more elegant way to perform this, or am I stuck employing this combination of methods?
Thanks
Edit: I discovered that the hough transform is only removing single pixels from my image and not the entire yellow line. I was contemplating dilating around the detected pixels and checking for similarity, but I'm worried that the yellow line is too similar to the background colors (it's position could change such that it is not fully tracking the dark background it happens to be over now). Any suggestions would be greatly appreciated.
%% This block was intended to deal with another data
set this function has to analyze, but it actually ended up removing my
yellow circles as well, making a further threshold step unnecessary so far
% Converts to a binary image containing almost exclusively lines and crosshairs
mask = im2bw(rgb_img, 0.8);
% Invert mask
mask = ~mask;
% Remove detected lines and crosshairs by setting to 0
rgb_img(repmat(~mask, [1, 1, 3])) = 0;
%% Removes blue targetting lines if present
% Define thresholds for RGB channel 3 based on histogram settings to remove
% blue lines
channel3Min = 0.000;
channel3Max = 0.478;
% Create mask based on chosen histogram thresholds
noBlue = (rgb_img(:,:,3) >= channel3Min ) & (rgb_img(:,:,3) <= channel3Max);
% Set background pixels where noBlue is false to zero.
rgb_img(repmat(~noBlue,[1 1 3])) = 0;
%% Removes any other targetting lines if present
imageGreyed = rgb2gray(rgb_img);
% Performs canny edge detection
BW = edge(imageGreyed, 'canny');
% Computes the hough transform
[H,theta,rho] = hough(BW);
% Finds the peaks in the hough matrix
P = houghpeaks(H,5,'threshold',ceil(0.3*max(H(:))));
% Finds any large lines present in the image
lines = houghlines(BW,theta,rho,P,'FillGap',5,'MinLength',100);
colEnd = [];
rowEnd = [];
for i = 1:length(lines)
% Extracts line start and end points from houghlines output
pointHold = lines(i).point1;
colEnd = [colEnd pointHold(1)];
rowEnd = [rowEnd pointHold(2)];
pointHold = lines(i).point2;
colEnd = [colEnd pointHold(1)];
rowEnd = [rowEnd pointHold(2)];
% Creates a line segment from the line endpoints using a simple linear regression
fit = polyfit(colEnd, rowEnd, 1);
% Creates index of "x" (column) values to be fed into regression
colIndex = (colEnd(1):colEnd(2));
rowIndex = [];
% Obtains "y" (row) pixel values from regression
for i = colIndex
rowHold = fit(1) * i + fit(2);
rowIndex = [rowIndex rowHold];
end
% Round regression output
rowIndex = round(rowIndex);
% Assemble coordinate matrix
lineCoordinates = [colIndex; rowIndex]';
rgbDim = size(rgb_img);
% Create mask based on input image size
yellowMask = ones(rgbDim(1), rgbDim(2));
for i = 1:length(rowIndex)
yellowMask(rowIndex(i), colIndex(i)) = 0;
end
% Remove the lines found by hough transform
rgb_img(repmat(~yellowMask,[1 1 3])) = 0;
end
end
I briefly tested the example given on
http://de.mathworks.com/help/images/examples/color-based-segmentation-using-k-means-clustering.html?prodcode=IP&language=en
using your image which is:
he = imread('HlQVN.jpg');
imshow(he)
cform = makecform('srgb2lab');
lab_he = applycform(he,cform);
ab = double(lab_he(:,:,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);
pixel_labels = reshape(cluster_idx,nrows,ncols);
segmented_images = cell(1,3);
rgb_label = repmat(pixel_labels,[1 1 3]);
for k = 1:nColors
color = he;
color(rgb_label ~= k) = 0;
segmented_images{k} = color;
end
imshow(segmented_images{1}), title('objects in cluster 1');
this already pretty well identifies the blue line.
This post won't go into the image processing side of the problem, but rather just focuses on the implementation and would suggest ways to improve the existing code. Now, the code has polyfit calculation at each loop iteration, which I am not sure could be vectorized. So, rather let's try to vectorize rest of the codes inside the loop and hopefully that would bring in some speedup for the overall code. The changes I would like to propose are at two steps within the innermost loop.
1) Replace -
rowIndex=[]
for i = colIndex
rowHold = fit(1) * i + fit(2)
rowIndex = [rowIndex rowHold];
end
with -
rowIndex = fit(1)*colIndex + fit(2)
2) Replace -
yellowMask = ones(rgbDim(1), rgbDim(2));
for i = 1:length(rowIndex)
yellowMask(rowIndex(i), colIndex(i)) = 0;
end
rgb_img(repmat(~yellowMask,[1 1 3])) = 0;
with -
idx1 = (colIndex-1)*rgbDim(1) + rowIndex
rgb_img(bsxfun(#plus,idx1(:),[0:rgbDim(3)-1]*rgbDim(1)*rgbDim(2))) = 0;
It turns out that the answer involved converting the image into the Lab colorspace and performing treshholding. This segmented out the lines with minimal loss in the rest of the image. The code is below:
% Convert RGB image to L*a*b color space for thresholding
rgb_img = im2double(rgb_img);
cform = makecform('srgb2lab', 'AdaptedWhitePoint', whitepoint('D65'));
I = applycform(rgb_img,cform);
% Define thresholds for channel 2 based on histogram settings
channel2Min = -1.970;
channel2Max = 48.061;
% Create mask based on chosen histogram threshold
BW = (I(:,:,2) <= channel2Min ) | (I(:,:,2) >= channel2Max);
% Determines the eccentricity for regions of pixels; basically how line-like
% (vals close to 1) or circular (vals close to 0) the region is
rp = regionprops(BW, 'PixelIdxList', 'Eccentricity');
% Selects for regions which are not line segments (areas which
% may have been incorrectly thresholded out with the crosshairs)
rp = rp([rp.Eccentricity] < 0.99);
% Removes the non-line segment regions from the mask
BW(vertcat(rp.PixelIdxList)) = false;
% Set background pixels where BW is false to zero.
rgb_img(repmat(BW,[1 1 3])) = 0;

Draw line to connect centroids

I have an image
.
After I process to find centroid, it has four centroids.
My goal is I want to connect them using line and measure the angle between this area. To be clear about the centroid and my goal, you can open .
Here it is my code to achieve the centroid
I = imread('22c.jpg');
Ibw = im2bw(I);
Ibw = imfill(Ibw,'holes');
Ilabel = bwlabel(Ibw);
stat = regionprops(Ilabel,'centroid');
imshow(I); hold on;
for x = 1: numel(stat)
plot(stat(x).Centroid(1),stat(x).Centroid(2),'ro');
end
The problem is I am still confused to do the next (to connect each centroids and measure the angle). I need your help, thanks
Here is a file exchange link to bresenham.m
Changed your code to get all the 4 centroids
%// read your input image
im = imread('http://i.stack.imgur.com/xeqe8.jpg');
BW = im>220;
CC = bwconncomp(BW);
stat = regionprops(CC,'Centroid');
figure; imshow(BW); hold on
for x = 1: numel(stat)
plot(stat(x).Centroid(1),stat(x).Centroid(2),'ro');
end
Here is the output:
Further implementation:
%// putting all the Centroid coordinates into corresponding x,y variable
x = [stat(1).Centroid(1),stat(2).Centroid(1),stat(3).Centroid(1),stat(4).Centroid(1)];
y = [stat(1).Centroid(2),stat(2).Centroid(2),stat(3).Centroid(2),stat(4).Centroid(2)];
%// obtain row and col dim
[r,c] = size(BW);
%// get all x,y values connecting the centroid points
[xAll{1},yAll{1}] = bresenham(x(1),y(1),x(4),y(4));
[xAll{2},yAll{2}] = bresenham(x(2),y(2),x(3),y(3));
[xAll{3},yAll{3}] = bresenham(x(3),y(3),x(4),y(4));
%// change row and col subs to linear index
for ii = 1:3
idx{ii} = sub2ind(size(BW),yAll{ii},xAll{ii});
end
%// change grayscale image to 3D (as you want red line)
out = repmat(im,[1,1,3]);
%// obtaining corresponding index of all 3 slices
for ii = 1:3
idxall{ii} = bsxfun(#plus, idx{ii},[0:2].*(r*c));
end
%// keep only the index of 1st slice to 255 and changing rest to 0 to obtain a red line.
%// Similar process for blue line except keep the index in the 3rd slice to 255
out(cat(1,idxall{:})) = 0;
out(idx{1}) = 255;
out(idx{2}) = 255;
out(idx{3}+2*(r*c)) = 255;
%// see what you have obtained
figure; imshow(out);hold on
for x = 1: numel(stat)
plot(stat(x).Centroid(1),stat(x).Centroid(2),'bo');
end
Result:
Note: The line may look dotted due to the picture's large size, but its continuous
Last figure zoomed to see continuous line:
Going further:
You may have to take the advice of #Spektre to find the angle of inclination using atan2. Also refer his answer for more explanation.

identify the redness in an image & then compare with the other image using Matlab

I want to identify redness in the image and then compare that value with the redness in another image. I am quite new to Matlab and don't have image processing knowledge. However, I have been trying some random techniques to do this. Till now, I have used histograms of RGB channels of individual images and have also compared average numeric values of RGB channels in individual images. Unfortunately, I see almost similar results in both cases and cannot identify difference between less red and more red image.
I randomly tried working with grayscale histograms as well but found it to be useless.
(source: ucoz.ru)
(source: luc.edu)
P.S. I searched on this forum and tried to find a similar problem but i did not find anything that could help me.
What I need is:
a. Which technique could be used to check redness in images?
b. How Matlab can me help there?
%-------------------------------------------
%For histograms of all 3 RGB channels in an image
i = imread('<Path>\a7.png');
imgr = i(:,:,1);
imgg = i(:,:,2);
imgb = i(:,:,3);
histr = hist(imgr(:), bins);
histg = hist(imgg(:), bins);
histb = hist(imgb(:), bins);
hfinal = [histr(:); histg(:); histb(:)];
plot(bins, histr);
%-------------------------------------------
%To compare mean values of R channels of all images
clear all;
%read all images in a sequence
flist=dir('<Path>\*.png');
for p = 1:length(flist)
for q = 1 : 3
fread = strcat('<Path>\',flist(p).name);
im = imread(fread);
meanim(p,q) = mean2(im(:,:,q));
end
end
%disp(meanim);
rm = meanim(:,1);
frm = sum(rm(:));
gm = meanim(:,2);
fgm = sum(gm(:));
bm = meanim(:,3);
fbm = sum(bm(:));
figure();
set(0,'DefaultAxesColorOrder',[1 0 0;0 1 0;0 0 1]);
pall = [rm(:), gm(:), bm(:)];
plot(pall);
title('Mean values of R, G and B in 12 images');
leg1 = legend('Red','Green','Blue', ...
'Location','Best');
print (gcf, '-dbmp', 'rgbchannels.bmp')
sm = sum(meanim);
fsum = sum(sm(:));
% disp(fsum);
f2 = figure(2);
set(f2, 'Name','Average Values');
t = uitable('Parent', f2, 'Position', [20 20 520 380]);
set(t, 'ColumnName', {'Average R', 'Average G', 'Average B'});
set(t, 'Data', pall);
print (gcf, '-dbmp', 'rgbtable.bmp') ;
rgbratio = rm ./ fsum;
disp(rgbratio);
f3 = figure(3);
aind = 1:6;
hold on;
subplot(1,2,1);
plot(rgbratio(aind),'r+');
title('Plot of anemic images - having more pallor');
nind = 7:12;
subplot(1,2,2);
plot(rgbratio(nind),'b.');
title('Plot of non anemic images - having less pallor');
hold off;
print (gcf, '-dbmp', 'anemicpics.bmp');
You can't assume the red channel is the same as the redness of a pixel by itself. A good estimate of redness of a pixel may be achieved by something like this:
redness = max(0, red - (blue + green) / 2);
Where red, green and blue are values of different RGB channels in the image.
Once you calculated this value for an image, you can estimate the redness of the image by some approaches like averaging or histograms.

display cluster in an image

I have an image, test3:
I want to cluster points on the image.
My code:
[I,map]=imread('test3','bmp');
I = ~I;
imshow(I,map);
[m n]=size(I)
P = [];
for i=1:m
for j=1:n
if I(i,j)==1
P = [P ; i j];
end
end
end
size(P)
MON=P;
[IDX,ctrs] = kmeans(MON,3)
As I plot the clusters in the image, I also want to draw IDX and ctrs in the image.
How do I get an image with 3 new clusters, with each cluster a different color in the image.
To generate the k-means clusters:
% locations of the black pixels
[I,map]=imread('test3','bmp'); I = ~I;
[ii,jj] = find(I==1);
P = [ii jj];
k = 3;
[IDX,ctrs] = kmeans(P,k);
To plot the points with different colors for each cluster:
cm = jet(k);
hold on
for ik=1:k,
imap = IDX==ik;
plot(P(imap,2),P(imap,1),'.','color',cm(:,ik));
end
axis image
axis ij
Add markers for the centers:
plot(ctrs(:,2),ctrs(:,1),'r*','markersize',10)
Result:
Or to make a color-coded image out of the cluster indexes:
kimg = zeros(size(I));
kimg(sub2ind(size(I),P(:,1),P(:,2))) = IDX;
cmap = [ones(1,3); jet(k)]
kimgRGB = ind2rgb(kimg+1,cmap);
imwrite(kimgRGB,'kimg.png')
Result:
Note that I have chosen the jet colormap, but you can use any map you wish. Also, I have set the background to white (ones(1,3) is the color for index value 1).