Computationally Fast Way to Save Matlab Figures as Image (eg PNG) - matlab

I have a program that creates a lot of plots which I want to save as fig001, fig002, .... At the moment, the only way I can do this is to either save them one by one as I go along using print() or so save them into a graphics object FIG.
Unfortunately, I have to create the plots sequentially (can't parallelise it). If I save as I go, then this takes quite a long time; if I store in FIG then do a parfor-loop over all the figures, then this is faster. However, it's still pretty slow, and it's a major bottleneck in my code.
Matlab is automatically writing a 1200x900 pixel image. In reality, it's just a graph with some (horizontal) straight lines on it. I really do not need a high image quality whatsoever; maybe reducing this would help speed things up? I can't find how to do this either.
I've had a look online, in particular at other SE questions, but I haven't been able to come up with a solution. There's various stuff about "playing around with the hardcore function". I'm a mathematician who wants a code to get some intuition for the problem; I'm not a proper programmer! As such, "play around with a function" (that might cause Matlab to crash) is rather difficult for me!
I've sorted this by using the Matlab movie function. Thank you Cecilia for your assistance!

Since your intention is to use the images in a slideshow. I would suggest making a movie and using that instead. I've run a few tests, and saving a movie is much faster than saving individual pngs. I see about a 5x speed up in my simple test.
numImages = 25;
%Saving one figure at a time
tic;
for i= 1:numImages
x = 0:0.1:2*pi;
y = i*sin(x);
plot(x, y);
fig = gcf;
print('img.png', '-dpng', '-r50'); %Downsample resolution
end
toc;
tic;
%Saving a movie
v = VideoWriter('mov.avi');
open(v);
for i = 1:numImages
x = 0:0.1:2*pi;
y = i*sin(x);
plot(x, y);
drawnow; %Force the figure to render
frame = getframe; %Convert the figure to a movie frame
writeVideo(v, frame); %Write the frame to the movie file
end
close(v);
toc;
Using writeVideo also has the advantage of never storing the whole movie in memory. Instead, as each frame is captured, it is immediately written to the file. The one downside is that because the frames need to be written in the correct sequence, you can not use a parfor
You could also consider making a movie matrix. Each element would be a frame. So to initialize the size of the matrix, you would need something like
numImages = 25;
mov(numImages) = struct('cdata',[],'colormap',[]);
Then using the parfor, you can assign all the images to your mov matrix in the correct order. After your loop is finished, you can write the movie to a file using VideoWriter. However, while generating your figures, you will need to keep your whole movie in memory, and there is an overhead associated with parfor, so it may not end up being more efficient.

If you made Matlab output a file like this, called "lines.txt" with each line describing the x1,y1 and x2,y2 of a line on your output image:
10 100 600 100
50 400 800 400
100 820 350 820
Then you could use ImageMagick to draw the lines like this:
#!/bin/bash
# Create the output image, 1200x900
convert -size 1200x900 xc:gray80 result.png
# Loop, reading one line at a time from the file "lines.txt"
while read x1 y1 x2 y2; do
echo Read line $x1,$y1 $x2,$y2
# Tell ImageMagick to draw the line on the image
convert result.png -stroke blue -strokewidth 5 -draw "line $x1,$y1 $x2,$y2" result.png
done < lines.txt
Obviously, you can draw non-horizontal lines and you could output a colour after the x,y coordinates and/or a thickness...
ImageMagick is installed on most Linux distros and is available for OSX and Windows. There are more efficient ways of plotting the lines, but this may be plenty to get you started.
Another option may be to use Gnuplot, see my other answer here.

On top of Cecilia's answer which suggests using getframe to generate a movie, I'd like to add that getframe can also be used to improve speed for saving static images. Combine it with frame2im to convert the figure into a rasterized image, and then use imwrite to save the rasterized image to a file.
frame = getframe(fig); % fig is the figure handle to save
[raster, raster_map] = frame2im(frame); % raster is the rasterized image, raster_map is the colormap
if isempty(raster_map)
imwrite(raster, fig_file);
else
imwrite(raster, raster_map, fig_file); % fig_file is the path to the image
end
In my particular case the time used to save the figures is reduced from ~60s for 19 files (using saveas) down to 7~9s using this method.
Do note that this method
Does not change the background color to white as saveas does;
Does not seem to support resolution and dpi other than that of the current figure;
Does not support saving as a vector image (obviously).

Related

How to increase the size of a subplot figure in MATLAB

I am trying to create a summary figure that consists of multiple subplots. The issue is that I can not increase the overall size of the figure past the size of the screen. I am trying to create a 12 by 8 subplot so I can view all the figures from an experiment at the same time. However when I generate the subplot this is the result I receive.
Example subplot figure
And here is the code I used to generate said figure.
c = {'r' 'c' 'g' 'm' 'y' 'k'};
for x = 1:96
table = load(file_array(x).name);
table = struct2array(table);
[~,col] = size(table);
subplot_tight(12,8,x);
for t = 1:col
plot(table(:,t), c{t});
hold on
end
H = sprintf('%s\n', file_array(x).name);
title(H);
end
figname = sprintf('%s_Duration_part1',heading.name(1:end-4));
saveas(gcf, figname, 'fig');
close all %reset figure
It's nice that all the sub-figures are visible but it is essentially impossible to actually read the data from the subplots. What would be ideal is to save it so the entire figure is bigger so I can scroll through a bigger figure and look at the sub-figures. Or alternatively if there is a way to save a stack of figures as a slideshow. Ultimately so that all the data for a specific experiment is organized in one respective figure.
If you want to save the whole figure, don't look at the figure itself, but use the print function to configure the renderer.
Try something like:
print('-r1200','Plot','-dpng',)
This sets the DPI setting higher, so you should have more information displayed. You can change some other properties to play with the aspect ratio, the renderer size, etc.
Unfortunately I only have access to octave right now, so I can't really provide you with a working example.

Extract images from video, change them to grayscale and find the differences

I am writing a function which takes the following actions: extracting from video, convert the frames to grayscale and calculate the difference between first two images that i have extract.
I have done the code up to this point. But I have no idea how to extract the images that were extracted previously. Can anyone provide some guidance on this?
function [ vid ] = motion( input_args )
vid = (input_args);
readerobj = VideoReader(vid);
vidFrames = read(readerobj);
numFrames = get(readerobj, 'Number of Frames');
for k = 1 : numFrames
mov(k).cdata = vidFrames(:,:,:,k);
mov(k).colormap = [];
imagename=strcat(int2str(k), '.jpg');
%save inside output folder
imwrite(mov(k).cdata, strcat('output\frame-',imagename));
end
end
Reading your problem description, you want to do a difference of frames (in grayscale) between the previous frame and the current frame. What you can do is store the first frame as a temporary variable, then in your for loop, you would take the difference between the current frame and the previous frame. Before you iterate to the next frame, be sure to set this current frame as the previous frame, and then proceed. Obviously, you need to create the grayscale equivalent of each frame you have read in. As such, you should change your for loop to this:
prevFrame = rgb2gray(vidFrames(:,:,:,1)); %// Initialize and get grayscale
for k = 2 : numFrames %// Note we start at index 2
currFrame = rgb2gray(vidFrames(:,:,:,k)); %// Get current frame
%// and get grayscale
%// Find difference frame
diffFrame = uint8(abs(double(currFrame) - double(prevFrame)));
mov(k).cdata = cat(3,diffFrame,diffFrame,diffFrame); %// Now save to file
mov(k).colormap = [];
imagename=strcat(int2str(k), '.jpg');
%//save inside output folder
imwrite(mov(k).cdata, strcat('output\frame-',imagename));
prevFrame = currFrame; %// Save for next iteration
end
Pay special attention to how I calculated the difference frame. I casted each of the frames to double, then took the absolute difference, then recast it as uint8. The reason why is because if you don't do this, if there are any pixels that have a negative difference, MATLAB will saturate this difference to 0. For example, if one pixel was intensity 128 in one frame, then 255 in the next, the difference should be -127. We put an abs here because this is really a difference of 127. It doesn't matter which direction we are going in. However, MATLAB will consider this difference as 0 as anything that is less than 0 gets saturated to 0. As such, I need to convert both frames to double as the frames you read in from file will most likely be uint8. Once you find the absolute difference, we then recast as uint8 so we can save to file and also be able to display these images if desired.
Now the code will save the difference images to file. Note that you will be one frame short because we started at frame 2. This is necessary if you want to compare differences between consecutive frames. Note that when you are creating your frame, I had to replicate the difference frame and make it three channels to mimic a RGB frame. For a grayscale image, the RGB will have each channel to be all the same. This was done using the cat command, and I stacked the difference frame in the third dimension three times.
What's good about your mov structure now is that you can use this structure and create a video out of it using MATLAB's VideoWriter class for example. The structure is exactly formatted to be written to file. Simply loop through your stucture and write each structure element to file with the VideoWriter class. You'll then be able to produce a movie that shows the difference between consecutive frames.

Dimension of a TIF file as "420x560x3" - Matlab to IDL

I am trying to create a movie in Matlab using series of functions under VideoWriter function. My code is somewhat like one shown below:
vidObj=VideoWriter('movie.avi');
open(vidObj);
for i=1:N %N is number of frames
[nx,ny]=coordinates(Lx,Ly,Nx,Ny,[x(i),-y(i)]);
%Lx and Ly refer to the length and height in meters.
%Nx and Ny are number of pixels (boxes) and fit into the respective L's.
%If Lx=10e-6 , with Nx=5, there will be 5 pixels in x dimension,
%each of length 2e-6 m.
[xf,yf]=ndgrid(nx,ny);
zf=zeros(size(xf))+z(i);
% generate a frame here
[E,H]=nfmie(an,bn,xf,yf,zf,rad,ns,nm,lambda,tf_flag,cc_flag);
Ecc=sqrt(real(E(:,:,1)).^2+real(E(:,:,2)).^2+real(E(:,:,3)).^2+imag(E(:,:,1)).^2+imag(E(:,:,2)).^2+imag(E(:,:,3)).^2);
clf
imagesc(nx/rad,ny/rad,Ecc)
rectangle('Position',[-rad(end),-rad(end),dia(end),dia(end)],'Curvature',[1,1]);
axis image;
axis off;
currFrame=getframe(gcf);
writeVideo(vidObj,currFrame);
end
close(vidObj);
return
This generated a movie called movie.avi. However, the movie (and the tif images generated from command window) has the dimensions of "420x560x3".
edit: the tif's are generated from the movie.avi using the following code:
obj = VideoReader('movie.avi');
vid = read(obj);
frames = obj.NumberOfFrames;
for x = 1 : frames
imwrite(vid(:,:,:,x),strcat('frame-',num2str(x),'.tif'));
end
I am trying to use these images in another software, IDL, following:
read Part 1 of this link
However, when runs on IDL, it detects the dimensions as [3x420x560], and therefore generates a really weird image when I run the normalization.
How do I fix this? Is using imwrite going to help?
I was able to successfully open the .tif in IDL, but it shows that the 420x560 is actually an image including an external grey boundary.
How do I remove this boundary? I tried seeing through my functions and they were fine. (I think)
I apologize in advance for asking so many questions. I am very new to this and need help. Thank you once again
I think that the grey boundary around your image is due to the fact that when the code grabs the frame (using getframe) it is being passed the handle to the current figure (gcf) which includes the grey boundary around the image. Try instead using the handle to the current axis
currFrame=getframe(gca);
I tried this and with gca there was no grey boundary around the image when I tried the
image(currFrame.cdata);
When I retrieved the frame, the data within that corresponded to the image was reduced in dimension. I'm not sure why the code does this but an alternative to getframe is to do the following
I = imread('someImage.jpg');
h = imagesc(I);
imageScData = get(h,'CData');
frameData = im2frame(imageScData);
Naturally, you won't have the first two lines since you are building your image at each iteration of the for loop. But I did notice that size(frameData.cdata)==size(I) - so no reduction.
You also mention how IDL (which I've never used) is reading in the files incorrectly or rather that the image dimensions are detected incorrectly. How are you writing out the image to a tif file? (I didn't see any MATLAB code above, like imwrite, indicating this.)
To fix interleave problem, i.e., 3 x 420 x 560 vs 420 vs 3 x 560 vs 420 x 560 x 3, when reading in IDL, use the INTERLEAVE keyword to READ_TIFF to specify how you want the result:
IDL> im = read_tiff(filename, interleave=2)

MATLAB's 'saveas' saves my figure as squares. But I want them to be more rectangular

I am using MATLABs 'saveas' to save off one of my figures as a .png or .jpg or whatever.
So I just do:
y = randn(1,00);
plot(y); grid on;
saveas(gcf,'y','png');
Now the problem is that the png or final picture comes out as a perfect square - even if I manually stretch the figure before I use the 'saveas' command.
How do I get it so save something more rectangular?
Thanks!
Here's a short sample taken from a mathworks discussion
figure('units','pix','pos',[100 100 200 400]) % create a 200x400 image
>> imagesc(rand(10,10)) % put some random data in it
>> print(gcf,'-dbitmap','test.bmp') % save to bmp
Using print like this saves an image in the desired resolution.

MATLAB - How to avoid a jagged image?

How do I avoid a jagged image in MATLAB?
I have a 600 x 600 pixels image opened in MATLAB and do some processing on the image. However, when I save it, it looks so blurred and jagged. What should I do?
(This question is related to my previous question, MATLAB - How to plot x,y on an image and save?)
fid = fopen(datafile.txt);
A = textscan(fid,'%f%f%f'); %read data from the file
code = A{1};
xfix = A{2};
yfix = A{3};
for k=1:length(code)
imagefile=code(k);
rgb = imread([num2str(imagefile) '.jpg']);
imshow(rgb);
hold on;
x = xfix2(k);
y = yfix2(k);
plot(x,y,'-+ b'); % plot x,y on the
saveas(([num2str(imagefile) '.jpg'])) % Save the image with the same name as it open.
end
hold off
If it is just a resolution issue, perhaps using the print command (as listed below) with an explicit resolution option may fix it.
print(gcf,'-djpeg','-r600',[num2str(imagefile)])
My guess would be JPEG compression artifacts. JPEG isn't a great format for data with a lot of high frequency components. Have you tried turning the compression down? Like so:
imwrite(f.cdata,([num2str(imagefile) '.jpg']),'Quality',100);
The default for the quality parameter is only 75. That's plenty for a lot of cases, but you might need more.