Getting video from image sequence of figures with subplots in MATLAB - matlab

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)

Related

creating a loop to save images from contour slice

I have the following code to plot a slice at any location in a fluid volume.
clc, clear all, close all
format long
%a ddpath('\\ds.leeds.ac.uk\staff\staff6\censsar\Polydisperse'); % <------Change
%% Import data
input = importdata('lci_000210.dat',' ',3);
nx = 96;
ny = 96;
nz = 49;
x = input.data(:,1);
y = input.data(:,2);
z = input.data(:,3);
Lci = input.data(:,4);
L3d = reshape(Lci,[nx,ny,nz]);
x3d = reshape(x,[nx,ny,nz]);
y3d = reshape(y,[nx,ny,nz]);
z3d = reshape(z,[nx,ny,nz]);
contourslice(y3d,x3d,z3d,L3d,[],[],[0.3]);
ax=gca;
ax.Children(1).LineStyle='none';
ax.Children(2).LineStyle='none';
ax.Children(3).LineStyle='none';
view(25,20);
colormap jet
colorbar
What I would like to do is have the contour slice in a loop so that it creates and saves a slice at each z location. I want to hopefully create a movie of the z slice moving from zero and going up the 3d axis.
I have tried something like:
Z=[0 0.1 0.5];
for S = 1:length(Z)
h = figure
contourslice(y3d,x3d,z3d,L3d,[],[],[Z],10);
saveas(h,sprintf('Fig%d.png',S));
end
but this is not working, I am not sure how to define the z axis in the loop, so it creates a slice at each point.
Here is the link to my data, it is a .dat file so it contains the data in 4 columns.
To create a set of slices in a loop you have to modify your code by replacing the last but one parameter in the call to contourslice by specifiybg the i-th element of the array Z.
Since you did not post your input data I've tested the proposed solution on an example data from MatLab contourslice help slightly modified in which
are created nine contour plots in the y-z plane, no plots in the x-z plane, and one plot in the x-y plane by specifying Sx as a vector of nine elements, Sy as an empty vector, and Sz as a scalar (from MatLab help).
Yoy have to adapt the definition of the Sx, Sy and Sz parameters to your needs.
To create a movie you can use the functions:
videowriter to create the video object
open to open the video file
getframe to capture axes or figure as movie frame
close to close and save the video file
As an alternative to the movie, you can create an animated gif by using the function imwrite
% Load input data
[X,Y,Z,V] = flow;
% Define the parameters for the set of slices
Sx = 1:9;
Sy = [];
Sz = [];
cvals = linspace(-8,2,10);
% Open the FIGURE window
figure
% Create the axes and set tehiur properties
axis([0,10,-3,3,-3,3])
hold on
daspect([1,1,1])
campos([0,-20,7])
box on
% Create the movie object
mov=VideoWriter('contour_slice_movie.avi');
% Open the movie file
open(mov);
% Define the number of frames to be captured for each slice
n_frame_x_image=33;
% Loop over the desired number of slices
for i=1:length(Sx)
contourslice(X,Y,Z,V,Sx(i),Sy,Sz,cvals)
% Capture the frames
for j=1:n_frame_x_image
FF=getframe(gcf);
writeVideo(mov,FF);
end
end
% Close the movie file
close(mov);

Plot digitization in MATLAB using ginput

