Search code examples
matlabanimated-gifmovieimage-compression

Compress movies of flat plots of few points and lines in Matlab (why are they so heavy?)


Consider a sequence of points:

N = 8 * 360; t = (0:9:N) + N / 4;
x = sqrt(t) .* cos(t * pi / 180);
y = sqrt(t) .* sin(t * pi / 180);

Then visualize the path, get the frames and make a movie:

f = figure('Position', [300 75 900 600]);
hold on
h_plot1 = plot(x(2:end), y(2:end), 'ob', 'MarkerEdgeColor', 'b', 'MarkerFaceColor', 'b', 'MarkerSize', 5);
h_plot2 = plot(x(1), y(1), 'o-r', 'MarkerEdgeColor', 'b', 'MarkerFaceColor', 'b', 'MarkerSize', 5, 'LineWidth', 3);
x_min = min(x); x_max = max(x); y_min = min(y); y_max = max(y);
x_w = x_max - x_min; y_w = y_max - y_min;
axis([x_min - x_w / 10 x_max + x_w / 10 y_min - y_w / 10 y_max + y_w / 10])
axis equal, axis off
set(f, 'Color', [1 1 .25])
set(gca, 'Color', [1 1 .25])
set(gca,'nextplot','replacechildren');
vid = VideoWriter('myveryheavymovie.avi');
vid.Quality = 100;
vid.FrameRate = 15;
open(vid);
for hh = 1:length(x)-1
  hold on
  set(h_plot1, 'XData', x((hh + 1):end), 'YData', y((hh + 1):end))
  set(h_plot2, 'XData', x(1:(hh + 1)), 'YData', y(1:(hh + 1)))
  tmp = plot(x(hh:hh+1), y(hh:hh+1), 'or', 'MarkerSize', 8, 'MarkerFaceColor', 'r');
  writeVideo(vid, getframe(f));
  delete(tmp)
  writeVideo(vid, getframe(f));
end
close(vid);

The movie is 111,840 KB, unreasonably heavy - is there a way to compress the movie? The images are just a handful of points and lines: can the movie be compressed to - say - less than 1,000 KB?

EDIT following answers from A. Donda, chappjc and horchler

Apparently I cannot change profile and for the moment I haven't tried third party software. I have tried generating a gif animation with imwrite:

% Consider a sequence of points:
N = 8 * 360; t = (0:9:N) + N / 4;
x = sqrt(t) .* cos(t * pi / 180);
y = sqrt(t) .* sin(t * pi / 180);
x_min = min(x); x_max = max(x); y_min = min(y); y_max = max(y);
x_w = x_max - x_min; y_w = y_max - y_min;

% Create figure:
f = figure('Position', [300 75 900 600]);
set(f, 'Color', [1 1 .25])
h_plot1 = plot(x(2:end), y(2:end), 'ob', 'MarkerEdgeColor', 'b', 'MarkerFaceColor', 'b', 'MarkerSize', 3);
hold on
h_plot2 = plot(x(1), y(1), 'o-r', 'MarkerEdgeColor', 'b', 'MarkerFaceColor', 'b', 'MarkerSize', 3, 'LineWidth', 3);
axis([x_min - x_w / 10 x_max + x_w / 10 y_min - y_w / 10 y_max + y_w / 10])
axis equal, axis off
set(gca, 'nextplot','replacechildren', 'Visible','off');

% preallocate mov fo gif animation
nFrames = length(x)-1;
frame = getframe(gca);
[frame, map] = rgb2ind(frame.cdata, 256, 'nodither');
map = [map; 1 0 0];
mov = repmat(frame, [1 1 1 2*nFrames]);

% Visualize it, get the frames and save gif animation
for hh = 1:nFrames
  set(h_plot1, 'XData', x((hh + 1):end), 'YData', y((hh + 1):end))
  set(h_plot2, 'XData', x(1:(hh + 1)), 'YData', y(1:(hh + 1)))
  hold on
  tmp = plot(x(hh:hh+1), y(hh:hh+1), 'or', 'MarkerSize', 8, 'MarkerEdgeColor', 'r', 'MarkerFaceColor', 'r');
  frame = getframe(gca);
  mov(:, :, 1, 2*hh-1) = rgb2ind(frame.cdata, map, 'nodither');
  delete(tmp)
  frame = getframe(gca);
  mov(:, :, 1, 2*hh) = rgb2ind(frame.cdata, map, 'nodither');
end
close(gcf)
imwrite(mov, map, 'mynotsoheavygif.gif', 'DelayTime', 0, 'LoopCount', inf)

The gif file is 4,420 KB - which is not good and not bad but better than the avi file.


Solution

  • If you can switch to the 'MPEG-4' profile, that will help a lot. Make sure that you specify the 'Quality' as well - the default is 75 out of 100.

    One reason that the movies can be large is that the available codecs aren't necessarily the most suitable for typical Matlab content. It looks like you're writing a video of a fairly basic plot window: flat content with areas of constant color and likely little use of gradients and antialiasing. If QuickTime is acceptable to you and you're interested in using a third-party function, you could try my QTWriter. Notably, it supports a QuickTime 'Photo PNG' codec that may compress your content more efficiently than the MPEG-4 codec (or very close) while being lossless and supporting transparency. QTWriter is modeled closely after Matlab's VideoWriter class so switching over should be easy. It's a single M-File and has several other unique and useful features. You can view example code and videos on the project website.

    Additionally, if you have a Mac with a Retina display, I think that getframe has a bug that leads to blurry images and video. Depending on the codec, this could make compression better or worse. Regardless, it looks bad. You can try getframebg if this is an issue for you.