Saving a MATLAB matrix as an image to a specific size and resolution - image

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.

Related

How to used linspace matlab function to get the and the y of all pixels in image?

I have an image of 200x200 * 3, and I should to find the x and the y of each pixel of the image by using the linspace Matlab function.
I have already used the mesh grid function with the following code:
Ny=200; % # pixels in y direction
Nx=200; % # pixels in x direction
resolution=1; % size of a pixel
img=rand(Ny,Nx,3); % random image
y=(img(1:Ny))*resolution-resolution/2;
x=(img(1:Nx))*resolution-resolution/2;
[x,y]=meshgrid(x,y);
But my supervisor told me to use the function linspace and I cannot understand how.Can someone help me please
The only reason I can think of that your supervisor wants you to use linspace is for creating the x and y vector inputs to meshgrid. linspace alone is not sufficient to generate all of the pixel coordinates, so you'll have to use meshgrid, ndgrid, repelem or repmat.
The one advantage that linspace has over simply doing something like 1:resolution:Ny is that linspace ensures that the endpoints always appear in the range. For example, if you do
>> 1:.37:4
ans =
1.0000 1.3700 1.7400 2.1100 2.4800 2.8500 3.2200 3.5900 3.9600
you don't get 4 as the last point. However, if you use linspace:
>> linspace(1,4,9)
ans =
1.0000 1.3750 1.7500 2.1250 2.5000 2.8750 3.2500 3.6250 4.0000
the spacing is automatically adjusted to make the last element 4.
So assuming that resolution is the multiplier for the number of points you want, for 2 you want 401 evenly spaced points and for 1/2 you want 101 points, your code would look something like this:
x_points = linspace(1, Nx, Nx*resolution+1);
y_points = linspace(1, Ny, Ny*resolution+1);
(I'm still not sure what the resolution/2 part is supposed to do, so we'll need to clarify that.)
Then you would pass those to meshgrid:
[X,Y] = meshgrid(x_points, y_points);

How can I crop several and random batches from an image?

