To perform K means clustering with k = 3 (segments). So I:
1) Converted the RGB img into grayscale
2) Casted the original image into a n X 1, column matrix
3) idx = kmeans(column_matrix)
4) output = idx, casted back into the same dimensions as the original image.
My questions are :
A
When I do imshow(output), I get a plain white image. However when I do imshow(output[0 5]), it shows the output image. I understand that 0 and 5 specify the display range. But why do I have to do this?
B)
Now the output image is meant to be split into 3 segments right. How do I threshold it such that I assign a
0 for the clusters of region 1
1 for clusters of region 2
2 for clusters of region 3
As the whole point of me doing this clustering is so that I can segment the image into 3 regions.
Many thanks.
Kind Regards.
A: Given that your matrix output contains scalar values ranging from 1 to 3, imshow(output) is treating this as a grayscale matrix and assuming that the full range of values is 0 to 255. This is why constraining the color limits is necessary as otherwise your image is all white or almost all white.
B: output = output - 1
As pointed out by Ryan, your problem is probably just how you display the image. Here's a working example:
snow = rand(256, 256);
figure;
imagesc(snow);
nClusters = 3;
clusterIndices = kmeans(snow(:), nClusters);
figure;
imagesc(reshape(clusterIndices, [256, 256]));
Related
This question already has an answer here:
Efficient inpaint with neighbouring pixels
(1 answer)
Closed 6 years ago.
I am trying to replace all pixels with certain value in an image with the average values of the neighbors. Can interp2 be useful here? I tried this -
I = imread('test_image.JPG');
[r c] = size(I);
class_of_image = class(I);
[xi,yi] = meshgrid(1:0.5:c,1:0.5:r);
I1 = cast(interp2(double(image),xi,yi,'linear'),class_of_image);
[x_z,y_z] = find(I1==0);
I1(x_z,y_z) = I1(x_z-1,y_z)+I1(x_z+1,y_z)+I1(x_z,y_z-1)+I1(x_z,y_z+1);
This fails spectacularly with an error message - Index exceeds matrix dimensions.
I realize that the error is in trying to access I1 indices beyond r and c. Is there a generic way to incorporate this in the code?
Please help!
If you are trying to replace pixels in an image that are at a certain value to be the average of its 4 neighbours, then you don't have to use interp2. It looks like you are doubling the size of the image and then sampling from that image when you're done.
If you want to do what you're asking, you need to use column-major indices to facilitate the vectorized access of pixels. Specifically, you need to use sub2ind to help determine the locations you need to access in your matrix.
However, you will need to account for pixels that go out of bounds. There are many ways to accommodate this, but what I will implement is known as zero-padding where the border pixels are simply set to 0. I would create a zero-padded image where the top and bottom rows as well as the left and right values are all some sentinel value (like -1), use find on this image to find the coordinates then do the inpainting. Make sure you set the border pixels back to 0 before doing this so that you don't use -1 as part of the inpainting. You would then crop the border pixels of this new image when you're done to obtain the final output image.
Therefore, if you want to perform your "inpainting" try this instead:
% Read in image
I = imread('test_image.JPG');
% Create padded image with border pixels set to -1
Ipad = -ones(size(I) + 2);
% Place image in the middle
Ipad(2:end-1,2:end-1) = I;
% Find zero pixels
[r,c] = find(I == 0);
% Now set border pixels to 0
Ipad(Ipad == -1) = 0;
% Find column major indices for those elements that are 0
% as well as their 4 neighbours
ind = sub2ind(size(I), r, c);
ind_up = sub2ind(size(I), r-1, c);
ind_down = sub2ind(size(I), r+1, c);
ind_left = sub2ind(size(I), r, c-1);
ind_right = sub2ind(size(I), r, c+1);
% Perform the inpainting by averaging
Ipad(ind) = (Ipad(ind_up) + Ipad(ind_down) + Ipad(ind_left) + Ipad(ind_right))/4;
% Store the output in I1 after removing border pixels
I1 = Ipad(2:end-1,2:end-1);
However, a possibly shorter way to do this even though you would operate on the entire image would be to perform 2D convolution using a 3 x 3 kernel whose elements are 1 in the cardinal directions and ensuring that you divide by 4 to find the average value per location. After, you would simply copy over those values in the output that are 0 in the original image. You can use conv2 to do that and make sure you specify the 'same' flag to ensure that the output size is the same as the input size. The behaviour of conv2 when you approach the border elements is to zero-pad, which is what I did already in the first implementation:
% Read in image
I = imread('test_image.JPG');
% Specify kernel
kernel = [0 1 0; 1 0 1; 0 1 0] / 4;
% Perform convolution - make sure you cast image to double
% as convolution in 2D only works for floating-point types
out = conv2(double(I), kernel, 'same');
% Copy over those values from the output that match the value
% to be inpainted for the input. Also cast back to original
% data type.
I1 = I;
I1(I == 0) = cast(out(I == 0), class(I));
After having read this question, I wanted to use this code to save my picture as to particular size.
I_c(:,:) = cropped_matrix(i,:,:);
set(I_c, 'PaperUnits', 'inches');
x_width = 7.25;
y_width = 9.125;
set(I_c, 'PaperPosition', [0 0 x_width y_width]);
imshow(I_c);
saveas(I_c,'fig1.pdf');
I_c represents a 2D matrix (about 40x40) of uint8's.
However, I get the error:
Error using set Invalid handle
This makes me believe that I can only use this code with figures and not matrices which contain matrices. How would I go about this?
I have looked at the API for print, as suggested as the first answer of the aforementioned linked question, but it also suggests using set and 'PaperUnits'.
Note: This question also looks at this problem but suggests the same solution.
Notes About Crowley's Answer
So I just tried the code that you have given with your answer. The .jpg being produced is the one shown below. How would I get rid of all the extra white area through code?
How would I change the colourmap of the figure being produced, to greyscale when I just use image(ImData)?
And here is how the actual figure appears:
Here is the code that I have put in:
im = image(I_c);
set(gcf,'units','inches','position',[1 2 5 5]);
set(gca,'ydir','normal','units','centimeters','position',[0 0 0.5 0.5].*get(gcf,'position')) ;
filename = strcat('slice',int2str(i),'_','bead',int2str(j),'.jpg');
saveas(im,filename);
Suppose we have matrix I_c containing values and x and y the coordinates so value I_c(ii,jj) corresponds to x(ii) and y(jj) coordinates.
Then:
ImMin=min(min(I_c)); % Find the minimum value in I_c
ImData=I_c-ImMin; % Ensure non-zero values
ImMax=max(max(ImData)); % Find maximum value in ImData
ImData=ImData./ImMax; % Ensure ImData do NOT exceed 1
image(x,y,ImData*[1 1 1]) % plot the image in greyscale
set(gcf,'units','inches','position',[1 2 5 5]) % set active figure properties
set(gca,'ydir','normal','units','inches','position',[0 0 1 1].*get(gcf,'position')) % set active axes properties
export_fig(gcf,'figure-I_c','-png','-nocrop')
'ydir','normal' parameter change default (1,1) point being in top-left corner to "normal" position in bottom-left corner.
[0 0 1 1].*get(gcf,'position) will read active figure position (here [1 2 5 5]) and after element-by-element multiplication the [0 0 5 5] is passed to the position which causes that axes fit the image.
export_fig function will create figure-I_c.png image as it is shown in Matlab figure, if -nocrop is omitted the possible white space in the edges is cropped out. This function is available from MathWorks' File Exchange.
does anybody know how to crop an image with a sliding window in Matlab?
e.g. I have an image of 1000x500 pixels, I would like to crop from this image blocks of 50x50 pixels... Of course I have to handle uneven divisions, but it is not necessary to have block of the same size.
Some details that have helped me in the past related to (i) ways to divide an image while block processing and (ii) "uneven division", as mentioned by OP.
(i) Ways to divide/process an image:
1. Process non-overlapping blocks:
Using default parameter {'BorderSize',[0 0]}, this can be handled with blockproc as below.
Example for (i)-1: Note the blocked nature of the output. Here each non-overlapping block of size 32 x 32 is used to calculate the std2() and the output std2 value is used to fill that particular block. The input and output are of size 32 x 32.
fun = #(block_struct) std2(block_struct.data) * ones(size(block_struct.data));
I2 = blockproc('moon.tif',[32 32],fun);
figure; subplot(1, 2, 1);
imshow('moon.tif'); title('input');
subplot(1,2, 2)
imshow(I2,[]); title('output');
Input and Output Image:
(i)-2: Process overlapping blocks:
Using parameter {'BorderSize',[V H]}: V rows are added above and below the block and H columns are added to the left and right of the block. The block that is processed has (N + 2*V) rows and (M + 2*H) columns. Using default parameter {'TrimBorder',true}, the border of the output is trimmed to the original input block size of N rows and M columns.
Example for (i)-2: Below code using blockproc uses {'BorderSize',[15 15]} with [N M] = [1 1]. This is similar to filtering each pixel of the image with a custom kernel. So the input to the processing unit is a block of size (1 + 2*15) rows and (1 + 2*15) columns. And since {'TrimBorder',true} by default, the std2 of the 31 rows by 31 columns block is provided as output for each pixel. The output is of size 1 by 1 after trimming border. Consequently, note that this example output is 'non-blocked' in contrast to the previous example. This code takes much longer time to process all the pixels.
fun = #(block_struct) std2(block_struct.data) * ones(size(block_struct.data));
I2 = blockproc('moon.tif',[1 1],fun,'BorderSize',[15 15]);
figure; subplot(1, 2, 1);
imshow('moon.tif'); title('input');
subplot(1,2, 2)
imshow(I2,[]); title('output');
Input and Output Image:
(ii) "Uneven division":
1. Zero/replicate/symmetric padding:
Zero padding so that an integer multiple of the blocks (N rows by M cols sized) can cover the [image + bounding zeros] in the uneven dimension. This can be achieved by using the default parameter {'PadMethod', 0} along with {'PadPartialBlocks' , true} ( which is false by default ). If a bounding region of zeros causes a high discontinuity in values computed from the bounding blocks, {'PadMethod', 'replicate'} or {'PadMethod', 'symmetric'} can be used.
2. Assume an "Active Region" within the image for block processing
For the case of processing each pixel, as in case (i)-2, we could assuming a bounding region of floor(block_size/2) pixels on all sides along the periphery of the image that is used as "Dummy" region. The Active region for block processing is contained within the Dummy region.
Something similar is used in imaging sensors where Dummy Pixels located at the periphery of an imaging array of Active Pixels allow for an operation like the color interpolation of all active area pixels. As color interpolation usually needs a 5x5 pixel mask to interpolate the color values of a pixel a bounding Dummy periphery of 2 pixels can be used.
Assuming MATLAB indexing, the region ( floor(block_size/2) + 1 ) to ( Input_Image_Rows - floor(block_size)/2) ) Rows by ( floor(block_size/2) + 1 ) to ( Input_ImageCols - floor(block_size)/2) ) Columns is considered as Active region (assuming a square block of side, block_size) which undergoes block processing for each pixel as in (i)-2.
Assuming a square block size of 5 by 5, this is shown by the following:
block_size = 5;
buffer_size = floor(block_size/2);
for i = (buffer_size+1):(image_rows-buffer_size)
for j = (buffer_size+1):(image_cols-buffer_size)
... % block processing for each pixel Image(i,j)
end
end
Matlab ver: R2013a
I would first look into the function blockproc to see if it can do what you want.
If you're sure you want to manually crop the image into blocks, you can use this script. It both writes the cropped images to .png files and saves the cropped images in the pages of a 3D array. You can modify it as you need.
This script assumes the image in evenly divisible by the block size. If it isn't, you'll need to pad it with zeros.
[rowstmp,colstmp]= size(myImage);
block_height = 50;
block_width = 50;
blocks_per_row = rows/block_height;
blocks_per_col = cols/block_width;
number_of_blocks = blocks_per_row*blocks_per_col;
%// pad image with zeros if needed
if ~(mod(rowstmp-1,block_height)==0)
rows = ceil(rowstmp/block_height)*block_height;
end
if ~(mod(colstmp-1,block_width)==0)
cols = ceil(colstmp/block_width)*block_width;
end
Im = uint8(zeros(rows,cols));
Im(1:rowstmp,1:colstmp) = myImage;
%// make sure these image have type uint8 so they save properly
cropped_image = uint8(zeros(rows,cols));
img_stack = uint8(zeros(rows,cols,number_of_blocks));
%// loop over the image blocks
for i = 1:blocks_per_row
for j = 1:blocks_per_col
%// get the cropped image from the original image
idxI = 1+(i-1)*block_height:i*block_height;
idxJ = 1+(j-1)*block_width :j*block_width;
cropped_image(idxI,idxJ) = Im(idxI,idxJ);
%//imshow(cropped_image)
%// write the cropped image to the current folder
filename = sprintf('block_row%d_col%d.png',i,j);
imwrite(cropped_image,filename);
cropped_image(idxI,idxJ) = 0;
%// keep all the blocks in a 3D array if we want to use them later
img_stack(:,:,(i-1)*blocks_per_col+j);
end
end
I am using the connected component labeling algorithm in Matlab. Is it possible to use different color for different labels when showing the output? (Even though the labels have the same intensity).
Clarification:
I used connected component labeling algorithm to label the connected components of a binary images. Now I got different labels. All the labels contains pixel of equal intensity. (All the labels have pixel intensity value of 1) and all the labels appear in the same color. I want to display the different labels using different colors so that I can eliminate the unwanted one easier.
That is easy - use the imagesc function:
p = imread('peppers.png'); %Read image
b = (p(:,:,2)>100); % Thresholding by some constant threshold
If you already have a binary image, just use this section of the code: (b is the image)
L = bwlabel(b); %Find components
figure(); %Create figure
imagesc(L); %Draw the components, each in its own color.
You can also change the colors by using the colormap command:
colormap(bone)
In order to customize the colors, define an nx3 matrix, and give it as input to colormap
cm = [1 0 0;
0 1 0;
0 0 1
0 1 1
1 1 0
];
colormap(cm)
I have a RGB image and want to apply following formulas to it so I get another image. How can I do that? I know how to read/write image and I know how to loop and apply formulas but I don't know functions to extract number of rows and columns of image in a variable and image pixles values of 3 planes in 3-dimensional plane.
I = imread('myimage.jpg');
RGBImagePixles = [?, ?, ?] %of I
ROWS = ? %of I
COLUMNS = ? %of I
for r = 0 : ROWS
for c = 0 : COLUMNS
N[r, c] = RGBImagePixles[r,c,1] + RGBImagePixles[r,c,2] + RGBImagePixles[r,c,3]
end
end
figure, imshow(N);
The output of imread is a 3 dimensional array, actually 3 matrices stacked along the 3rd dimension - so if your image is m pixels high and n pixels wide, you'd get a m x n x 3 array.
so:
RGBImagePixles = I;
ROWS = size (I,1);
COLUMNS = size(I,2);
and you can replace the loop with:
N = sum(I, 3);
However, I am not sure that simple summation is what you need in order to produce a grayscale image.
[ROWS COLUMNS DIMS] = size(I);
I assume that RGB to grayscale is just an example, and your real goal is to learn how to manipulate the pixels of an image. Otherwise, you should just use:
ImageRGB = imread('yourfile.jpg')
ImageGray = rgb2gray(ImageRGB)
To do it by hand:
ImageRGB = imread('yourfile.jpg')
ImageGray = sum(ImageRGB,3) / (3*255)
You have to divide by 3*255 because matlab expects a grayscale image's values to be between 0 and 1, while the RGB values will between 0 and 255 in each channel.