引言
「把图片压一压」「把视频转个格式」是每个工程师都会遇到的杂活,但真做起来却处处是坑:转出来体积更大、颜色变了、透明底变黑、视频音画不同步、上传的图片带着定位信息泄露了住址。这些坑的根因是没搞清格式的适用边界与没掌握工具的语义。本文把图像(ImageMagick)与媒体(ffmpeg)两条工具链讲透:先建立「格式选型」的判断力,再给出可直接复制的命令速查,最后落到 EXIF 隐私、批处理与响应式交付的工程实践。
前置:字节、magic bytes 与编码、有损与无损压缩原理。CDN 交付见 network 专题。
目录
- 1. 图像格式选型:位图与矢量
- 2. ImageMagick 常用命令
- 3. 图像压缩:有损与无损
- 4. ffmpeg 常用命令
- 5. 视频编码与容器
- 6. EXIF 与元数据处理
- 7. 批处理与自动化
- 8. 响应式图片与交付
- 9. 常见坑与性能
- 10. 速查表与一句话记忆
- 延伸阅读
1. 图像格式选型:位图与矢量
位图(像素网格,缩放会糊)与矢量(数学描述,任意缩放清晰)是两个世界。
| 格式 | 类型 | 透明 | 动画 | 适用 |
|---|---|---|---|---|
| JPEG | 位图有损 | 否 | 否 | 照片、复杂渐变 |
| PNG | 位图无损 | 是 | 否(APNG 可) | 图标、截图、需透明 |
| WebP | 位图有损/无损 | 是 | 是 | 通用现代替代(体积小 25~35%) |
| AVIF | 位图有损 | 是 | 是 | 更省体积,编码慢、兼容稍弱 |
| GIF | 位图无损(256 色) | 1 位 | 是 | 老动画,体积大,建议换 WebP |
| SVG | 矢量 | 是 | 可 | 图标、Logo、图表 |
选型决策:
照片/复杂图像 → JPEG(兼容)或 WebP/AVIF(省体积)
图标/Logo/图形 → SVG;需透明 → PNG 或 WebP;截图/UI → PNG 或 WebP 无损
动画 → WebP/AVIF 替代 GIF
关键判断:图像是「照片类连续色调」还是「线条/文字/纯色块」。照片类用有损(JPEG/WebP),线条/文字用无损(PNG/WebP 无损)或矢量(SVG)——用 JPEG 存截图会出现文字边缘的「振铃」噪点。
记忆:照片用有损(JPEG/WebP/AVIF)、图标用矢量(SVG)、透明用 PNG/WebP、动画弃 GIF 换 WebP——先判断内容是「连续色调」还是「线条色块」,再选格式。
2. ImageMagick 常用命令
magick(v7,v6 是 convert)是图像处理的命令行主力。
缩放与裁剪:
magick input.jpg -resize 800x output.jpg # 宽 800,高自适应
magick input.jpg -resize 800x600\> output.jpg # 内接不放大
magick input.jpg -resize 200x200^ -gravity center -extent 200x200 thumb.jpg # 方形
magick input.jpg -crop 300x200+100+50 +repage cropped.jpg # 裁剪
格式转换与质量控制:
magick input.png -quality 82 -interlace Plane output.jpg # 渐进式 JPEG
magick input.jpg -quality 80 output.webp # 转 WebP(60 可转 AVIF)
for f in *.jpg; do magick "$f" -quality 80 "${f%.jpg}.webp"; done # 批量
水印、合成与特效:
magick input.jpg -gravity southeast -pointsize 32 -fill white -annotate +20+20 "© 2026" out.jpg
magick input.jpg logo.png -gravity southeast -geometry +10+10 \
-compose dissolve -define compose:args=60 -composite out.jpg # 图片水印 60%
magick a.png b.png +append row.png # 横向拼接
magick input.png -fuzz 10% -transparent white out.png # 去白底成透明
查看信息与色彩空间:
identify -verbose input.jpg # 详细信息(尺寸/色彩/EXIF/质量)
identify -format "%wx%h %m" *.jpg # 批量看尺寸与格式
magick input.jpg -colorspace sRGB out.jpg # 统一色彩空间;-strip 去元数据
策略限制(生产安全):/etc/ImageMagick-7/policy.xml 可禁止危险格式(MVG/MSL/PDF 曾出过 RCE)、限制内存/磁盘/时间(防解压炸弹)。用不可信图片输入时务必收紧策略。
记忆:magick 五件事——resize/extent 缩放裁剪、-quality 控有损质量、-gravity+annotate/composite 打水印、+append/-append 拼接、-strip 去元数据;处理不可信图片前收紧 policy.xml 防 RCE 与解压炸弹。
3. 图像压缩:有损与无损
有损 vs 无损:有损丢弃人眼不敏感的信息(换体积),无损完整保留(可逆)。
无损:PNG(Deflate)、WebP 无损
技巧:pngquant(有损量化到 256 色,体积大降但视觉接近)、oxipng/zopfli(更优 Deflate)
有损:JPEG、WebP 有损、AVIF
技巧:先缩放再压缩(尺寸是体积的第一决定因素)
质量与体积的关系:
JPEG 质量 90 → 100:体积涨 2~3 倍,肉眼几乎无差
质量 80 → 90:涨 40%,轻微差异;60 → 80:涨 60%
→ 质量 75~85 是多数场景的甜点;先缩到目标尺寸再压
现代格式的收益(同等视觉质量下):WebP 比 JPEG 小 2535%;AVIF 比 JPEG 小 4050%(但编码慢、旧设备兼容差)→ 用 <picture> 提供 AVIF/WebP/JPEG 三级回退。
先缩放,后压缩:
# 4000px 照片要展示成 800px:直接压 4000px 是错的(传到前端再缩放,浪费带宽)
magick big.jpg -resize 800x -quality 80 small.jpg # 先缩到 800px 再压
无损优化组合拳:
pngquant --quality=65-80 in.png -o out.png && oxipng -o max --strip safe out.png
jpegoptim --strip-all --max=85 in.jpg # JPEG 无损优化(不改像素)
记忆:压缩第一原则是「先缩放再压」(尺寸比质量更影响体积)——照片用有损(质量 75
85 甜点)、截图用无损(pngquant/oxipng);WebP 省 2535%、AVIF 省 40~50%,用 picture 做三级回退。
4. ffmpeg 常用命令
ffmpeg 是媒体处理的万能工具,记住「输入 - 处理 - 输出」的结构。
格式转换与压缩:
# 转 MP4(H.264 + AAC),CRF 越小质量越高
ffmpeg -i input.mov -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k out.mp4
ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 32 -b:v 0 -c:a libopus out.webm # 转 WebM
ffmpeg -i input.mkv -c copy output.mp4 # 只换容器(秒级)
剪辑与抽帧:
ffmpeg -ss 00:01:00 -t 30 -i input.mp4 -c copy clip.mp4 # 从 1:00 截 30 秒
ffmpeg -ss 10 -i input.mp4 -frames:v 1 frame.png # 抽第 10 秒一帧
ffmpeg -i input.mp4 -vf fps=1 thumb_%03d.jpg # 每秒抽 1 帧
ffmpeg -i input.mp4 -vf "fps=1/10,scale=160:-1,tile=5x5" sprite.jpg # 雪碧图
缩放、旋转与水印:
ffmpeg -i in.mp4 -vf scale=1280:-2 out.mp4 # 缩放到宽 1280
ffmpeg -i in.mp4 -vf transpose=1 out.mp4 # 顺时针旋转 90 度
ffmpeg -i in.mp4 -i logo.png -filter_complex "overlay=W-w-10:H-h-10" out.mp4 # 水印
音视频处理与查看:
ffmpeg -i in.mp4 -vn -c:a copy out.m4a # 提取音频
ffmpeg -i in.mp4 -an out_silent.mp4 # 去掉声音
ffmpeg -f concat -safe 0 -i list.txt -c copy merged.mp4 # 拼接(同编码)
ffprobe -v error -show_format -show_streams input.mp4 # 查看媒体信息
记忆:ffmpeg 记「输入-处理-输出」——转码用 libx264 -crf、换容器用 -c copy(秒级)、剪辑用 -ss/-t、抽帧用 -frames:v 1 或 fps、缩放用 scale=1280:-2;ffprobe 看媒体信息。
5. 视频编码与容器
**容器(container)与编码(codec)**是两回事:容器是盒子(MP4/MKV/WebM),编码是里面的东西(H.264/H.265/AV1)。
| 容器 | 常见编码 | 特点 |
|---|---|---|
| MP4 | H.264/H.265/AV1 + AAC | 兼容性最好,Web/移动首选 |
| WebM | VP8/VP9/AV1 + Opus/Vorbis | 开放免专利,Web 友好 |
| MKV | 几乎任意 | 功能强,兼容性一般 |
| MOV | H.264/ProRes | 苹果生态,剪辑中间格式 |
编码器对比:
H.264(AVC) 兼容性最好,硬件支持最广 → 通用首选
H.265(HEVC) 同质量体积省 30~50%,但专利复杂、兼容一般
VP9 开放,WebM 常用,编码慢
AV1 比 H.265 再省 20~30%,编码最慢,新设备渐支持
CRF 与码率控制:
CRF(恒定质量因子):-crf 越小质量越高
H.264:18~28(23 是甜点);VP9:30~35;AV1:28~35
两遍编码(目标大小):-b:v 定码率 + -pass 1/2
恒定码率(CBR,直播):-b:v + -maxrate + -bufsize
preset:越慢压缩率越高;离线用 slow、实时用 veryfast
关键参数速记:
-c:v / -c:a 指定视频/音频编码器(copy = 不重编码)
-crf / -b:v 恒定质量 / 目标码率
-vf / -af 视频/音频滤镜;-map 选择流(多轨场景)
-pix_fmt yuv420p 兼容性最好的像素格式(Web 播放必备)
记忆:容器装编码——MP4 兼容最好、WebM 开放免专利;H.264 通用首选、H.265/AV1 更省但兼容差;CRF 控质量(H.264 用 23 左右)、preset 慢换体积、Web 播放加 -pix_fmt yuv420p。
6. EXIF 与元数据处理
元数据是双刃剑:EXIF 里有拍摄参数(对摄影有用),也有 GPS 定位、设备序列号、缩略图(对隐私有害)。
EXIF → 相机型号、光圈快门 ISO、GPS 坐标、拍摄时间、方向
IPTC/XMP → 版权、作者、关键词 / Adobe 扩展元数据(可含编辑历史)
ICC → 色彩配置文件(影响颜色呈现)
查看与清理:
exiftool photo.jpg # 查看全部元数据
exiftool -gps:all photo.jpg # 只看 GPS
exiftool -all= photo.jpg # 清理全部元数据(上传前必做)
magick photo.jpg -strip out.jpg # 等价清理
exiftool -all= -tagsfromfile @ -Orientation photo.jpg # 只保留方向
方向(Orientation)陷阱:手机拍照常写入 Orientation=6(需旋转 90°);有的查看器尊重它、有的忽略 → 图是躺着的。处理时「按方向旋转并归一化」再清掉该标签:magick input.jpg -auto-orient output.jpg。
隐私清理 SOP:
上传用户图片前:1) -strip / exiftool -all= 去全部元数据
2) 如需保留拍摄参数 → 白名单式保留(只留 ISO/光圈等)
3) 重新编码(部分缩略图藏在元数据里,strip 才彻底)
4) 检查 ICC:去掉可能致颜色变化,需按需保留
记忆:EXIF 里有隐私(GPS/序列号/内嵌缩略图)——上传前必须 -strip 或 exiftool -all= 清理;Orientation 标签会导致「躺着」的图,处理时先 -auto-orient 归一化再清标签。
7. 批处理与自动化
并行批处理:
# 用全部 CPU 核并行转 WebP
find ./images -name '*.jpg' -print0 \
| xargs -0 -P "$(nproc)" -I{} sh -c \
'magick "{}" -resize 1600x -quality 80 "${1%.jpg}.webp"' _ {}
find . -name '*.png' | parallel -j8 'oxipng -o max --strip safe {}' # GNU parallel
幂等与增量:
for f in src/*.jpg; do
out="dist/$(basename "${f%.jpg}.webp")"
[ -f "$out" ] && [ "$out" -nt "$f" ] && continue # 产物更新则跳过
magick "$f" -quality 80 "$out"
done
Makefile 驱动的媒体流水线:用「目标: 依赖」表达增量——dist/%.webp: src/%.jpg 让 make 只重建比源文件旧的产物,$(patsubst src/%.jpg,dist/%.webp,$(SRC)) 生成目标列表,magick "$<" ... "$@" 执行转换。
视频批量转码(带校验):
for f in raw/*.mov; do
ffmpeg -nostdin -i "$f" -c:v libx264 -crf 23 -preset slow \
-c:a aac -b:a 128k -pix_fmt yuv420p "out/${f%.mov}.mp4" || echo "FAIL $f"
done
自动化要点:-nostdin / </dev/null 防 ffmpeg 吞掉循环输入;错误不中断(|| continue)并记录失败清单;幂等(先判断产物是否已存在/更新);并行度按 CPU 与内存调(图像可高并行,视频编码吃满核)。
记忆:批处理靠「find + xargs -P 并行」「Makefile 做增量依赖」「-nostdin 防 ffmpeg 吞输入」;自动化三原则——幂等、错误不中断(记录失败)、并行度匹配资源。
8. 响应式图片与交付
响应式图片让浏览器按屏幕挑最合适的文件,省流量、提首屏。
<picture>
<source type="image/avif" srcset="hero-800.avif 800w, hero-1600.avif 1600w" sizes="100vw">
<source type="image/webp" srcset="hero-800.webp 800w, hero-1600.webp 1600w" sizes="100vw">
<img src="hero-800.jpg" srcset="hero-800.jpg 800w, hero-1600.jpg 1600w"
sizes="100vw" width="1600" height="900" alt="hero" loading="lazy" decoding="async">
</picture>
生成多尺寸 + 多格式(一条命令):
for w in 400 800 1600; do
magick hero.jpg -resize ${w}x -quality 82 hero-${w}.jpg
magick hero.jpg -resize ${w}x -quality 80 hero-${w}.webp
done # 三档尺寸 × 两格式
关键实践:
1. 声明 width/height → 避免布局抖动(CLS)
2. loading="lazy" + decoding="async" → 延迟加载、不阻塞渲染
3. srcset + sizes → 让浏览器选最合适的
4. 现代格式回退:AVIF → WebP → JPEG 三级
5. 占位(LQIP/blurhash):先出模糊占位再替换
CDN 与按需变换:CDN 图片服务(如 Cloudflare Images/imgix)上传原图,URL 参数按需变换:/cdn-cgi/image/width=800,format=auto/hero.jpg——无需预生成所有尺寸,缓存命中率高。视频交付用 HLS/DASH 分片 + 多码率自适应(ABR),配 poster 帧与 preload="metadata"。
记忆:响应式交付用 picture + srcset/sizes 让浏览器挑文件,AVIF→WebP→JPEG 三级回退;必写 width/height 防 CLS、lazy 延迟加载;CDN 按需变换(format=auto)免预生成;视频用 HLS/DASH 多码率。
9. 常见坑与性能
图像十大坑:
1. 用 JPEG 存截图/文字 → 边缘振铃噪点(该用 PNG/WebP)
2. 透明 PNG 转 JPEG → 透明区变黑(需先铺底色)
3. 不先缩放就压缩 → 体积没降多少(尺寸才是大头)
4. 忘记 -auto-orient → 手机图"躺着";元数据没清理 → GPS 泄露隐私
5. 生成物进 git → 仓库膨胀;色彩空间不统一 → 色差;策略过宽 → RCE/解压炸弹
视频五大坑:
1. -c copy 拼接不同编码的片段 → 音画不同步/损坏
2. 忘 -pix_fmt yuv420p → Safari/部分播放器黑屏
3. 用 -ss 在 -i 之后 → 慢(放 -i 之前是快速定位)
4. CRF 与 -b:v 同时给 → 行为不明确;帧率不匹配拼接 → 卡顿
性能要点:图像并行度高(xargs -P 核数),单图处理轻;视频编码吃满 CPU,-preset 越慢越久,可上 GPU 编码(NVENC/QSV);处理超大图/4K 视频时限制 ImageMagick 的 memory/disk 策略;按「源文件哈希 + 参数」做产物缓存避免重复计算。
统一色彩空间(避免色差):
magick input.jpg -profile /usr/share/color/icc/sRGB.icc \
-colorspace sRGB -strip -quality 82 out.jpg
记忆:图像坑集中在「格式选错(JPEG 存截图)、透明转 JPEG 变黑、不先缩放、忘 -auto-orient、元数据泄露」;视频坑集中在「copy 拼不同编码、忘 yuv420p、-ss 位置、CRF 与码率混用」;性能上图像高并行、视频吃 CPU 或上 GPU。
10. 速查表与一句话记忆
| 任务 | 命令要点 |
|---|---|
| 缩放 | magick in.jpg -resize 800x out.jpg |
| 方形缩略图 | -resize 200x200^ -gravity center -extent 200x200 |
| 转 WebP | magick in.jpg -quality 80 out.webp |
| 去元数据 | -strip 或 exiftool -all= |
| 自动转正 | magick in.jpg -auto-orient out.jpg |
| 批量转码 | find ... -print0 + xargs -0 -P n |
| 视频压缩 | ffmpeg -i in.mp4 -c:v libx264 -crf 23 -pix_fmt yuv420p out.mp4 |
| 换容器 | ffmpeg -i in.mkv -c copy out.mp4 |
| 剪辑 | ffmpeg -ss 60 -t 30 -i in.mp4 -c copy clip.mp4 |
| 抽帧 | ffmpeg -ss 10 -i in.mp4 -frames:v 1 frame.png |
| 看信息 | identify -verbose / ffprobe -show_streams |
| 响应式 | picture + srcset/sizes + 三级格式回退 |
一句话记忆:图像选型先判断内容——照片用有损(JPEG/WebP/AVIF)、图标用矢量(SVG)、透明用 PNG/WebP、动画弃 GIF;压缩第一原则是「先缩放再压」(尺寸比质量更影响体积,质量 75~85 是甜点);ImageMagick 五件事(resize/extent 缩放裁剪、-quality 控质量、-gravity 打水印、+append 拼接、-strip 去元数据),ffmpeg 记「输入-处理-输出」(libx264 -crf 转码、-c copy 换容器、-ss/-t 剪辑、-pix_fmt yuv420p 保兼容);EXIF 藏隐私上传前必清、Orientation 致图躺下要 -auto-orient;批处理靠 find+xargs -P 与 Makefile 增量;交付用 picture+srcset 三级回退并写 width/height 防 CLS——媒体处理的功夫,一半在选格式,一半在懂工具语义。
延伸阅读
- 有损/无损压缩与熵编码原理
- magic bytes 识文件与字节级观察
- 管道、xargs 与并行工作流
- 批量文件匹配与筛选
- 命令行工具的退出码与错误处理
- network 专题 — CDN 与静态资源交付
- ImageMagick 文档
- ffmpeg 官方文档
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。