I want to read an image into MATLAB, draw a rectangle on it, and then save the image.
Also, I'm just learning MATLAB--please be gentle. It seems like it should be simple, but I can't seem to do it.
im = imread('image.tif');
imshow(im);
rectangle('Position', [100, 100, 10, 10]);
imwrite(im, 'image2.tif');
Even though I can see the rectangle on the image, the saved image does not display the rectangle. How can I save the image and have the rectangle show up?
FWIW, I've already tried saveas(), but that gives me a HUGE image. Is there a way to use saveas() and make the saved image the correct size?
The reason the rectangle doesn't show up in the saved image is because you are not modifying the variable im, which stores the image data. The rectangle is simply a plot object displayed over the plotted image. You have to modify the image data itself.
Typically, images read into MATLAB are loaded as an N-by-M-by-3 matrix (i.e. an N-by-M pixel image with RGB (red-green-blue) values for each pixel). Usually, the image data is a uint8 data type, so the RGB values range from 0 to 255. If you wanted to change the RGB value for a given pixel, you would do the following:
im = imread('test.jpg'); % Load a jpeg image
im(1,1,1) = 255; % Change the red value for the first pixel
im(1,1,2) = 0; % Change the green value for the first pixel
im(1,1,3) = 0; % Change the blue value for the first pixel
imwrite(im,'new.jpeg'); % Save modified image
There are different ways you can modify more than one pixel at a time (i.e. a rectangular area), which will require that you look into how to index into multidimensional arrays. For more detail about how different types of images are read into MATLAB (i.e. truecolor vs. indexed), I would check the documentation for imread.
to the question in the top, there is quite a simple solution provided by matlab:
% you so far
im = imread('image.tif');
imshow(im);
rectangle('Position', [100, 100, 10, 10]);
% now you use "getframe" and "frame2im"
f = getframe(gca);
im = frame2im(f);
imwrite(im,'image2.tif');
that worked great for me when i also drew a rectangle on an image and tried to save it. If you want to keep on working with it, just add
imread('image2.tif');
and keep on working with it :)
Regards, Laura
There's actually a bug at The MathWorks site about this issue. Too bad they don't spell out a real answer (as, IMHO, holding up a ruler to your monitor is not a real solution).
Using the print command, you must manually change the -r parameter until the size of the saved image matches the size of the input image. The -r parameter specifies the DPI of the saved image. Since most screens have different DPIs, there's no one-size-fits-all solution.
im = imread('image.tif');
f = figure, imshow(im, 'Border', 'tight');
rectangle('Position', [100, 100, 10, 10]);
print(f, '-r80', '-dtiff', 'image2.tif');
Use the code above, tweak the -r parameter until it looks right, and voilà!
following up to jacobko answer. Setting the figures paperposition and paperunits properties and the axis units and position properties usually gives me the desired results without having to tweak the resolution. So,
>> im = imread('image.tif');
>> f = figure, imshow(im);
>> r=rectangle('Position',[100, 100,10,10]);
>> set(r,'edgecolor','b') % change the color of the rectangle to blue
>> set(f,'units','centimeters','position',[1 1 2.5 2.5]) % set the screen size and position
>> set(f,'paperunits','centimeters','paperposition',[1 1 2.5 2.5]) % set size and position for printing
>> set(gca,'units','normalized','position',[0 0 1 1]) % make sure axis fills entire figure
>> print(f, '-r80','-dtiff','image2.tif')
The output image, image2.tif, will now be 2.5cm by 2.5cm at a resoultion of 80dpi without the border around the axis.
If you want to save im, you must first modify its value.
I am not familiar with the rectangle function,
but you can do the following (brute force):
im = imread('image.tif');
im(100:110,100)=0;
im(100:110,110)=0;
im(100,100:110)=0;
im(110,100:110)=0;
imshow(im);
imwrite(im, 'image2.tif');
Note, the code above is for gray scale image, if your image is an RGB image, you will need to do the following:
im(100:110,100,:)=0;
....
You might be able to use getframe to grab the modified image from the figure window. I think you could pass the cdata and colormap fields of the structure returned by getframe to imwrite as the image and its colormap, respectively.
[f,p] = uigetfile('*.*');
I = imread([p,f]);
imwrite(I,'img12.tif');%
Any name we can give for saving the image
Automatically it will save in your folder and you can browse any image.
close all; clear; clc;
r = 240 ; c = 320;
fig = figure('Visible', 'off');
imshow( zeros(r,c) );
hold on;
plot([c-fix(c/2),c-fix(c/2)],[r-fix(r/2),r-fix(r/2)],'r*', 'MarkerSize', 10 );
% Sets position and size of figure on the screen
set(fig, 'Units', 'pixels', 'position', [100 100 c r] );
% Sets axes to fill the figure space
set(gca, 'Units', 'pixels', 'position', [0 0 c+1 r+1 ]);
% Sets print properties; Looks like 1 pixel = (3/4)th of a point
set(fig, 'paperunits', 'points', 'papersize', [fix((c-1)*(3/4))+1 fix((r-1)*(3/4))+1]);
set(fig, 'paperunits', 'normalized', 'paperposition', [0 0 1 1]);
print( fig, sprintf('-r%d', ceil(72*(4/3))), '-dpng', 'image.png' );
im = imread( 'image.png');
figure; imshow(im);
Related
Matlab provides an ability to set individual colormaps for each subplot. The same functionality described by Octave docs
Namely following phrases:
Function File: cmap = colormap (hax, …)
If the first argument hax is an axes handle, then the colormap for the parent figure of hax is queried or set.
But when I try to run following code, I end up having single colormap for all subplots for some reason.
clear -all;
clc;
img = zeros(128, 128);
img(64,64) = 0.2;
rad = radon(img);
x = 1;
y = 4;
s1 = subplot(y,x,1); imagesc(img);
colormap(s1, gray);
s2 = subplot(y,x,2); imagesc(rad);
colormap(s2, hot);
colorbar;
line_img = zeros(128, 128);
line_img(64, 32:64) = 0.5;
line_img(63, 32:64) = 0.4;
line_img(63, 32:64) = 0.2;
line_rad = radon(line_img);
s3 = subplot(y,x,3); imshow(line_img);
colormap(s3, gray);
s4 = subplot(y,x,4); imagesc(line_rad);
colormap(s4, hot);
colorbar;
Any help is appreciated. I expect to have source images in grayscale and radon transform images in "hot". For some reason I'm getting first subplot somewhat like grayscale (it's actually not, since I initialize point with value 0.2 and octave gives me pure white color for it, while I'm expecting reasonably dark gray), and thee remaining images seem to have "hot" colormap being set.
If you read that line from the documentation a little close you will see that is that that if you pass an axes handle that the parent figure's colormap is changed. This is not the same as each axes having a different colormap since they're all in the same figure.
Function File: cmap = colormap (hax, …) If the first argument hax is an axes handle, then the colormap for the parent figure of hax is queried or set.
Currently, Octave does not support this functionality which was only just recently introduced in MATLAB.
The way around this is to convert your images to RGB images prior to displaying with imshow and then the figure's colormap is irrelevant. You can do this by first converting it to an indexed image (using gray2ind) and then to RGB with ind2rgb.
% To display the grayscale image
rgb = ind2rgb(gray2ind(img), gray);
imshow(rgb);
As a side note, the reason that your first grayscale image is showing up as all white is that if the input to imshow is of type double, then all values are expected to be between 0 and 1. If you want to change this behavior you can use the second input of imshow to specify that you'd like to scale the color limits to match your data
imshow(img, [])
h = vision.GeometricShearer('values' , [0 20]);
The above MATLAB command defines an object for horizontal shearing of an image. Is there a way to define the same object but for up and down shearing?
BTW, you have a small typo in your syntax. values should be capitalized and so it's Values. This apparently is case-sensitive.... which is a bit ridiculous, but that's the way it is.
Back to your post, you need to specify an additional flag to vision.GeometricShearer that determines the direction of where you want to apply the shear. Specifically, you need to set the Direction flag, and you set this to either Horizontal or Vertical. If you omit this, the default is Horizontal. As such, if you want to shear the last column of your image and move it down by 20 pixels, you would do this:
h = vision.GeometricShearer('Values', [0 20], 'Direction', 'Vertical');
If you want to visualize the results, you would use step and apply it to the image. As an example, let's load in the checkerboard image that's part of the MATLAB system path, apply the shear, then show both of the results in the same figure:
%// Define vertical shear
h = vision.GeometricShearer('Values', [0 20], 'Direction', 'Vertical');
img = im2single(checkerboard); %// Read in image
outimg = step(h,img); %// Apply shear
%// Show both results
subplot(2,1,1), imshow(img);
title('Original image');
subplot(2,1,2), imshow(outimg);
title('Output image');
This is what I get:
Is there a way to get the content of a contourf plot as an image matrix? I want rasterize only the content, not the axes, labels and the empty space of the entire figure.
My goal is to overlay a transparent, colored contour plot over a grayscale image and I don't see another way, since MATLAB has only one colormap per figure.
Try getframe and frame2im
Example from the frame2im documentation:
Create and capture an image using getframe and frame2im:
peaks %Make figure
f = getframe; %Capture screen shot
[im,map] = frame2im(f); %Return associated image data
if isempty(map) %Truecolor system
rgb = im;
else %Indexed system
rgb = ind2rgb(im,map); %Convert image data
end
Not a direct answer to the question, but this is how I think you could achieve your goal:
%# load in grayscale image
gray_im = rgb2gray(imread('peppers.png'));
%# converting n x m grey image to n x m x 3 rgb gray image
rgb_gray_im = cat( 3, gray_im, gray_im, gray_im );
%# displaying this image
imshow( rgb_gray_im );
%# plotting contourf on top with arbitrary colourmap
hold on
h = axes('position', [0.5, 0.5, 0.2, 0.2]);
z = peaks;
contourf(h, z, [min(z(:)), -6 : 8]);
Which gives the result:
The figure's colourmap is being used for the contourf plot. The background image is not relying on a colourmap, and is instead being displayed in truecolour - i.e. each pixel is being displayed as an RGB value defined in rgb_gray_im.
There are also other ways of getting around the MATLAB colourmap restrictions: see for example this blog post or these answers.
I have noticed that MATLAB sometimes displays my colors incorrectly. I'm not sure if this is a programming error on my side, or if it is an actual bug in MATLAB. I noticed this behavior with some regularity over the last year or so.
This time, I decided to take a snapshot of a figure with the error in question (taken on MATLAB 2011b on Windows 7, 64 bit):
The code that displays the image in question is the following:
figure;
clf;
cla;
imshow(matrix, []);
colormap(cmap);
set(gca, 'Clim', [0 highest_index]);
where:
matrix is of type uint32 (although I have also tried explitly casting matrix as double prior to calling imshow)
The values in matrix range between 0 and 900
cmap has 901 entries
highest_index is 900
The RGB entry for the value 259 in matrix is [1, 0, 0.1] both in the image above and in the colormap array cmap, i.e. cmap(300, :) = [1, 0, 0.1] (notice that the matrix value 259 gets the index 300 in the colormap, since the first entry of the colormap is for the matrix value 0).
Questions:
Why does this happen? Is it an error? Is there anything I am doing wrong?
Update 1:
I tried switching CDataMapping to direct or scaled, but it didn't make a difference.
I also tried using imagesc instead of imshow, but it didn't make a difference.
If I convert the image to RGB first (i.e. transform the indexed image to a true color image; see here for more info on this), i.e. with i_rgb = ind2rgb(i_indexed, cmap), the error goes away and the image is displayed correctly.
Unfortunately, if I display a true color image the data tip does not reveal the index in the original matrix for each color anymore and instead it just displays the RGB vector (i.e. this is logical, since MATLAB is not aware of the original index anymore).
Update 2:
Here's some sample code:
h_f = figure(1);
clf;
i_spiral = spiral(40);
h_i = image(i_spiral);
% Synthesize a colormap first in HSV and then transform it to RGB:
max_i_spiral = max(i_spiral(:));
m = max_i_spiral;
h = (0:m-1)'/max(m,1);
cmap_spiral = hsv2rgb([h ones(m,2)]);
colormap(cmap_spiral);
% If I comment out the following two lines or use imshow instead of image,
% it makes no difference (I still get the same error):
set(gca, 'Clim', [1 max_i_spiral]);
set(h_i, 'CDataMapping', 'direct');
The code above results in:
[Since this answer is totally unrelated to my former answer, I'm not editing the first]
The link you mention (http://www.mathworks.com/help/matlab/creating_plots/image-types.html)
says:
Note When using the painters renderer on the Windows platform, you should only use 256 colors when attempting to display an indexed
image. Larger colormaps can lead to unexpected colors because the
painters algorithm uses the Windows 256 color palette, which graphics
drivers and graphics hardware are known to handle differently. To work
around this issue, use the Zbuffer or OpenGL renderer, as appropriate.
For more information regarding graphics renderers in MATLAB, see
Technical Note 1201: The Technical Support Guide to Graphics Rendering
and Troubleshooting.
So it seems the problem is that your colormap has more then 256 values. It also explains why the problem goes away if you don't use an indexed image. Try using a different renderer, as suggested in the technical support link from the note:
set(gcf, 'Renderer', 'opengl')
or
set(gcf, 'Renderer', 'Zbuffer')
A better way to use IMSHOW is:
imshow(img,map)
Here is your example slightly rewritten:
%# indexed image
I = spiral(40);
%# Synthesize a colormap first in HSV and then transform it to RGB
mx = max(I(:));
cmap = hsv2rgb([(0:mx-1)'./max(mx,1) ones(mx,2)]); %'
%# show image
imshow(I,cmap)
colorbar
datacursormode on
EDIT:
Thanks to #ItamarKatz, we now know that on Windows, if you are displaying an indexed image with more than 256 colors, one must not use the 'painters' algorithm as renderer.
IMSHOW (which underneath calls the lower level IMAGE function), detects such a case and correctly handles it.
If you still want to use IMAGE/IMAGESC, you must be aware of the indexed image data type:
double:
Image contains integers in the range [1 length(cmap)] as indices in the current colormap
uint8/uint16:
Image contains integers in the range [0 255] for uint8 or [0 65535] for uint16, interpreted as indices in the current colormap.
thus there is an offset (range starts at 0 or 1) which you should be careful about.
Here is the same example as above using IMAGE function directly (once with double data type, the other with uint16):
%# indexed image and colormap
I = spiral(40);
cmap = hsv( max(I(:)) );
%# show indexed image (double)
hFig = figure(2);
hImg = image(I); %# one-based index into colormap
colormap(cmap), colorbar
axis off image
%# fix bug on Windows with indexed image of more than 256 colors
if ispc && strcmpi(get(hImg,'CDataMapping'),'direct') && size(cmap,1) > 256
set(hFig, 'Renderer','zbuffer') %# opengl renderer also works
end
%# show indexed image (uint16)
hFig = figure(3);
hImg = image( uint16(I-1) ); %# zero-based index into colormap
colormap(cmap), colorbar
axis off image
%# fix bug on Windows with indexed image of more than 256 colors
if ispc && strcmpi(get(hImg,'CDataMapping'),'direct') && size(cmap,1) > 256
set(hFig, 'Renderer','zbuffer')
end
I am not 100% sure (couldn't verify without your data), but I think the reason is wrong mapping/rounding done by the data-tip display function callback. You can create your own callback, by right-clicking the data tip, selecting Edit Text Update Function..., and entering something like that:
function output_txt = dataCursorCallback(obj,event_obj)
% Display the position of the data cursor, and the RGB data to 6 decimal places.
pos = get(event_obj,'Position');
output_txt = {['X: ',num2str(pos(1),4)],...
['Y: ',num2str(pos(2),4)]};
h = get(event_obj,'target');
cdata = get (h, 'CData');
cmap = colormap;
rgb = cmap(cdata(pos(2),pos(1)),:);
output_txt{end+1} = ['RGB: ' num2str(rgb,'%.6f')];
Note that the above code assumes the colormap length and and data range of the matrix plotted are the same - like in your example.
To save the callback, click save and close, and you can re-select it on next occasions by right-clicking the data tip and selecting Select Text Update Function...
All I want to do is make the width greater and the height smaller. I'm just doing raster plots but this question applies to any MATLAB figure. I can manually resize it using the figure directly when it's created but I want the program to spit it out in the right size to start with.
The properties that can be set for a figure is referenced here.
You could then use:
figure_number = 1;
x = 0; % Screen position
y = 0; % Screen position
width = 600; % Width of figure
height = 400; % Height of figure (by default in pixels)
figure(figure_number, 'Position', [x y width height]);
Write it as a one-liner:
figure('position', [0, 0, 200, 500]) % create new figure with specified size
figure (1)
hFig = figure(1);
set(gcf,'PaperPositionMode','auto')
set(hFig, 'Position', [0 0 xwidth ywidth])
plot(x,y)
print -depsc2 correlation.eps; % for saving in eps, look up options for saving as png or other formats you may need
This saves the figure in the dimensions specified
I managed to get a good result with the following sequence (run Matlab twice at the beginning):
h = gcf; % Current figure handle
set(h,'Resize','off');
set(h,'PaperPositionMode','manual');
set(h,'PaperPosition',[0 0 9 6]);
set(h,'PaperUnits','centimeters');
set(h,'PaperSize',[9 6]); % IEEE columnwidth = 9cm
set(h,'Position',[0 0 9 6]);
% xpos, ypos must be set
txlabel = text(xpos,ypos,'$$[\mathrm{min}]$$','Interpreter','latex','FontSize',9);
% Dump colored encapsulated PostScript
print('-depsc2','-loose', 'signals');
A different approach.
On the figure() call specify properties or modify the figure handle properties after h = figure().
This creates a full screen figure based on normalized units.
figure('units','normalized','outerposition',[0 0 1 1])
The units property can be adjusted to inches, centimeters, pixels, etc.
See figure documentation.