I'm trying to digitize this image using MATLAB:
I have the following script:
%// Get data from plot
clear all; close all;
%// Input
fname = 'Fig15a.PNG';
xvec = [1e3:1:1e8];
yvec = [1e-4:1:1e-1];
xt = [1e3 1e4 1e5 1e6 1e7 1e8];
yt = [1e-4 1e-3 1e-2 1e-1];
%// Read and plot the image
im = imread(fname);
figure(1), clf
im = im(end:-1:1,:,:);
image(xvec,yvec,im)
axis xy;
grid on;
%// Set ticks
set(gca,'xtick',xt,'ytick',yt); %// Match tick marks
%// Collect data
[x,y] = ginput; %// Click on points, and then hit ENTER to finish
%// Plot collected data
hold on; plot(x,y,'r-o'); hold off;
%// Then save data as:
save Fig15a.mat x y
The script works fine
Is there a way I can change the x and y axes to a log scale ?
I have tried adding the following code in different places without luck:
%// Set Log scale on x and y axes
set(gca,'XScale','log','YScale','log');
Below's a proof of concept that should get you on the right track. I have replaced things in your original code with what I consider "good practices".
function q36470836
%% // Definitions:
FIG_NUM = 36470836;
%% // Inputs:
fname = 'http://i.stack.imgur.com/2as4t.png';
xt = logspace(3,8,6);
yt = logspace(-4,-1,4);
%% // Init
figure(FIG_NUM); clf
% Read and plot the image
im = imread(fname);
hIMG = imshow(im); axis image;
%// Set ticks
hDigitizer = axes('Color','none',...
'XLim',[xt(1) xt(end)],'YLim',[yt(1) yt(end)],...
'XScale','log','YScale','log',...
'Position',hIMG.Parent.Position .* [1 1 696/785 (609-64+1)/609]);
uistack(hDigitizer,'top'); %// May be required in some cases
grid on; hold on; grid minor;
%// Collect data:
[x,y] = ginput; %// Click on points, and then hit ENTER to finish
%// Plot collected data:
scatter(x,y,'o','MarkerEdgeColor','r');
%// Save data:
save Fig15a.mat x y
Here's an example of what it looks like:
Few notes:
xt, yt may be created in a cleaner fashion using logspace.
It is difficult (possibly impossible) to align the digitization grid with the image correctly, which would inevitably result in errors in your data. Though this can be helped in the following scenarios (for which you will require a vector graphics editor, such as the freeware InkScape):
If, by any chance, you got this image from a PDF file, where it appears as a vector image (you can test this by zooming in as much as you like without the chart becoming pixelated; this seems to be your case from the way the .png looks), you would be better off saving it as a vector image and then you have two options:
Exporting the image to a bitmap with a greatly increased resolution and then attempting the digitization procedure again.
Saving the vector image as .svg then opening the file using your favorite text editor and getting the exact coordinates of the points.
If the source image is a bitmap (as opposed to vector graphic), you can "trace the bitmap", thus converting it to vectoric, then #GOTO step 1.
This solution doesn't (currently) support resizing of the figure.
The magic numbers appearing in the Position setting are scaling factors explained in the image below (and also size(im) is [609 785 3]). These can technically be found using "primitive image processing" but in this case I just hard-coded them explicitly.
You can plot in double logarithmic scale with
loglog(x,y);
help loglog or the documentation give additional information.
For a single logarithmic scale use
semilogx(x,y);
semilogy(x,y);

Creating a video from a set of 3D plots

I used a function called ind2patch to make a 3D block which contains a number of smaller blocks in 3 dimensions. Each small block has a value that is represented by a color. A typical plot is like this one:
Now I would like to show the evolution of values (i.e. the color) with time of these small blocks using a video. I have data at different moments but I only know how to plot the graphs at different time by reading different files. Is there a way to combine the plots to a video or directly plot the graphs in the form of video?
Here is my code:
clear; close all; clc;
fig = figure(1);
set (fig, 'Units', 'normalized', 'Position', [0,0,1,1]);
fig_color='w'; fig_colordef='white';
cMap=jet(256);
faceAlpha1=1;
faceAlpha2=0.65;
edgeColor1='none';
edgeColor2='none';
NumBoxX=100;%box number in x direction
NumBoxY=100;%box number in y direction
NumBoxZ=5;%box number in z direction
fid = fopen('rho 20950.dat','r');
datacell = textscan(fid, '%f%f%f%f%f%f%f%f');
fclose(fid);
all_data = cell2mat(datacell);
M=zeros(NumBoxX,NumBoxY,NumBoxZ);
for i=1:NumBoxX
for j=1:NumBoxY
for k=1:NumBoxZ
num=k+NumBoxZ*(j-1)+NumBoxZ*NumBoxY*(i-1);
M(i,j,k)=all_data(num,4);
end
end
end
indPatch=1:numel(M);
[F,V,C]=ind2patch(indPatch,M,'v');
title('\sigma_{xy} in different cells','fontsize',20);
xlabel('y','fontsize',20);ylabel('x','fontsize',20); zlabel('z','fontsize',20); hold on;
set(get(gca,'xlabel'),'Position',[5 -50 30]);
set(get(gca,'ylabel'),'Position',[5 50 -15]);
set(get(gca,'zlabel'),'Position',[64 190 -60]);
patch('Faces',F,'Vertices',V,'FaceColor','flat','CData',C,'EdgeColor','k','FaceAlpha',0.5);
axis equal; view(3); axis tight; axis vis3d; grid off;
colormap(cMap); caxis([min(M(:)) max(M(:))]);
cb = colorbar;
set(get(cb,'title'),'string','Stress (MPa)','fontsize',20);
lbpos = get(cb,'title'); % get the handle of the colorbar title
%set(lbpos,'Units','data');% change Units to data
%pos = get (lbpos,'position'); % get position of the colorbar title
set(lbpos,'units','normalized','position',[0,1.04]);
MyAxes=gca;
set(MyAxes,'Units','Normalized','position',[0.05,0.1,0.8,0.8]);
zoom(1.85);
You could do something like this:
Loop through each patch and grab an image of it.
Insert the images into a matrix
Convert the image matrix into a movie using immovie
% // Create a matrix to hold your images
A = zeros(row,col,numOfColours, numOfFrames);
where row is the number of rows and col is the number of columns in one image.
Loop through your patches and create a video of the individual images.
for n=1:numOfPatches
imshow(patches(:,:,n)) % // display the image
frame = getframe(gcf) % // get the current figure window
im = frame2im(frame); % // convert it to an image
A(:,:,1:3,n) = im; % // Insert the image into the matrix
end
The you can use immovie to convert it to a movie
mov = immovie(RGB);
movie(mov); % // play the movie

Include axes labels when saving plot from MATLAB GUI

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!

matlab: how to plot multidimensional array

Let's say I have 9 MxN black and white images that are in some way related to one another (i.e. time lapse of some event). What is a way that I can display all of these images on one surface plot?
Assume the MxN matrices only contain 0's and 1's. Assume the images simply contain white lines on a black background (i.e. pixel value == 1 if that pixel is part of a line, 0 otherwise). Assume images are ordered in such a way as to suggest movement progression of line(s) in subsequent images. I want to be able to see a "side-view" (or volumetric representation) of these images which will show the surface that a particular line "carves out" in its movement across the images.
Coding is done in MATLAB. I have looked at plot (but it only does 2D plots) and surf, which does 3D plots but doesn't work for my MxNx9 matrix of images. I have also tried to experiment with contourslice, but not sure what parameters to pass it.
Thanks!
Mariya
Are these images black and white with simple features on a "blank" field, or greyscale, with more dense information?
I can see a couple of approaches.
You can use movie() to display a sequence of images as an animation.
For a static view of sparse, simple data, you could plot each image as a separate layer in a single figure, giving each layer a different color for the foreground, and using AlphaData to make the background transparent so all the steps in the sequenc show through. The gradient of colors corresponds to position in the image sequence. Here's an example.
function plotImageSequence
% Made-up test data
nLayers = 9;
x = zeros(100,100,nLayers);
for i = 1:nLayers
x(20+(3*i),:,i) = 1;
end
% Plot each image as a "layer", indicated by color
figure;
hold on;
for i = 1:nLayers
layerData = x(:,:,i);
alphaMask = layerData == 1;
layerData(logical(layerData)) = i; % So each layer gets its own color
image('CData',layerData,...
'AlphaData',alphaMask,...
'CDataMapping','scaled');
end
hold off
Directly showing the path of movement a "line" carves out is hard with raster data, because Matlab won't know which "moved" pixels in two subsequent images are associated with each other. Don't suppose you have underlying vector data for the geometric features in the images? Plot3() might allow you to show their movement, with time as the z axis. Or you could use the regular plot() and some manual fiddling to plot the paths of all the control points or vertexes in the geometric features.
EDIT: Here's a variation that uses patch() to draw each pixel as a little polygon floating in space at the Z level of its index in the image sequence. I think this will look more like the "surface" style plots you are asking for. You could fiddle with the FaceAlpha property to make dense plots more legible.
function plotImageSequencePatch
% Made-up test data
nLayers = 6;
sz = [50 50];
img = zeros(sz(1),sz(2),nLayers);
for i = 1:nLayers
img(20+(3*i),:,i) = 1;
end
% Plot each image as a "layer", indicated by color
% With each "pixel" as a separate patch
figure;
set(gca, 'XLim', [0 sz(1)]);
set(gca, 'YLim', [0 sz(2)]);
hold on;
for i = 1:nLayers
layerData = img(:,:,i);
[x,y] = find(layerData); % X,Y of all pixels
% Reshape in to patch outline
x = x';
y = y';
patch_x = [x; x+1; x+1; x];
patch_y = [y; y; y+1; y+1];
patch_z = repmat(i, size(patch_x));
patch(patch_x, patch_y, patch_z, i);
end
hold off