I have written the following code to try and retrieve ONLY the axes and its plot from my MATLAB GUI.
F = getframe(gca);
figure();
image(F.cdata);
saveas(gcf,'PlotPic','png');
close(gcf);
I noticed, however, that this method does not include ANY of my axis labels or title. Is there any way which I can get the getframe function to include the axis labels and title?
I tried the following code but it did exactly the same
pl = plot(x,y);
xlabel('x')
ylabel('y')
ftmp = figure;
atmp = axes;
copyobj(pl,atmp);
saveas(ftmp,'PlotPic.png');
delete(ftmp);
I would do it using the rect option of the getframe function.
Basically you can provide a 2nd input argument to getframe, which then captures the content of the rectangle specified as argument. The nice thing is that you can use the handles to an axes, so it does not capture your whole GUI figure but rather a specific axes.
For example, using this line:
F = getframe(gca,RectanglePosition);
Concretely, you could set the coordinates of the rectangle such that they span both axis labels and the title as well. Here is a sample code. The pushbutton callback executes getframe and opens a new figure with the content of F.cdata:
function GUI_GetFrame
clc
clear
close all
%// Create GUI components
hFigure = figure('Position',[100 100 500 500],'Units','Pixels');
handles.axes1 = axes('Units','Pixels','Position',[60,90,400,300]);
handles.Button = uicontrol('Style','Push','Position',[200 470 60 20],'String','Get frame','Callback',#(s,e) GetFrameCallback);
%// Just create a dummy plot to illustrate
handles.Period = 2*pi;
handles.Frequency = 1/handles.Period;
handles.x = 0:pi/10:2*pi;
handles.y = rand(1)*sin(handles.Period.*handles.x);
plot(handles.x,handles.y,'Parent',handles.axes1)
title('This is a nice title','FontSize',18);
guidata(hFigure,handles); %// Save handles structure of GUI.
function GetFrameCallback(~,~)
handles = guidata(hFigure);
%// Get the position of the axes you are interested in. The 3rd and
%// 4th coordinates are useful (width and height).
AxesPos = get(handles.axes1,'Position');
%// Call getframe with a custom rectangle size.You might need to change this.
F = getframe(gca,[-30 -30 AxesPos(3)+50 AxesPos(4)+80]);
%// Just to display the result
figure()
imshow(F.cdata)
end
end
The GUI looks like this:
And once I press the pushbutton, this is the figure that pops up:
So the only trouble you have is to figure out the dimensions of the rectangle you need to select to capture the axis labels and the title.
Hope that solves your problem!
Related
Please see the following code which creates a 2 by 2 subplot with some plots:
x = linspace(0,2*pi);
y = sin(x);
hfig = figure('Position',[1317 474 760 729]);
subplot(2,2,1)
plot(x,y)
ylabel('plot1');
subplot(2,2,2)
plot(x,y.^2)
ylabel('plot2');
subplot(2,2,3)
plot(x,y.^3)
ylabel('plot3');
subplot(2,2,4)
plot(x,abs(y))
ylabel('plot4');
in each one, I have added labels by hand in Tools: Edit plot (a) (b) (c) (d) producing this figure:
The problem is, if I resize the plot they are no longer aligned with the ylabel text:
Is there a way to add these labels programmatically and have them automatically align to the ylabel text? I am surprised MATLAB does not have something like this built in already.
Thanks
This is not something that is easy to do without attaching a listener to the figure resize event (see example), and doing some computations related to aspect ratios.
It's not entirely clear what sort of objects your labels are (text or annotation), so I'll just show how to do this programmatically using the text command, which creates labels in axes coordinates (as opposed to figure coordinates). This doesn't solve the problem entirely, but it looks better, possibly to an acceptable degree:
function q56624258
x = linspace(0,2*pi);
y = sin(x);
hF = figure('Position',[-1500 174 760 729]);
%% Create plots
[hAx,hYL] = deal(gobjects(4,1));
for ind1 = 1:3
hAx(ind1) = subplot(2,2,ind1, 'Parent' , hF);
plot(hAx(ind1), x,y.^ind1);
hYL(ind1) = ylabel("plot" + ind1);
end
hAx(4) = subplot(2,2,4);
plot(hAx(4), x,abs(y));
hYL(4) = ylabel('plot4');
%% Add texts (in data coordinates; x-position is copied from the y-label)
for ind1 = 1:4
text(hAx(ind1), hYL(ind1).Position(1), 1.1, ['(' char('a'+ind1-1) ')'], ...
'HorizontalAlignment', 'center');
end
Note several modifications to your code:
The handles returned by some functions that create graphical elements are now stored (mainly: hAx, hYL).
All functions that create graphical elements (subplot, plot, ylabel) now have the target (i.e. parent or container) specified.
I changed the 'Position' of the figure so that it works in my setup (you might want to change it back).
I have a sequence of MATLAB figures which I want to convert to a video. The figures are composed of 2 subplots, such that each subplot contains an imshow with 2 different plots (red and green) overlayed on it, like the following:
How do I get an image of all the data contained inside each figure, so that next I can convert the sequence of images to a video with VideoWriter?
The key is:
im = frame2im(getframe(h));
Where h is the handle to your plot or axis
You can also pass an optional argument 'rect' to getframe to specify the area to capture.
Here is an example from the MathWorks on how to use getframe in a loop to record the frames. For more information type doc getframe
Z = peaks;
surf(Z)
axis tight manual
ax = gca;
ax.NextPlot = 'replaceChildren';
loops = 40;
F(loops) = struct('cdata',[],'colormap',[]);
for j = 1:loops
X = sin(j*pi/10)*Z;
surf(X,Z)
drawnow
F(j) = getframe(gcf);
end
% Play back the movie two times.
fig = figure;
movie(fig,F,2)
In a for loop, I create a variable number of subplots that are displayed on a single figure. Can I also save each subplot as a separate, full size plot and image file (preferably JPG)?
Use copyobj to a new figure and then use saveas with the new figure handle:
Example code that YOU should have provided (see SSCCE):
figure
nLines = 2;
nColumns = 3;
handles = zeros(nLines,nColumns)
for line = 1:nLines
for column = 1:nColumns
handles(line,column)=subplot(nLines,nColumns,column+(line-1)*nColumns);
plot([line column]);
title(sprintf('Cool title (%d,%d)',line,column))
ylabel(sprintf('Ylabel yeah (%d,%d)',line,column))
xlabel(sprintf('Xlabel nah (%d,%d)',line,column))
end
end
Here I have the subplot handles saved, but supposing you don't have them saved:
axesH = findobj(gcf,'Type','axes','-not','Tag','legend'); % This will change the order from which the axes will be saved. If you need to save in the correct order, you will need access to the subplot handles
nAxes = numel(axesH)
newFig = figure;
for k=1:nAxes
newAxes=copyobj(axesH(k),newFig);
% Expand subplot to occupy the hole figure:
set(newAxes,'OuterPosition',[0 0 1 1]);
tightInset=get(newAxes,'TightInset');
set(newAxes,'Position',[tightInset(1:2) [1 1]-(tightInset(3:4)+tightInset(1:2))])
saveas(newFig,sprintf('axes_%d.jpg',k),'jpg');
delete(newAxes);
end
delete(newFig);
Example of one axes saved:
To remove the deadspace I used information available on this topic.
Say you have the handle to the subfigure's axis, ha, you can use getframe and frame2im as follows,
F = getframe(ha);
[im,map] = frame2im(F);
if isempty(map)
imwrite(im,'subframe.jpg');
else
imwrite(im,map,'subframe.jpg');
end
Note that this will save the axis exactly how it appears on your screen, so resize the figure to your liking before saving. To use as much figure real estate as possible, tryout the subfigure_tight function on MATLAB Central.
I am trying to plot both horizontal and vertical lines on a histogram that will be accurate to changing limits on both the x and y axes. I was using the line(X,Y) function, but cannot find a useful way to get the lines to be set depending on the parameters of the graph window.
I'm not entirely clear on what you want but here's the simplest answer to what I think you want:
Makes a sample histogram
y = randn(100,1);
hist(y,10)
Get the current limits of the x and y axes
xlimits = get(gca, 'XLim');
ylimits = get(gca, 'YLim');
Computes a single numeric value to plot a horizontal line.You'll want to replace this with your specific function of the axes limits
halfpt = ((ylimits(2)-ylimits(1))/2) + ylimits(1);
line(xlimits, [halfpt halfpt])
I'm not sure, but from your comment I'm suspecting that you aren't changing your axes programmatically, with say set(gca,'Xlim', [0 10]) but that you want to be able to drag the axes of your your figure with the mouse, say by using that hand/pointer button in the figure editor. In which case, one solution is to make your figure a GUI and write a callback function that handles line plotting that is a function of the xlim and ylim. Here's an example that always keeps the line in the middle of the axes regardless of how they are dragged:
function myGUI
figure('WindowButtonMotionFcn',#myCallback)
y = randn(100,1);
hist(y,10)
function myCallback(src,eventdata)
xlimits = get(gca, 'XLim');
ylimits = get(gca, 'YLim');
halfpt = ((ylimits(2)-ylimits(1))/2) + ylimits(1);
lh = findall(gcf,'Type','Line');
delete(lh);
myline = line(xlimits, [halfpt halfpt])
end
end
So I have a simple loop in MATLAB that does the following:
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
figure(1)
plot(randn(1,100));
figure(2);
plot(randn(1,100));
end
The x and y are made up, but that is the jist of it. Anyway, when I run this code, not surprisingly, MATLAB will make two figures and plot accordingly. The problem is, I get a sort of 'blinking' between figures when I do this, and it makes the quality of seeing x and y evolve over time poorer.
I discovered a way to make one of the plots smoother like this:
figure(1);
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(randn(1,100));
drawnow
end
If I do this, then of course figure(1) will plot very smoothly showing x nicely, without figure(1) 'blinking' between plots, but now I cant show figure(2) or y!
How can I plot both those quantities on different figures (not subplots) smoothly without 'blinking'?
EDIT:
Thanks Geodesic for your answer, the solution works, however there is a subtlety that I did not think would be an issue, however it is.
1) I am unable to use 'imagesc' with this solution.
For example,
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*rand(10,100);
plot(aone,x);
drawnow;
imagesc(atwo,y);
drawnow;
end
In this case the part with imagesc(atwo, y) crashes.
Your flicker is because you're generating each figure window again and again through the loop, which is forcing the window to come to the foreground each time. Generate the figures first, attach some axes to them, and plot your data to each axis like so:
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(aone,randn(1,100));
drawnow;
imagesc(y,'Parent',atwo);
drawnow;
end
Edit: functions like plot take an axis argument directly, but imagesc does not. In this particular case you'll need to send a Property Name/Value pair in as an argument. The 'Parent' of the image generated will be our axis atwo (see above).
For p = 1, create the plots you need, using the plot command or the imagesc command. Keep the handle of the resulting graphics object by getting an output argument: for example h = plot(.... or h = imagesc(..... This will be a Handle Graphics lineseries or image object, or something else, depending on the particular plot type you create.
For p = 2:100, don't use the plotting commands directly, but instead update the relevant Data properties of the original Handle Graphics object h. For example, for a lineseries object resulting from a plot command, set its XData and YData properties to the new data. For an image object resulting from an imagesc command, set its CData property to the new image.
If necessary, call drawnow after updating to force a flush of the graphics queue.