diff --git a/video-ai-system/ENTRY.hdlp b/video-ai-system/ENTRY.hdlp index 281206c..62daa60 100644 --- a/video-ai-system/ENTRY.hdlp +++ b/video-ai-system/ENTRY.hdlp @@ -177,6 +177,7 @@ video-ai-system/ | video-api-adapter.js (Seedance提交) | 自研 | ✅ (preflightCheck预校验) | | video-composer.js (FFmpeg拼接) | 自研 | ✅ (溶解过渡·faststart) | | image-api-adapter.js (Seedream生图) | 自研 | ✅ (苏白人设图已生成) | +| svg-to-png.py (SVG→PNG渲染) | 自研 | ✅ (D144·CairoSVG·解决FFmpeg无SVG解码器) | | MoviePy v2.x | MIT | ⏳ 待评估 | | WhisperX + Subaligner | MIT | ⏳ | | Edge-TTS (一期) → GPT-SoVITS MIT (二期) | MIT | ⏳ | diff --git a/video-ai-system/tools/README-SVG-TO-PNG.md b/video-ai-system/tools/README-SVG-TO-PNG.md new file mode 100644 index 0000000..de2bfd9 --- /dev/null +++ b/video-ai-system/tools/README-SVG-TO-PNG.md @@ -0,0 +1,113 @@ +# SVG to PNG 渲染工具 + +## 功能 + +将 SVG 矢量图文件渲染为 PNG 位图,解决本机缺少 SVG 渲染工具、FFmpeg 无 SVG 解码器的问题。 + +## 依赖 + +- Python 3.8+ +- CairoSVG 2.9.0+ +- Pillow 12.0.0+ +- 系统 cairo 库(macOS: `brew install cairo`) + +## 安装 + +```bash +# 1. 安装系统 cairo 库(macOS) +brew install cairo + +# 2. 安装 Python 依赖 +pip install cairosvg Pillow +``` + +## 用法 + +### 单文件转换 + +```bash +# 基本用法(自动读取 SVG 原始尺寸) +python tools/svg-to-png.py input.svg output.png + +# 指定输出宽度(高度等比缩放) +python tools/svg-to-png.py input.svg output.png --width 1920 + +# 指定输出高度(宽度等比缩放) +python tools/svg-to-png.py input.svg output.png --height 1080 + +# 指定缩放因子(基于 SVG 原始尺寸放大/缩小) +python tools/svg-to-png.py input.svg output.png --scale 2 + +# 透明背景 +python tools/svg-to-png.py input.svg output.png --background none +``` + +### 批量转换 + +```bash +# 转换目录下所有 SVG 文件 +python tools/svg-to-png.py ./svg-dir/ ./png-dir/ --batch + +# 批量转换并指定缩放因子 +python tools/svg-to-png.py ./svg-dir/ ./png-dir/ --batch --scale 2 +``` + +### 作为模块导入 + +```python +from tools.svg_to_png import convert_svg_to_png + +# 转换单个文件 +convert_svg_to_png("input.svg", "output.png", scale=2) + +# 指定宽度(高度等比) +convert_svg_to_png("input.svg", "output.png", width=1920) +``` + +## 路径 + +``` +video-ai-system/tools/svg-to-png.py +``` + +## 视频处理流水线集成 + +在视频处理流水线中,使用该工具将 SVG 字幕/特效渲染为 PNG 序列,再通过 FFmpeg 合成到视频中: + +```bash +# 1. 渲染 SVG 字幕为 PNG 序列 +python tools/svg-to-png.py ./subtitles/ ./subtitles-png/ --batch --width 1920 + +# 2. 用 FFmpeg 将 PNG 序列合成到视频 +ffmpeg -i input.mp4 -i ./subtitles-png/frame-%04d.png -filter_complex overlay output.mp4 +``` + +## 故障排查 + +### OSError: no library called "cairo-2" was found + +**原因**:Python 找不到系统 cairo 库。 + +**解决**: + +```bash +# macOS(Apple Silicon) +export DYLD_LIBRARY_PATH="/opt/homebrew/lib:$DYLD_LIBRARY_PATH" + +# macOS(Intel) +export DYLD_LIBRARY_PATH="/usr/local/lib:$DYLD_LIBRARY_PATH" + +# 然后重新运行 +python tools/svg-to-png.py input.svg output.png +``` + +### 建议在 `~/.zshrc` 中添加: + +```bash +# video-ai-system SVG 渲染工具依赖 +export DYLD_LIBRARY_PATH="/opt/homebrew/lib:$DYLD_LIBRARY_PATH" +``` + +## 更新记录 + +- 2026-06-23:初始版本,支持单文件/批量 SVG→PNG 转换 diff --git a/video-ai-system/tools/svg-to-png.py b/video-ai-system/tools/svg-to-png.py new file mode 100644 index 0000000..4936682 --- /dev/null +++ b/video-ai-system/tools/svg-to-png.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +""" +SVG to PNG Renderer +========== +将 SVG 文件渲染为 PNG 图片。 + +依赖: + pip install cairosvg Pillow + +用法: + # 基本用法(自动读取 SVG 尺寸) + python svg-to-png.py input.svg output.png + + # 指定输出宽度(高度等比缩放) + python svg-to-png.py input.svg output.png --width 1920 + + # 指定输出高度(宽度等比缩放) + python svg-to-png.py input.svg output.png --height 1080 + + # 指定缩放因子(基于 SVG 原始尺寸放大 / 缩小) + python svg-to-png.py input.svg output.png --scale 2 + + # 批量转换目录下的所有 SVG + python svg-to-png.py ./svg-dir/ ./png-dir/ --batch + + # 作为模块导入 + from svg_to_png import convert_svg_to_png + convert_svg_to_png("input.svg", "output.png", scale=2) + +路径: + video-ai-system/tools/svg-to-png.py +""" + +import argparse +import os +import sys +from pathlib import Path + +try: + import cairosvg + from PIL import Image +except ImportError as e: + print(f"[ERROR] 缺少依赖:{e}") + print("请先安装:pip install cairosvg Pillow") + sys.exit(1) + + +def convert_svg_to_png( + svg_path: str, + png_path: str, + width: int = None, + height: int = None, + scale: float = None, + background: str = "white", +) -> bool: + """ + 将单个 SVG 文件转换为 PNG。 + + :param svg_path: 输入 SVG 文件路径 + :param png_path: 输出 PNG 文件路径 + :param width: 输出宽度(px),为 None 时保留 SVG 原始宽度 + :param height: 输出高度(px),为 None 时保留 SVG 原始高度 + :param scale: 缩放因子,基于 SVG 原始尺寸等比缩放 + :param background: 背景色,默认白色;设为 None 保留透明 + :return: 是否转换成功 + """ + if not os.path.isfile(svg_path): + print(f"[ERROR] SVG 文件不存在:{svg_path}") + return False + + # 确保输出目录存在 + os.makedirs(os.path.dirname(os.path.abspath(png_path)), exist_ok=True) + + try: + # 构造 cairosvg 参数 + kwargs = { + "url": svg_path, + "write_to": png_path, + } + + if background: + kwargs["background_color"] = background + + if scale is not None: + kwargs["scale"] = scale + else: + if width is not None: + kwargs["output_width"] = width + if height is not None: + kwargs["output_height"] = height + + cairosvg.svg2png(**kwargs) + + # 验证输出文件 + if os.path.isfile(png_path): + file_size = os.path.getsize(png_path) + print(f"[OK] {svg_path} → {png_path} ({file_size // 1024} KB)") + return True + else: + print(f"[ERROR] 输出文件未生成:{png_path}") + return False + + except Exception as e: + print(f"[ERROR] 转换失败 {svg_path}:{e}") + return False + + +def batch_convert( + svg_dir: str, + png_dir: str, + width: int = None, + height: int = None, + scale: float = None, +) -> tuple: + """ + 批量转换目录下的所有 SVG 文件。 + + :param svg_dir: 输入目录(包含 SVG 文件) + :param png_dir: 输出目录 + :return: (成功数, 总数) + """ + if not os.path.isdir(svg_dir): + print(f"[ERROR] 输入目录不存在:{svg_dir}") + return 0, 0 + + os.makedirs(png_dir, exist_ok=True) + + svg_files = sorted(Path(svg_dir).glob("*.svg")) + if not svg_files: + print(f"[WARN] 目录中没有找到 SVG 文件:{svg_dir}") + return 0, 0 + + print(f"[INFO] 找到 {len(svg_files)} 个 SVG 文件,开始批量转换...") + success = 0 + total = len(svg_files) + + for svg_file in svg_files: + png_file = os.path.join(png_dir, svg_file.stem + ".png") + if convert_svg_to_png( + str(svg_file), png_file, + width=width, height=height, scale=scale + ): + success += 1 + + print(f"\n[INFO] 批量转换完成:{success}/{total} 成功") + return success, total + + +def main(): + parser = argparse.ArgumentParser( + description="SVG to PNG 渲染工具(基于 CairoSVG)", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +示例: + python svg-to-png.py input.svg output.png + python svg-to-png.py input.svg output.png --width 1920 + python svg-to-png.py ./svg/ ./png/ --batch --scale 2 +""" + ) + parser.add_argument("input", help="输入 SVG 文件或目录") + parser.add_argument("output", help="输出 PNG 文件或目录") + parser.add_argument("--width", type=int, default=None, help="输出宽度(px)") + parser.add_argument("--height", type=int, default=None, help="输出高度(px)") + parser.add_argument("--scale", type=float, default=None, help="缩放因子") + parser.add_argument("--batch", action="store_true", help="批量转换模式") + parser.add_argument("--background", default="white", help="背景色(默认 white,设为 'none' 保留透明)") + + args = parser.parse_args() + + if args.batch or os.path.isdir(args.input): + # 批量模式 + success, total = batch_convert( + args.input, args.output, + width=args.width, height=args.height, scale=args.scale + ) + sys.exit(0 if success == total else 1) + else: + # 单文件模式 + bg = None if args.background.lower() == "none" else args.background + ok = convert_svg_to_png( + args.input, args.output, + width=args.width, height=args.height, + scale=args.scale, background=bg + ) + sys.exit(0 if ok else 1) + + +if __name__ == "__main__": + main()