I have an image of size (224 x 224) and I want to extract a number of random patches from the original image using Matlab (let say 5 patches). One of these patch should be at the centre of the original image. The patch size is (128 x 128).
I have tried this to crop just the centre patch:
II = imread('img.png')
[p3, p4] = size(II);
q1 = 50; // size of the crop box
i3_start = floor((p3-q1)/2); % or round instead of floor; using neither gives warning
i3_stop = i3_start + q1;
i4_start = floor((p4-q1)/2);
i4_stop = i4_start + q1;
II = II(i3_start:i3_stop, i4_start:i4_stop, :);
figure ,imshow(II);
I've tried to accomplish this in the following way:
A=imread('Lena.bmp');%sample image
rnd_x = randperm(size(A,1)-128,5);%choose 5 tandom unique points on x-axis
rnd_y = randperm(size(A,2)-128,5);%choose 5 tandom unique points on y-axis
for ii = 1:5
piece{ii} = A((rnd_x(ii):(rnd_x(ii)+127)),(rnd_y(ii):(rnd_y(ii)+127)),1:3);%Convert chosen numbers to image pieces
figure(ii)
imshow(piece{ii});
end
This takes image like this:
This gives 5 pics like this:
Here our image size is 512x512. So, if we want to cut the 128x128 piece from it, we need to seek from 385x385 grid (512-127). We find 5 random points on the grid expressed in rnd_x and rnd_y. Finally, we take the found points as the upper-left corners of the pieces and construct 128x128 images from them. The 5 pieces are recorded in piece cell array.
EDIT: forgot to add how to extract the center patch. The following code performs the task:
A=imread('Lena.bmp');%sample image
if mod(size(A,1),2)
A = A(1:(end-1),:,:);
end
if mod(size(A,2),2)
A = A(:,1:(end-1),:);
end
while size(A,1) > 128
A = A(2:(end-1),:,:);
end
while size(A,2) > 128
A = A(:,2:(end-1),:);
end
imshow(A)
The code removes one pixel from each side until we get the 128-pixel image.
Careful! In your code, if you load a color image (3 channels) and call size with only two outputs, you will have an incorrect value for p4.
Use three outputs when loading images to avoid this problem:
[nrows ncols nchannels] = size(II);
Your code correctly extracts a (q1 x q1) from the center of the image.
If you want a random patch just generate a random integer for the top-left column of the patch with the correct range to ensure that it doesn't fall outside the image. You can generate random integers using the function randi.
i3_start = randi(floor((p3-q1));
i4_start = randi(floor((p4-q1));
The rest of the code is the same. If you want several patches you can generate several values when calling the randi function with a second and third parameter for the desired number of rows and columns. And then process each patch inside a for loop.
BTW: In the third line you have an invalid Matlab comment (use % for comments). Also you should name your variables with more intuitive names.
Eg: [nrows ncols nchannels] = size(II);

Compute contour of a binary image using matlab

I am trying to compute contour of a binary image. Currently i identify the first non zero and the last non zero pixel in the image through looping. Is there a better way? i have encountered few functions:
imcontour(I)
bwtraceboundary(bw,P,fstep,conn,n,dir)
But the first doesn't return the x and y coordinates of the contour. The second function requires a seed point which i cannot provide. An example of the image is shown below. Thanks.
I'm surprised you didn't see bwperim. Did you not try bwperim? This finds the perimeter pixels of all closed objects that are white in a binary image. Using your image directly from StackOverflow:
im = im2bw(imread('http://i.stack.imgur.com/yAZ5L.png'));
out = bwperim(im);
imshow(out);
We get:
#rayryeng have already provided the correct answer. As another approach (might be that bwperim performs this operations internally) boundaries of a binary image can be obtained by calculating the difference between the dilated and the eroded image.
For a given image:
im = im2bw(imread('http://i.stack.imgur.com/yAZ5L.png'));
and a given binary structural element:
selem = ones(3,3); %// square, 8-Negihbours
% selem = [0 1 0; 1 0 1; 0 1 0]; %// cross, 4-Neighbours
The contour of the object can be extracted as:
out = imerode(im, selem) ~= imdilate(im, selem);
Here, however, the boundary is thicker than using bwperim, as the pixels are masked in both inside and outside of the object.
I had the same problem, stumbled across this question and just wanted to add that imcontour(Img); does return a matrix. The first row contains the x-values, the second row contains the y-values.
contour = imcontour(Img); x = contour(1,:); y = contour(2,:);
But I would discard the first column.

Resize an image with bilinear interpolation without imresize

I've found some methods to enlarge an image but there is no solution to shrink an image. I'm currently using the nearest neighbor method. How could I do this with bilinear interpolation without using the imresize function in MATLAB?
In your comments, you mentioned you wanted to resize an image using bilinear interpolation. Bear in mind that the bilinear interpolation algorithm is size independent. You can very well use the same algorithm for enlarging an image as well as shrinking an image. The right scale factors to sample the pixel locations are dependent on the output dimensions you specify. This doesn't change the core algorithm by the way.
Before I start with any code, I'm going to refer you to Richard Alan Peters' II digital image processing slides on interpolation, specifically slide #59. It has a great illustration as well as pseudocode on how to do bilinear interpolation that is MATLAB friendly. To be self-contained, I'm going to include his slide here so we can follow along and code it:
Please be advised that this only resamples the image. If you actually want to match MATLAB's output, you need to disable anti-aliasing.
MATLAB by default will perform anti-aliasing on the images to ensure the output looks visually pleasing. If you'd like to compare apples with apples, make sure you disable anti-aliasing when comparing between this implementation and MATLAB's imresize function.
Let's write a function that will do this for us. This function will take in an image (that is read in through imread) which can be either colour or grayscale, as well as an array of two elements - The image you want to resize and the output dimensions in a two-element array of the final resized image you want. The first element of this array will be the rows and the second element of this array will be the columns. We will simply go through this algorithm and calculate the output pixel colours / grayscale values using this pseudocode:
function [out] = bilinearInterpolation(im, out_dims)
%// Get some necessary variables first
in_rows = size(im,1);
in_cols = size(im,2);
out_rows = out_dims(1);
out_cols = out_dims(2);
%// Let S_R = R / R'
S_R = in_rows / out_rows;
%// Let S_C = C / C'
S_C = in_cols / out_cols;
%// Define grid of co-ordinates in our image
%// Generate (x,y) pairs for each point in our image
[cf, rf] = meshgrid(1 : out_cols, 1 : out_rows);
%// Let r_f = r'*S_R for r = 1,...,R'
%// Let c_f = c'*S_C for c = 1,...,C'
rf = rf * S_R;
cf = cf * S_C;
%// Let r = floor(rf) and c = floor(cf)
r = floor(rf);
c = floor(cf);
%// Any values out of range, cap
r(r < 1) = 1;
c(c < 1) = 1;
r(r > in_rows - 1) = in_rows - 1;
c(c > in_cols - 1) = in_cols - 1;
%// Let delta_R = rf - r and delta_C = cf - c
delta_R = rf - r;
delta_C = cf - c;
%// Final line of algorithm
%// Get column major indices for each point we wish
%// to access
in1_ind = sub2ind([in_rows, in_cols], r, c);
in2_ind = sub2ind([in_rows, in_cols], r+1,c);
in3_ind = sub2ind([in_rows, in_cols], r, c+1);
in4_ind = sub2ind([in_rows, in_cols], r+1, c+1);
%// Now interpolate
%// Go through each channel for the case of colour
%// Create output image that is the same class as input
out = zeros(out_rows, out_cols, size(im, 3));
out = cast(out, class(im));
for idx = 1 : size(im, 3)
chan = double(im(:,:,idx)); %// Get i'th channel
%// Interpolate the channel
tmp = chan(in1_ind).*(1 - delta_R).*(1 - delta_C) + ...
chan(in2_ind).*(delta_R).*(1 - delta_C) + ...
chan(in3_ind).*(1 - delta_R).*(delta_C) + ...
chan(in4_ind).*(delta_R).*(delta_C);
out(:,:,idx) = cast(tmp, class(im));
end
Take the above code, copy and paste it into a file called bilinearInterpolation.m and save it. Make sure you change your working directory where you've saved this file.
Except for sub2ind and perhaps meshgrid, everything seems to be in accordance with the algorithm. meshgrid is very easy to explain. All you're doing is specifying a 2D grid of (x,y) co-ordinates, where each location in your image has a pair of (x,y) or column and row co-ordinates. Creating a grid through meshgrid avoids any for loops as we will have generated all of the right pixel locations from the algorithm that we need before we continue.
How sub2ind works is that it takes in a row and column location in a 2D matrix (well... it can really be any amount of dimensions you want), and it outputs a single linear index. If you're not aware of how MATLAB indexes into matrices, there are two ways you can access an element in a matrix. You can use the row and column to get what you want, or you can use a column-major index. Take a look at this matrix example I have below:
A =
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
If we want to access the number 9, we can do A(2,4) which is what most people tend to default to. There is another way to access the number 9 using a single number, which is A(11)... now how is that the case? MATLAB lays out the memory of its matrices in column-major format. This means that if you were to take this matrix and stack all of its columns together in a single array, it would look like this:
A =
1
6
11
2
7
12
3
8
13
4
9
14
5
10
15
Now, if you want to access element number 9, you would need to access the 11th element of this array. Going back to the interpolation bit, sub2ind is crucial if you want to vectorize accessing the elements in your image to do the interpolation without doing any for loops. As such, if you look at the last line of the pseudocode, we want to access elements at r, c, r+1 and c+1. Note that all of these are 2D arrays, where each element in each of the matching locations in all of these arrays tell us the four pixels we need to sample from in order to produce the final output pixel. The output of sub2ind will also be 2D arrays of the same size as the output image. The key here is that each element of the 2D arrays of r, c, r+1, and c+1 will give us the column-major indices into the image that we want to access, and by throwing this as input into the image for indexing, we will exactly get the pixel locations that we want.
There are some important subtleties I'd like to add when implementing the algorithm:
You need to make sure that any indices to access the image when interpolating outside of the image are either set to 1 or the number of rows or columns to ensure you don't go out of bounds. Actually, if you extend to the right or below the image, you need to set this to one below the maximum as the interpolation requires that you are accessing pixels to one over to the right or below. This will make sure that you're still within bounds.
You also need to make sure that the output image is cast to the same class as the input image.
I ran through a for loop to interpolate each channel on its own. You could do this intelligently using bsxfun, but I decided to use a for loop for simplicity, and so that you are able to follow along with the algorithm.
As an example to show this works, let's use the onion.png image that is part of MATLAB's system path. The original dimensions of this image are 135 x 198. Let's interpolate this image by making it larger, going to 270 x 396 which is twice the size of the original image:
im = imread('onion.png');
out = bilinearInterpolation(im, [270 396]);
figure;
imshow(im);
figure;
imshow(out);
The above code will interpolate the image by increasing each dimension by twice as much, then show a figure with the original image and another figure with the scaled up image. This is what I get for both:
Similarly, let's shrink the image down by half as much:
im = imread('onion.png');
out = bilinearInterpolation(im, [68 99]);
figure;
imshow(im);
figure;
imshow(out);
Note that half of 135 is 67.5 for the rows, but I rounded up to 68. This is what I get:
One thing I've noticed in practice is that upsampling with bilinear has decent performance in comparison to other schemes like bicubic... or even Lanczos. However, when you're shrinking an image, because you're removing detail, nearest neighbour is very much sufficient. I find bilinear or bicubic to be overkill. I'm not sure about what your application is, but play around with the different interpolation algorithms and see what you like out of the results. Bicubic is another story, and I'll leave that to you as an exercise. Those slides I referred you to does have material on bicubic interpolation if you're interested.
Good luck!

K means clustering in MATLAB - output image

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]));

Resources