{"id":3955,"date":"2019-12-20T12:12:25","date_gmt":"2019-12-20T17:12:25","guid":{"rendered":"https:\/\/blogs.mathworks.com\/steve\/?p=3955"},"modified":"2019-12-24T11:14:04","modified_gmt":"2019-12-24T16:14:04","slug":"how-to-write-animated-png-apng-files-from-matlab","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/steve\/2019\/12\/20\/how-to-write-animated-png-apng-files-from-matlab\/","title":{"rendered":"How to Write Animated PNG (APNG) Files from MATLAB"},"content":{"rendered":"<div class=\"content\"><p>Today, I'm writing about how to create animated PNG (APNG) files, such as this one:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/animated-pursuit-curves.png\" alt=\"\"> <\/p><p>I first wrote about APNG files <a href=\"https:\/\/blogs.mathworks.com\/steve\/2019\/02\/07\/animated-png-apng-files\/\">last February<\/a>. An APNG file is a valid PNG image file that uses file metadata in a clever way to compress and store the frames of a full animation. See the <a href=\"https:\/\/blogs.mathworks.com\/steve\/2019\/02\/07\/animated-png-apng-files\/\">07-Feb-2019 post<\/a> for more details about this.<\/p><p>Just recently, I created a MATLAB utility for creating APNG files. It is available on the <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/73715-animated-png-apng-writer\">File Exchange<\/a> and also on <a href=\"https:\/\/github.com\/mathworks\/apng-writer\">GitHub<\/a>. This utility makes use of the program <i>APNG Assember<\/i>, which is available under separate license terms. The utility will automatically download this program for you the first time you use it.<\/p><p>To demonstrate its use, I will use it to create an animation of some pursuit curves that I first showed in my <a href=\"https:\/\/blogs.mathworks.com\/steve\/2019\/02\/14\/pursuit-curves\/\">14-Feb-2019 blog post<\/a>. Here is the code for one version of the pursuit curves:<\/p><pre class=\"codeinput\">theta = 0:60:330;\r\nP = cosd(theta) + 1i*sind(theta);\r\nP = reshape(P,1,[]);\r\nN = length(P);\r\nd = 0.01;\r\n<span class=\"keyword\">for<\/span> k = 1:1000\r\n    V = P(end,[(2:N) 1]) - P(end,:);\r\n    P(end+1,:) = P(end,:) + d*V;\r\n<span class=\"keyword\">end<\/span>\r\nhold <span class=\"string\">on<\/span>\r\n<span class=\"keyword\">for<\/span> k = 1:N\r\n    plot(real(P(:,k)),imag(P(:,k)))\r\n<span class=\"keyword\">end<\/span>\r\nhold <span class=\"string\">off<\/span>\r\naxis <span class=\"string\">equal<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/writing_apng_files_01.png\" alt=\"\"> <p>This relatively simplistic way to compute the curves doesn't work so well for animating them, however, because the spacing of the computed points gets smaller and smaller as the points get closer to the center. The animation would appear to slow down.<\/p><p>So, I wrote a little code to resample the generated curve in approximately equal arc-length segments. Here's what that looks like:<\/p><pre class=\"codeinput\">step_distance = abs(diff(P(:,1),1));\r\ncumulative_step_distance = [0; cumsum(step_distance,1)];\r\nmax_travel_distance = cumulative_step_distance(end);\r\nnum_resampled_steps = 10000;\r\nx = linspace(0,max_travel_distance,num_resampled_steps);\r\nPr = interp1(cumulative_step_distance,P,x);\r\n<\/pre><p>And here's the code to generate one frame of the animation at a time.<\/p><pre class=\"codeinput\">num_frames = 100;\r\nq = round(linspace(1,size(Pr,1),num_frames));\r\n\r\nfig = gcf;\r\nfig.Position(3:4) = 300;\r\n\r\nframes = zeros(0,0,<span class=\"string\">'uint8'<\/span>);\r\n<span class=\"keyword\">for<\/span> k = 1:length(q)\r\n    clf\r\n    hold <span class=\"string\">on<\/span>\r\n    <span class=\"keyword\">for<\/span> v = 1:N\r\n        plot(real(Pr(1:q(k),v)),imag(Pr(1:q(k),v)))\r\n    <span class=\"keyword\">end<\/span>\r\n    hold <span class=\"string\">off<\/span>\r\n    axis(limits)\r\n    axis <span class=\"string\">equal<\/span>\r\n    axis <span class=\"string\">off<\/span>\r\n    frame = print(fig,<span class=\"string\">'-r150'<\/span>,<span class=\"string\">'-RGBImage'<\/span>);\r\n    frames = cat(4,frames,frame);\r\n<span class=\"keyword\">end<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/writing_apng_files_02.png\" alt=\"\"> <p>Here's an extra wrinkle. Although APNG is widely supported by browsers today, there are plenty of programs around that display image files and that don't know about the extra animation metadata in APNG files. I want these viewers to show the <b>last<\/b> frame of the animation because that's the frame that includes the complete curves. So I will prepend that frame to the beginning, and then I'll tell the APNG utility class to skip the first frame for animations.<\/p><pre class=\"codeinput\">frames = cat(4,frame,frames);\r\n\r\n<span class=\"comment\">% Create the APNG file.<\/span>\r\nw = animatedPNGWriter(<span class=\"string\">'animated-pursuit-curves.png'<\/span>);\r\nw.FramesPerSecond = 20;\r\nw.SkipFirstFrame = true;\r\n<span class=\"keyword\">for<\/span> k = 1:size(frames,4)\r\n    addframe(w,frames(:,:,:,k));\r\n<span class=\"keyword\">end<\/span>\r\nfinish(w);\r\n<\/pre><p>Download and give <tt>animatedPNGWriter<\/tt> a try. If you find trouble with it, feel free to report an issue on its <a href=\"https:\/\/github.com\/mathworks\/apng-writer\">GitHub repository<\/a>.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_2b931198255e48888f3463212f382890() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='2b931198255e48888f3463212f382890 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 2b931198255e48888f3463212f382890';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        copyright = 'Copyright 2019 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n\r\n        d.title = title + ' (MATLAB code)';\r\n        d.close();\r\n    }   \r\n     --> <\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_2b931198255e48888f3463212f382890()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; R2019b<br><\/p><\/div><!--\r\n2b931198255e48888f3463212f382890 ##### SOURCE BEGIN #####\r\n%%\r\n% Today, I'm writing about how to create animated PNG (APNG) files, such as\r\n% this one:\r\n%\r\n% <<https:\/\/blogs.mathworks.com\/steve\/files\/animated-pursuit-curves.png>>\r\n%\r\n% I first wrote about APNG files <https:\/\/blogs.mathworks.com\/steve\/2019\/02\/07\/animated-png-apng-files\/ \r\n% last February>. An APNG file is a valid PNG\r\n% image file that uses file metadata in a clever way to compress and store\r\n% the frames of a full animation. See the <https:\/\/blogs.mathworks.com\/steve\/2019\/02\/07\/animated-png-apng-files\/ \r\n% 07-Feb-2019 post> for more details\r\n% about this.\r\n%\r\n% Just recently, I created a MATLAB utility for creating APNG files. It is\r\n% available on the <https:\/\/www.mathworks.com\/matlabcentral\/fileexchange\/73715-animated-png-apng-writer \r\n% File Exchange> and also on <https:\/\/github.com\/mathworks\/apng-writer\r\n% GitHub>. This utility makes use of the program _APNG Assember_, which is\r\n% available under separate license terms. The utility will automatically\r\n% download this program for you the first time you use it.\r\n%\r\n% To demonstrate its use, I will use it to create an animation of some\r\n% pursuit curves that I first showed in my <https:\/\/blogs.mathworks.com\/steve\/2019\/02\/14\/pursuit-curves\/ \r\n% 14-Feb-2019 blog post>. Here is the code for one version of the pursuit\r\n% curves:\r\n\r\ntheta = 0:60:330;\r\nP = cosd(theta) + 1i*sind(theta);\r\nP = reshape(P,1,[]);\r\nN = length(P);\r\nd = 0.01;\r\nfor k = 1:1000\r\n    V = P(end,[(2:N) 1]) - P(end,:);\r\n    P(end+1,:) = P(end,:) + d*V;\r\nend\r\nhold on\r\nfor k = 1:N\r\n    plot(real(P(:,k)),imag(P(:,k)))\r\nend\r\nhold off\r\naxis equal\r\n\r\n%%\r\n% This relatively simplistic way to compute the curves doesn't work so well\r\n% for animating them, however, because the spacing of the computed points\r\n% gets smaller and smaller as the points get closer to the center. The\r\n% animation would appear to slow down.\r\n%\r\n% So, I wrote a little code to resample the generated curve in\r\n% approximately equal arc-length segments. Here's what that looks like:\r\n\r\nstep_distance = abs(diff(P(:,1),1));\r\ncumulative_step_distance = [0; cumsum(step_distance,1)];\r\nmax_travel_distance = cumulative_step_distance(end);\r\nnum_resampled_steps = 10000;\r\nx = linspace(0,max_travel_distance,num_resampled_steps);\r\nPr = interp1(cumulative_step_distance,P,x);\r\n\r\n%%\r\n% And here's the code to generate one frame of the animation at a time.\r\nnum_frames = 100;\r\nq = round(linspace(1,size(Pr,1),num_frames));\r\n\r\nfig = gcf;\r\nfig.Position(3:4) = 300;\r\n\r\nframes = zeros(0,0,'uint8');\r\nfor k = 1:length(q)\r\n    clf\r\n    hold on\r\n    for v = 1:N\r\n        plot(real(Pr(1:q(k),v)),imag(Pr(1:q(k),v)))\r\n    end\r\n    hold off\r\n    axis(limits)\r\n    axis equal\r\n    axis off\r\n    frame = print(fig,'-r150','-RGBImage');\r\n    frames = cat(4,frames,frame);\r\nend\r\n\r\n%%\r\n% Here's an extra wrinkle. Although APNG is widely supported by browsers\r\n% today, there are plenty of programs around that display image files and\r\n% that don't know about the extra animation metadata in APNG files. I want\r\n% these viewers to show the *last* frame of the animation because that's\r\n% the frame that includes the complete curves. So I will prepend that frame\r\n% to the beginning, and then I'll tell the APNG utility class to skip the\r\n% first frame for animations.\r\n\r\nframes = cat(4,frame,frames);\r\n\r\n% Create the APNG file.\r\nw = animatedPNGWriter('animated-pursuit-curves.png');\r\nw.FramesPerSecond = 20;\r\nw.SkipFirstFrame = true;\r\nfor k = 1:size(frames,4)\r\n    addframe(w,frames(:,:,:,k));\r\nend\r\nfinish(w);\r\n\r\n%%\r\n% Download and give |animatedPNGWriter| a try. If you find trouble with it,\r\n% feel free to report an issue on its <https:\/\/github.com\/mathworks\/apng-writer \r\n% GitHub repository>.\r\n##### SOURCE END ##### 2b931198255e48888f3463212f382890\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img decoding=\"async\"  class=\"img-responsive\" src=\"https:\/\/blogs.mathworks.com\/steve\/files\/writing_apng_files_02.png\" onError=\"this.style.display ='none';\" \/><\/div><p>Today, I'm writing about how to create animated PNG (APNG) files, such as this one: I first wrote about APNG files last February. An APNG file is a valid PNG image file that uses file metadata in a... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/steve\/2019\/12\/20\/how-to-write-animated-png-apng-files-from-matlab\/\">read more >><\/a><\/p>","protected":false},"author":42,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[208,50,46,178,1207,128,695,172,90,202,723,705,32,68,1273,200,170,188,1211,190,130],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/3955"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/users\/42"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/comments?post=3955"}],"version-history":[{"count":1,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/3955\/revisions"}],"predecessor-version":[{"id":3957,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/posts\/3955\/revisions\/3957"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/media?parent=3955"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/categories?post=3955"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/steve\/wp-json\/wp\/v2\/tags?post=3955"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}