Search code examples
matlabmemorymoviememory-efficient

Making a movie in MATLAB takes too much RAM and hard disk space... what movie writer should I use?


I am faced with making movies with ~10,000 frames and ~ 10,000 pixels per frame using e.g. imagesc. However, even though my computer is equipped with 8 GB of RAM (running 32-bit MATLAB), MATLAB complains with Out Of Memory at ~ 1,000 frames. Also, I want my movie compressed as well. What is my best option to create larger movies?


Solution

  • To find out what is your best option, I tested several movie writing functions (Windows 7 MATLAB 2012a 32-bit).

    Too long didn't read

    The key is to make sure the function flushes frames to disk before the movie is completed. VideoWriter combined with MPEG-4 yields both constant (small) memory size and a compressed video.

    The Code

    %%%
    % General Setup
    %%%
    number_of_frames = 1000;
    Z = peaks; 
    
    %%%
    % Video Writer -- AVI
    %%%
    %%
    f = figure('renderer', 'zbuffer');
    a = axes('parent', f);
    axis(a, 'tight');
    surf(a, Z);
    current_zlim = zlim();
    set(a, 'nextplot', 'replacechildren', 'zlimmode', 'manual', 'zlim', current_zlim);
    vid = VideoWriter('myPeaks - VideoWriter.avi');
    vid.Quality = 100;
    vid.FrameRate = 15;
    open(vid);
    current_memory_used_Bytes(1, number_of_frames + 1) = 0;
    current_memory_struct = memory();
    current_memory_used_Bytes(1) = current_memory_struct.MemUsedMATLAB;
    for(k = 1 : number_of_frames)
        surf(a, sin(2*pi*k/20)*Z, Z);
        writeVideo(vid, getframe(f));
        current_memory_struct = memory();
        current_memory_used_Bytes(k + 1) = current_memory_struct.MemUsedMATLAB;
    end
    close(vid);
    figure;
    plot((current_memory_used_Bytes(2 : end) - current_memory_used_Bytes(1)) / 1e6);
    xlabel('Frame Number');
    ylabel('Movie Memory in RAM (MB)');
    title('VideoWriter -- AVI');
    saveas(gcf, 'myPeaks - VideoWriter -- AVI.png');
    close all;
    clearvars -except number_of_frames Z
    
    %%%
    % Video Writer -- MPEG-4
    %%%
    %%
    f = figure('renderer', 'zbuffer');
    a = axes('parent', f);
    axis(a, 'tight');
    surf(a, Z);
    current_zlim = zlim();
    set(a, 'nextplot', 'replacechildren', 'zlimmode', 'manual', 'zlim', current_zlim);
    vid = VideoWriter('myPeaks - VideoWriter.mp4', 'MPEG-4');
    vid.Quality = 100;
    vid.FrameRate = 15;
    open(vid);
    current_memory_used_Bytes(1, number_of_frames + 1) = 0;
    current_memory_struct = memory();
    current_memory_used_Bytes(1) = current_memory_struct.MemUsedMATLAB;
    for(k = 1 : number_of_frames)
        surf(a, sin(2*pi*k/20)*Z, Z);
        writeVideo(vid, getframe(f));
        current_memory_struct = memory();
        current_memory_used_Bytes(k + 1) = current_memory_struct.MemUsedMATLAB;
    end
    close(vid);
    figure;
    plot((current_memory_used_Bytes(2 : end) - current_memory_used_Bytes(1)) / 1e6);
    xlabel('Frame Number');
    ylabel('Movie Memory in RAM (MB)');
    title('VideoWriter -- MPEG-4');
    saveas(gcf, 'myPeaks - VideoWriter -- MPEG-4.png');
    close all;
    clearvars -except number_of_frames Z
    
    %%%
    % Movie2AVI
    %%%
    %%
    f = figure('renderer', 'zbuffer');
    a = axes('parent', f);
    axis(a, 'tight');
    surf(a, Z);
    current_zlim = zlim();
    set(a, 'nextplot', 'replacechildren', 'zlimmode', 'manual', 'zlim', current_zlim);
    movie_frames(1 : number_of_frames) = struct('cdata', [], 'colormap', []);
    current_memory_used_Bytes(1, number_of_frames + 1) = 0;
    current_memory_struct = memory();
    current_memory_used_Bytes(1) = current_memory_struct.MemUsedMATLAB;
    for(k = 1 : number_of_frames)
        surf(a, sin(2*pi*k/20)*Z, Z);
        movie_frames(k) = getframe(f);
        current_memory_struct = memory();
        current_memory_used_Bytes(k + 1) = current_memory_struct.MemUsedMATLAB;
    end
    movie2avi(movie_frames, 'myPeaks - Movie2AVI.avi', 'compression', 'none', 'fps', 10);
    figure;
    plot((current_memory_used_Bytes(2 : end) - current_memory_used_Bytes(1)) / 1e6);
    xlabel('Frame Number');
    ylabel('Movie Memory in RAM (MB)');
    title('Movie2AVI -- AVI');
    saveas(gcf, 'myPeaks - Movie2AVI -- AVI.png');
    close all;
    clearvars -except number_of_frames Z
    
    %%%
    % IMWrite
    %%%
    %%
    f = figure('renderer', 'zbuffer');
    a = axes('parent', f);
    axis(a, 'tight');
    surf(a, Z);
    current_zlim = zlim();
    set(a, 'nextplot', 'replacechildren', 'zlimmode', 'manual', 'zlim', current_zlim);
    current_frame = getframe(a);
    [frame_indices, cmap] = rgb2ind(current_frame.cdata, 256, 'nodither');
    movie_frames = repmat(frame_indices, [1 1 1 number_of_frames]);
    current_memory_used_Bytes(1, number_of_frames + 1) = 0;
    current_memory_struct = memory();
    current_memory_used_Bytes(1) = current_memory_struct.MemUsedMATLAB;
    for(k = 1 : number_of_frames)
        surf(sin(2*pi*k/20)*Z, Z)
        f = getframe(a);
        movie_frames(:, :, 1, k) = rgb2ind(f.cdata, cmap, 'nodither');
        current_memory_struct = memory();
        current_memory_used_Bytes(k + 1) = current_memory_struct.MemUsedMATLAB;
    end
    imwrite(movie_frames, cmap, 'myPeaks - IMWrite.gif', 'DelayTime', 0, 'LoopCount', inf);
    figure;
    plot((current_memory_used_Bytes(2 : end) - current_memory_used_Bytes(1)) / 1e6);
    xlabel('Frame Number');
    ylabel('Movie Memory in RAM (MB)');
    title('Movie2AVI -- AVI');
    saveas(gcf, 'myPeaks - IMWrite.png');
    close all;
    clearvars -except number_of_frames Z
    
    %%%
    % MPGWrite
    %%%
    %%
    f = figure('renderer', 'zbuffer');
    a = axes('parent', f);
    axis(a, 'tight');
    surf(a, Z);
    current_zlim = zlim();
    set(a, 'nextplot', 'replacechildren', 'zlimmode', 'manual', 'zlim', current_zlim);
    winsize = get(f, 'Position');
    winsize(1:2) = [0 0];
    movie_frames = moviein(number_of_frames, f, winsize);
    current_memory_used_Bytes(1, number_of_frames + 1) = 0;
    current_memory_struct = memory();
    current_memory_used_Bytes(1) = current_memory_struct.MemUsedMATLAB;
    for(k = 1 : number_of_frames)
        surf(sin(2*pi*k/20)*Z, Z)
        drawnow;
        movie_frames(:, k) = im2frame(hardcopy(f, '-Dzbuffer', '-r0')); 
        current_memory_struct = memory();
        current_memory_used_Bytes(k + 1) = current_memory_struct.MemUsedMATLAB;
    end
    mpgwrite(movie_frames, jet, 'myPeaks - MPGWrite.mpg');
    figure;
    plot((current_memory_used_Bytes(2 : end) - current_memory_used_Bytes(1)) / 1e6);
    xlabel('Frame Number');
    ylabel('Movie Memory in RAM (MB)');
    title('MPGWrite -- MPG');
    saveas(gcf, 'myPeaks - MPGWrite.png');
    close all;
    clearvars -except number_of_frames Z
    

    The Results

    Memory vs. Frame Number

    As we can see, VideoWriter, IMWrite and Movie2AVI have nearly constant memory requirements, while the memory necessary for AVI2File and MPGWrite scales with the number of frames. This makes sense as the latter require holding every frame before writing the movie, while the former appear to flush to disk regularly.

    Movie Size

    VideoWriter -- AVI: 154,529 KB

    VideoWriter -- MPEG-4: 19,355 KB

    IMWrite: 13,385 KB

    Movie2AVI -- AVI: 992,276 KB

    MPGWrite: 24,301 KB

    MPEG-4 yields a much more efficient compression than AVI, as expected. Interestingly, VideoWriter and IMWrite both outperform MPGWrite on this test (though I give credit to MPGWrite for coming many years before VideoWriter!).