원클릭으로
animate
Generate animations from a time sequence of PNG files using Python (for frame generation) and ffmpeg (for video encoding to .mp4/.ogg).
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Generate animations from a time sequence of PNG files using Python (for frame generation) and ffmpeg (for video encoding to .mp4/.ogg).
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
| name | animate |
| description | Generate animations from a time sequence of PNG files using Python (for frame generation) and ffmpeg (for video encoding to .mp4/.ogg). |
| user-invocable | true |
Help the user create animations from a sequence of plots/images. The workflow has two stages: (1) generate PNG frames with Python/matplotlib, then (2) encode with ffmpeg.
conda install -c conda-forge ffmpeg matplotlib xarray cartopy
import matplotlib.pyplot as plt
from pathlib import Path
out_dir = Path("frames")
out_dir.mkdir(exist_ok=True)
for i, t in enumerate(time_steps):
fig, ax = plt.subplots()
# --- plot your data here ---
ax.set_title(str(t))
fig.savefig(out_dir / f"frame_{i:04d}.png", dpi=150, bbox_inches="tight")
plt.close(fig)
Write a frames.txt concat list so each frame can have its own duration:
with open("frames.txt", "w") as f:
for p in obs_frames:
f.write(f"file '{p}'\n")
f.write(f"duration {1/12:.6f}\n") # 12 fps
for p in fcst_frames:
f.write(f"file '{p}'\n")
f.write(f"duration {1/2:.6f}\n") # 2 fps
# ffmpeg concat demuxer requires the last frame repeated
f.write(f"file '{fcst_frames[-1]}'\n")
import re, datetime as dt
pattern = re.compile(r".*_(\d{10})\.png") # e.g. field__2024060112.png
def parse_time(p):
m = pattern.match(p.name)
return dt.datetime.strptime(m.group(1), "%Y%m%d%H") if m else None
frames = sorted([p for p in Path("img_dir").glob("*.png") if parse_time(p)])
# MP4 (H.264, broadly compatible)
ffmpeg -y -f image2 -r 12 -i frames/frame_%04d.png \
-vcodec libx264 -crf 25 -pix_fmt yuv420p \
output.mp4
# Ogg Theora (open format)
ffmpeg -y -f image2 -r 12 -i frames/frame_%04d.png \
-vcodec libtheora -b:v 4096k \
output.ogg
ffmpeg -y -f concat -safe 0 -i frames.txt \
-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" \
-vsync vfr -pix_fmt yuv420p -c:v libx264 \
output.mp4
ffmpeg -y -f image2 -r 12 -i frames/frame_%04d.png \
-vf scale=1920:1080 \
-vcodec libx264 -crf 25 -pix_fmt yuv420p \
output.mp4
| Flag | Meaning |
|---|---|
-y | Overwrite output without prompting |
-f image2 | Input is an image sequence |
-f concat -safe 0 -i frames.txt | Input is a concat list (variable duration) |
-r N | Frame rate (fps) |
-c:v libx264 | H.264 codec (MP4) |
-c:v libtheora | Theora codec (OGG) |
-crf 25 | H.264 quality (0=lossless, 51=worst; 18–28 typical) |
-b:v 4096k | Bitrate (alternative to -crf) |
-pix_fmt yuv420p | Pixel format for broad compatibility |
-vf scale=W:H | Resize output |
-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" | Ensure even dimensions (required by libx264) |
-vsync vfr | Variable frame rate (use with concat list) |
#!/usr/bin/env python3
"""Generate frames and encode animation."""
import subprocess
import datetime as dt
from pathlib import Path
import matplotlib.pyplot as plt
FRAME_DIR = Path("frames")
FRAME_DIR.mkdir(exist_ok=True)
FPS = 12
# --- 1. Generate frames ---
for i, t in enumerate(time_steps):
fig, ax = plt.subplots(figsize=(10, 6))
# plot ...
fig.savefig(FRAME_DIR / f"frame_{i:04d}.png", dpi=150, bbox_inches="tight")
plt.close(fig)
# --- 2. Encode ---
subprocess.run([
"ffmpeg", "-y",
"-f", "image2", "-r", str(FPS),
"-i", str(FRAME_DIR / "frame_%04d.png"),
"-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2",
"-c:v", "libx264", "-crf", "25", "-pix_fmt", "yuv420p",
"output.mp4",
], check=True)
#!/usr/bin/env python3
"""Variable frame rate animation: fast obs, slow forecast."""
import subprocess, datetime as dt
from pathlib import Path
obs_frames = sorted(Path("obs_imgs").glob("*.png"))
fcst_frames = sorted(Path("fcst_imgs").glob("*.png"))
with open("frames.txt", "w") as f:
for p in obs_frames:
f.write(f"file '{p.resolve()}'\nduration {1/12:.6f}\n")
for p in fcst_frames:
f.write(f"file '{p.resolve()}'\nduration {1/2:.6f}\n")
f.write(f"file '{fcst_frames[-1].resolve()}'\n") # required by concat demuxer
subprocess.run([
"ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", "frames.txt",
"-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2",
"-vsync", "vfr", "-pix_fmt", "yuv420p", "-c:v", "libx264",
"output.mp4",
], check=True)
SOC 직업 분류 기준
Use when the user asks for a map, especially a Cartopy-based geospatial plot or multi-panel map. Apply the established map style: include coastlines, country borders, land, and ocean on every panel; use selective outer-panel grid labels to reduce clutter; and preserve shared colorbar and subplot layout conventions.
Calculate derived atmospheric quantities using metpy.calc — thermodynamics, soundings, kinematics, boundary layer, smoothing, and more.
Regrid NetCDF/climate data using CDO (default), NCO (ncremap), or ncatted.