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.
Related
Gnoivce and Hartmut helped a lot with this code but it takes a while to run.
The CData property in the bar command doesn't seem to be implemented in the Octave 4.0-4.2.1 version which I'm using. The work around for this was to plot all the single bars individually and set an individual color for each individual bar. People helped me out and got me this far but it takes 5 minutes for the plot to show does anyone know a way of speeding this up?
The following code runs:
marbles.jpg image file used below:
clear all,clf reset,tic,clc
rgbImage = imread('/tmp/marbles.jpg');
hsvImage = rgb2hsv(rgbImage); % Convert the image to HSV space
hPlane = 360.*hsvImage(:, :, 1); % Get the hue plane scaled from 0 to 360
binEdges = 0:360; %# Edges of histogram bins
N = histc(hPlane(:),binEdges); %# Bin the pixel hues from above
C = colormap(hsv(360)); %# create an HSV color map with 360 points
stepsize = 1; % stepsize 1 runs for a while...
for n=binEdges(2:stepsize:end) %# Plot the histogram, one bar each
if (n==1), hold on, end
h=bar(n,N(n));
set(h,'FaceColor',C(n,:)); %# set the bar color individually
end
axis([0 360 0 max(N)]); %# Change the axes limits
set(gca,'Color','k'); %# Change the axes background color
set(gcf,'Pos',[50 400 560 200]); %# Change the figure size
xlabel('HSV hue (in degrees)'); %# Add an x label
ylabel('Bin counts'); %# Add a y label
fprintf('\nfinally Done-elapsed time -%4.4fsec- or -%4.4fmins- or -%4.4fhours-\n',toc,toc/60,toc/3600);
Plot created after 5 mins:
To see original question original question
I'm guessing the loop is the bottleneck in your code that is taking so long? You could remove the loop and create the plot with one call to bar, then call set to modify the hggroup object and its child patch object:
h = bar(binEdges(1:end-1), N(1:end-1), 'histc'); % hggroup object
set(h, 'FaceColor', 'flat', 'EdgeColor', 'none');
hPatch = get(h, 'Children'); % patch object
set(hPatch, 'CData', 1:360, 'CDataMapping', 'direct');
Repeating your code with this fix renders right away for me in Octave 4.0.3:
As I suggested in a comment, I would use image (takes 0.12s on my system for your image).
EDIT: more comments, fix little bug, allow to create bins with stepsize > 1
img_fn = "17S9PUK.jpg";
if (! exist (img_fn, "file"))
disp ("downloading image from imgur.com...");
fflush (stdout);
urlwrite ("http://i.imgur.com/17S9PUK.jpg", "17S9PUK.jpg");
endif
rgbImage = imread (img_fn);
## for debugging so the matrixes fit on screen
if (0)
pkg load image
rgbImage = imresize (rgbImage, [6 8]);
endif
hsvImage = rgb2hsv(rgbImage);
hPlane = 360 .* hsvImage(:, :, 1);
## create bins, I've choosen 2 step to "smooth" the result
binEdges = 1:2:360;
N = histc (hPlane(:), binEdges)';
cm = permute (hsv (numel (binEdges)), [3 1 2]);
## Create an image with x = hue
img = repmat (cm, max(N), 1);
## Create binary mask which is used to black "img" dependent on N
sp = sparse (N(N > 0), (1:360)(N > 0), true, max(N), numel (binEdges));
mask = full (cumsum (flipud (sp)));
## extend mask in depth to suppress RGB
mask = repmat (mask, [1 1 3]);
## use inverted mask to "black out" pixels < N
img(logical (1 - flipud (mask))) = 0;
## show image
image (binEdges, 1:max(N), img)
set (gca, "ydir", "normal");
xlabel('HSV hue (in degrees)');
ylabel('Bin counts');
## print it for stackoverflow
print ("out.png")
Same as above but with bin width 1 (Elapsed time is 0.167423 seconds.)
binEdges = 1:360;
I have an X Y Z dataset.
X and Y are the 2D coordinates and Z is the intensity.
I plot the data using the scatter function:
markerSize=100;
scatter(x,y,markerSize,z,'s','fill');
I use the options 's' and 'fill' to get filled squares.
My problem is the markerSize value corresponds to the area of the marker, and its unit is points (1/72 of one inch).
The marker size is constant, even if I resize the figure plot. So that the gap between the data points increases when I increase the figure size.
What I would like is a constant marker size which is a constant of the axis unit. For instance, the marker size should be 5x5 (5 in X axis and 5 in Y axis).
Thanks for your help.
You want to make the size of markers proportional to the figure size.
The size of markers is controlled by SizeData parameter of the scattergroup object. The size of figure is stored in Position parameter of the figure object. The difficult part is to interactively resize the marker when the figure size is changed. So you need to use ResizeFcn callback and call setmarkersize function that you define.
function [ ] = setmarkersize( src, evnt )
% # get position of the figure (pos = [x, y, width, height])
pos = get(src, 'Position');
% # get the scattergroup object
h = get(get(src,'children'),'children');
% # resize the marker
relativesize = 0.5;
set(h,'SizeData', pos(3)*relativesize);
end
================================================
% # attach the callback to figure
f = figure('ResizeFcn', #setmarkersize);
h = scatter(x,y,markerSize,z,'s','fill');
You will have to set the marker size manually according to the actual figure size on screen. Using axes property Position you can convert data units to relative figure units. In the next step this size can be converted to the absolute size in points on screen. With that information you can set the marker size accordingly. In the following code snippet I've set the x/y axis limits and width/height of the axis to identical values, because the square marker area can only be calculated reasonably if the marker width is equal to the marker height.
Set marker size relative to data units
% test data
x = [25*rand(1,10) 2.5];
y = [25*rand(1,10) 2.5];
z = [rand(1,10) 0.5];
% relative marker size in squared normalized figure units
marker_rel = 5;
%% Set relative marker size (approximately)
scatter(x,y,100,z,'s','fill');
% Set identical x/y limits and set axes height=widht, so that markers
% really represent squares in data units
xlim([0 25]);
ylim([0 25]);
set(gca, 'Units', 'Points');
axpos_pt = get(gca, 'Position');
axpos_pt = [axpos_pt(1) axpos_pt(2) min(axpos(3:4)) min(axpos(3:4))];
set(gca, 'Position', axpos_pt);
grid on;
grid minor;
% Set marker size relative to data units
markerSize = (marker_rel * axpos_pt(3) / diff(xlim))^2;
set(get(gca, 'children'), 'sizedata', markerSize);
As it turns out, the displayed marker size is slightly smaller than expected. Obviously, there's some bounding box of unknown (at least to me) size, see here.
An alternative approach is to plot rectangles "manually" as shown in the following code snippet (same test data is used). When the figure is resized, the rectangles are resized as well without any special callback function being needed.
Draw rectangles manually
%% Use rectangles (exact)
figure;
axes;
cmp = colormap;
z_range = max(z) - min(z);
line_style = 'none'; % set to '-' to make the edges visible
for k = 1:length(x)
x_pos = x(k) - marker_rel/2;
y_pos = y(k) - marker_rel/2;
w = marker_rel;
h = marker_rel;
color = cmp( round(((z(k) - min(z))/z_range)*(length(cmp) - 1)) + 1, : );
rectangle('Position', [x_pos y_pos w h], 'FaceColor', color,...
'LineStyle', line_style);
end
grid on;
grid minor;
The above code produces the desired marker size:
In general, these are no squares. They can (and will) only be squares if xlim = ylim and absolute height of axis = absolute width of axis. I have shown in my first code snippet how to achieve this.
I found an answer on the Matlab central forum which does not use the useful dsxy2figxy function. Here is the link to it (link)
The code is the following:
x = rand(1,50);
y = rand(1,50);
s = 5; %Marker width in units of X
h = scatter(x,y); % Create a scatter plot and return a handle to the 'hggroup' object
%Obtain the axes size (in axpos) in Points
currentunits = get(gca,'Units');
set(gca, 'Units', 'Points');
axpos = get(gca,'Position');
set(gca, 'Units', currentunits);
markerWidth = s/diff(xlim)*axpos(3); % Calculate Marker width in points
set(h, 'SizeData', markerWidth^2)
The answer by ysakamoto doesn't work in Matlab 2014, where Mathworks changed figure handles from double to object. The following modification makes it work again:
function [ ] = setmarkersize( src, ~ )
% get position of the figure (pos = [x, y, width, height])
pos = get(src, 'Position');
% marker size
relativesize = 0.01;
% axes is not necessarily the only child of figure (e.g. colorbar may be first)
for i = 1:numel(src.Children)
if strcmpi(src.Children(i).Type, 'axes')
% make marker size depend on figure width
src.Children(i).Children.SizeData = pos(3) * relativesize;
break
end
end
end
Also, when creating the figure it will not set the marker size to the correct value until resized. So call setmarkersize explicitly:
f = figure('ResizeFcn', #setmarkersize);
...
setmarkersize(f)
Consider abandoning 'markers' and drawing directly on the axis using rectangles or circles. The 'rectangle' command with 'curvature' set to [1 1] is essentially an open circle. Units are in plot units, so each rectangle can be scaled:
rectangle('position', [rectCentX rectCentY widthInPlotUnits, heightInPlotUnits],'curvature',[1 1])
I want to shift the x ticks labels downwards in this figure:
I'm not sure how to do this?
This is the script I'm using:
y=[0.5093 0.8526 0.9171];
x=[0 1600 1100];
hand =plot(y, 'ob-');
set(gca, 'XTick',1:3, 'XTickLabel',{'no interference' '1600' '1100'})
set(hand, 'LineWidth', 4);
set(hand, 'MarkerSize', 30);
set(findobj('type','text'),'FontSize',25);
set(gca,'FontSize',25);
set(findobj('type','axes'),'FontSize',25);
h=get(gca,'Title');
set(h,'FontSize',20);
Following the example from this mathworks solution, you can use the text function to add labels in any position you wish.
Increase the value of delta for a larger gap between x tick labels and x axis.
EDIT: Added custom control of yticks: the value of stp changes the step between each tick. Obviously a more general solution would identify the end-points of the tick range automatically as well.
figure(1), clf
% set data as your example
y=[0.5093 0.8526 0.9171];
x=[0 1600 1100];
Xt=1:length(x);
hand =plot(y, 'ob-');
set(gca, 'XTick',Xt);
stp=0.05;
Yt=0.5:stp:0.95;
set(gca, 'ytick', Yt)
% Reduce the size of the axis so that all the labels fit in the figure.
pos = get(gca,'Position');
set(gca,'Position',[pos(1), .2, pos(3) .7])
ax = axis; % Current axis limits
axis(axis); % Set the axis limit modes (e.g. XLimMode) to manual
Yl = ax(3:4); % Y-axis limits
Xl = ax(1:2);
% Place the text labels -- the value of delta modifies how far the labels
% are from the axis.
delta=0.1;
t = text(Xt, Yl(1)*ones(1,length(x))-delta, {'no interference' '1600' '1100'});
%set(t, 'HorizontalAlignment','left','VerticalAlignment','top')
set(t, 'HorizontalAlignment','center','VerticalAlignment','middle')
% Remove the default labels
set(gca,'XTickLabel','')
% and continue with your other settings as required
set(hand, 'LineWidth', 4);
set(hand, 'MarkerSize', 30);
set(findobj('type','text'),'FontSize',25);
set(gca,'FontSize',25);
set(findobj('type','axes'),'FontSize',25);
h=get(gca,'Title');
set(h,'FontSize',20);
The text function has lots of options that you can configure.
I think what most people want is something that works in 2-3 lines of code, nevermind-ing a quick&dirty approach.
This is undocumented (credits go to here) but just works:
% adjust ticklabels away from axes
a=gca;
a.XRuler.TickLabelGapOffset = 8;
a.YRuler.TickLabelGapOffset = 8;
tested with Matlab 2019a
I have 3 objects (a photo and 2 plots) to put into subplots on one figure. It should look like this:
But as one can notice, the photo should not be square but rectangle. I tried to make it this way (found here Matlab: How to align the axes of subplots when one of them contains a colorbar?):
main=subplot(4,4,[5,6,7,9,10,11,13,14,15]) %photo
imagesc(im);
axis('image')
pion=subplot(4,4,[8,12,16]); %right plot (rotated)
view(90, 90)
plot(ypion,Ppion,'.k');
poz=subplot(4,4,1:3); %upper plot
plot(xpoz,Ppoz,'.k');
pos1=get(poz,'Position')
pos2=get(main,'Position')
pos3=get(pion,'Position')
pos1(3) = pos2(3); %width for the upper plot
set(poz,'Position',pos1)
pos3(4) = pos2(4); %height for the right plot
set(pion,'Position',pos3)
All I get is like this:
How can I force the upper plot to have the width as the photo itself (not as the photo subplot)? Setting the equal widths of the subplots doesn't work, as the photo doesn't fill the subplot area.
The command axis image adjust the image axis ratio. So, in principle, if you adjust the plot ratios of the two plots to the same ratio, it will do what you want.
There is one caveat; the image is inherently 3 times wider or higher than the plots, due to the fact that you've plotted it in 3x3 subplots, vs 1x3 for the top and 3x1 for the right plots. So, you'll have to divide either the x or y ratios of the plots by 3.
Some example code:
clc, clf
% generate some bogus data
ypion = rand(500,1);
Ppion = 450*rand(500,1);
xpoz = rand(500,1);
Ppoz = 450*rand(500,1);
% Load photo
photoSub = subplot(4,4,[5,6,7,9,10,11,13,14,15]);
load mandrill
photo = imagesc([X,X]);
colormap(map)
axis image
photoAxs = gca;
photoAxsRatio = get(photoAxs,'PlotBoxAspectRatio')
% right plot
subplot(4,4,[8,12,16]);
plot(Ppion,ypion,'k.');
rightAxs = gca;
axis tight
% upper plot
subplot(4,4,[1 2 3]);
plot(xpoz,Ppoz,'k.');
topAxs = gca;
axis tight
% adjust ratios
topAxsRatio = photoAxsRatio;
topAxsRatio(2) = photoAxsRatio(2)/3.8; % NOTE: not exactly 3...
set(topAxs,'PlotBoxAspectRatio', topAxsRatio)
rightAxsRatio = photoAxsRatio;
rightAxsRatio(1) = photoAxsRatio(1)/3.6; % NOTE: not exactly 3...
set(rightAxs,'PlotBoxAspectRatio', rightAxsRatio)
This gives the following result:
Just to test, changing photo = imagesc([X,X]); to photo = imagesc([X;X]); gives this:
Note that I did not divide the ratios by 3 exactly; it only came out OK if I used factors closer to 4. I do not know why that is; AFAIK, a factor of 3 should do the trick...
Oh well, at least you have something to work with now :)
Here's a solution that removes the guesswork in the accepted answer. This solution is adapted from the original one posted here.
% adjust ratios
photoAxsratio = photoAxs.PlotBoxAspectRatio(1)/photoAxs.PlotBoxAspectRatio(2);
topAxsratio = photoAxsratio * photoAxs.Position(4)/topAxs.Position(4);
topAxs.PlotBoxAspectRatio = [topAxsratio, 1, 1];
rightAxsratio = rightAxs.Position(3) / (photoAxs.Position(3) / photoAxsratio);
rightAxs.PlotBoxAspectRatio = [rightAxsratio, 1, 1];
Preview:
A bit of explanation
Some of the explanation has been posted in the original post, I'm not going to repeat them here.
The idea is to calculate the correct aspect ratio for the figures required to be resized.
We have the following equations:
Photo.width = Photo.height * Photo.ratio
TopAxis.width = TopAxis.height * TopAxis.ratio
RightAxis.width = RightAxis.height * RightAxis.ratio
Let
TopAxis.width = Photo.width
RightAxis.height = Photo.height
We have
TopAxis.height * TopAxis.ratio = Photo.height * Photo.ratio
TopAixs.ratio = Photo.ratio * Photo.height / TopAxis.height
RightAxis.width / RightAxis.ratio = Photo.width / Photo.ratio
RightAxis.ratio = RightAxis.width / (Photo.width / Photo.ratio)
Since the release of matlab R2019b you can now use: tiledlayout and nexttile.
It is now easy to do:
% Load a random scary image
I = im2gray(imread('https://unwinnable.com/wp-content/uploads/2011/10/The-Ring-well.jpg'));
% Some computation...
Sx = sum(I)/max(sum(I));
Sy = sum(I,2)/max(sum(I,2));
% We create an empty 3x3 tiled layout:
% 1 2 3
% 4 5 6
% 7 8 9
tiledlayout(3, 3);
% Starting from the tile 1, we plot a 1x2 tile:
% 1 2 x
% x x x
% x x x
nexttile(1,[1 2])
plot(Sx,'k.')
% Starting from the tile 4, we plot a 2x2 tile:
% x x x
% 4 5 x
% 7 8 x
nexttile(4,[2 2])
imagesc(I)
colormap(gray(256))
% Starting from the tile 6, we plot a 2x1 tile:
% x x x
% x x 6
% x x 9
nexttile(6,[2 1])
plot(Sy,1:numel(Sy),'k.')
Matlab adjust the size of the plots automatically.
And we obtain:
Under the hood the tiled layout looks like this:
For this particular case I suggest using low-level axes directly instead of high-level subplot.
Use the 'OuterPosition' properties of the three axes objects you create to place them in the right place with the appropriate size.
If you want the image to be aligned to the axes(distorted image):
change axis('image') to axis('tight') .
If you want the image aspect ratio to remain, and to align the axes to the image:
Well this is quick and dirty, but after applying axis('tight'), resize the figure to the appropriate scale...
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);