Skip to content

Instantly share code, notes, and snippets.

@jdmaturen
Last active September 22, 2021 23:02
Show Gist options
  • Save jdmaturen/1e0e3c7fa69e1ccb6c21257c260ac676 to your computer and use it in GitHub Desktop.
Save jdmaturen/1e0e3c7fa69e1ccb6c21257c260ac676 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"source": [
"# Animated charts\n",
"\n",
"Often times its important to be able to see a multidimensional data over time\n",
"or another linear dimension. We can help our brains out by making a video!\n",
"\n",
"To do this we'll use a pretty contrived example to:\n",
"1. Use matplotlib to output individual frames\n",
"2. Combine the frames with ffmpeg"
],
"metadata": {}
},
{
"cell_type": "code",
"execution_count": 17,
"source": [
"# Ensure we have an output directory\n",
"! [ ! -d \"output\" ] && mkdir output"
],
"outputs": [],
"metadata": {}
},
{
"cell_type": "code",
"execution_count": 18,
"source": [
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"\n",
"\n",
"def write_img(frame: int):\n",
" fig, axis = plt.subplots(1, 1, figsize=(4, 4))\n",
" plt.suptitle(f\"Frame {frame:04d}\")\n",
" x = np.linspace(0, 2 * np.pi, 100)\n",
" y = np.sin(frame / np.pi + x)\n",
" axis.plot(x, y)\n",
" plt.savefig(f\"output/{frame:04d}.png\", dpi=400)\n",
" fig.clf()\n",
" plt.close()\n",
"\n",
"\n",
"for i in range(100):\n",
" write_img(i)\n"
],
"outputs": [],
"metadata": {}
},
{
"cell_type": "code",
"execution_count": 20,
"source": [
"!ffmpeg -y -r 60 -f image2 -s 1600x1600 -i output/%04d.png -crf 25 -pix_fmt yuv420p output/animated-chart.mp4"
],
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"ffmpeg version 4.4 Copyright (c) 2000-2021 the FFmpeg developers\n",
" built with Apple clang version 12.0.5 (clang-1205.0.22.9)\n",
" configuration: --prefix=/usr/local/Cellar/ffmpeg/4.4_2 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-avresample --enable-videotoolbox\n",
" libavutil 56. 70.100 / 56. 70.100\n",
" libavcodec 58.134.100 / 58.134.100\n",
" libavformat 58. 76.100 / 58. 76.100\n",
" libavdevice 58. 13.100 / 58. 13.100\n",
" libavfilter 7.110.100 / 7.110.100\n",
" libavresample 4. 0. 0 / 4. 0. 0\n",
" libswscale 5. 9.100 / 5. 9.100\n",
" libswresample 3. 9.100 / 3. 9.100\n",
" libpostproc 55. 9.100 / 55. 9.100\n",
"Input #0, image2, from 'output/%04d.png':\n",
" Duration: 00:00:01.67, start: 0.000000, bitrate: N/A\n",
" Stream #0:0: Video: png, rgba(pc), 1600x1600 [SAR 15748:15748 DAR 1:1], 60 fps, 60 tbr, 60 tbn, 60 tbc\n",
"Stream mapping:\n",
" Stream #0:0 -> #0:0 (png (native) -> h264 (libx264))\n",
"Press [q] to stop, [?] for help\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0musing SAR=1/1\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0musing cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mprofile High, level 5.1, 4:2:0, 8-bit\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0m264 - core 163 r3060 5db6aa6 - H.264/MPEG-4 AVC codec - Copyleft 2003-2021 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=18 lookahead_threads=3 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=25.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00\n",
"Output #0, mp4, to 'output/animated-chart.mp4':\n",
" Metadata:\n",
" encoder : Lavf58.76.100\n",
" Stream #0:0: Video: h264 (avc1 / 0x31637661), yuv420p(tv, progressive), 1600x1600 [SAR 1:1 DAR 1:1], q=2-31, 60 fps, 15360 tbn\n",
" Metadata:\n",
" encoder : Lavc58.134.100 libx264\n",
" Side data:\n",
" cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A\n",
"frame= 100 fps= 56 q=-1.0 Lsize= 295kB time=00:00:01.61 bitrate=1494.9kbits/s speed=0.899x \n",
"video:294kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.426828%\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mframe I:1 Avg QP:26.32 size: 18243\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mframe P:99 Avg QP:36.29 size: 2847\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mmb I I16..4: 33.0% 61.9% 5.1%\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mmb P I16..4: 0.2% 2.5% 0.9% P16..4: 2.3% 0.3% 0.1% 0.0% 0.0% skip:93.8%\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0m8x8 transform intra:68.0% inter:15.9%\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mcoded y,uvDC,uvAC intra: 15.2% 23.8% 23.6% inter: 0.1% 0.2% 0.0%\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mi16 v,h,dc,p: 56% 41% 3% 0%\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mi8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 14% 2% 78% 1% 0% 1% 0% 4% 0%\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mi4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 11% 5% 40% 12% 1% 4% 1% 25% 1%\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mi8c dc,h,v,p: 87% 2% 7% 4%\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mWeighted P-Frames: Y:0.0% UV:0.0%\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mref P L0: 64.2% 2.7% 29.5% 3.5%\n",
"\u001b[1;36m[libx264 @ 0x7fb3d280d400] \u001b[0mkb/s:1440.64\n"
]
}
],
"metadata": {}
}
],
"metadata": {
"orig_nbformat": 4,
"language_info": {
"name": "python",
"version": "3.9.5",
"mimetype": "text/x-python",
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"pygments_lexer": "ipython3",
"nbconvert_exporter": "python",
"file_extension": ".py"
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3.9.5 64-bit ('gauntlet-dev-KpC29WPo-py3.9': poetry)"
},
"interpreter": {
"hash": "efab027e9e3fb4f9baa78cd5d4b10a127c6a1f0828167436cb2ce385c61b8e5c"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@jdmaturen
Copy link
Author

ffmpeg can be installed on osx with homebrew.

@jdmaturen
Copy link
Author

output video:

animated-chart.mp4

@jdmaturen
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment