179 lines
7.2 KiB
Markdown
179 lines
7.2 KiB
Markdown
# hilo-promo · 规范速查与文件结构
|
||
|
||
详细技术规范与文件组织,供阶段六合成时参考。
|
||
|
||
---
|
||
|
||
## 关键规范速查
|
||
|
||
### 字幕规范
|
||
|
||
> 📸 视觉参考:`assets/subtitle_style_ref.jpg`(实拍样式图,收录于 skill assets)
|
||
|
||
| 项目 | 规范 |
|
||
|------|------|
|
||
| **渲染方式** | PIL 生成全帧 RGBA 透明 MOV,再用 ffmpeg overlay 叠加(本环境无 `drawtext`/`ass` filter) |
|
||
| **字体** | Arial Black,全大写(`text.upper()`) |
|
||
| **颜色** | 白色填充 `(255,255,255)`,橙红色描边 `(210,70,10)`,`stroke_width=4` |
|
||
| **自适应字号** | 从 56pt 开始逐步缩小(52→48→44→40→36→32→28→24),直到文字宽度 ≤ **756px**(画面宽 70%) |
|
||
| **左右边距** | 每侧留白 15-20%;最大文字宽度 = `1080 × 0.70 = 756px` |
|
||
| **竖向位置** | 文字**底边**在 `y = 1344`(画面高度 70%,位于数字人 PiP 上方) |
|
||
| **显示方式** | **逐句单行**:一次只显示一句话,句与句之间无重叠;长句拆成短语分时段显示 |
|
||
| **时间戳来源** | 先用 `audio_transcribe_lyrics` 获取句级时间戳,再按自然语义拆成 5-7 词的短语,时长按字符数比例分配 |
|
||
| **无背景条** | 文字直接叠加在视频画面上,不加半透明底条 |
|
||
|
||
**PIL 渲染核心代码:**
|
||
```python
|
||
font, tw, fs = fit_font(text, max_w=756) # 自适应字号
|
||
x = (1080 - tw) // 2 # 水平居中
|
||
y = 1344 - fs # 底边对齐 y=1344
|
||
draw.text((x, y), text.upper(), font=font,
|
||
fill=(255,255,255,255),
|
||
stroke_width=4, stroke_fill=(210,70,10,255))
|
||
```
|
||
|
||
### 数字人 PiP 规范
|
||
|
||
> ⚠️ **`pip_clean.mov` 易过度抠像(alpha 全零)**,阶段七合成时改用 `talking_head_green.mp4` + ffmpeg `chromakey` 实时抠像,更可靠。
|
||
|
||
| 项目 | 规范 |
|
||
|------|------|
|
||
| **抠像方式** | ffmpeg `chromakey` 实时抠绿幕(阶段七合成时内联,无需预处理) |
|
||
| **绿幕颜色** | 采样视频角落像素确定实际颜色(AI 生成绿幕通常为 RGB≈56,138,61 = `0x388A3D`) |
|
||
| **⚠️ similarity** | **必须 ≤ 0.10**(深色皮肤角色 YUV 距离仅 0.25,similarity=0.15 即开始抠掉人物;切勿使用 0.2 以上) |
|
||
| **blend** | `0.02`(边缘锐利,减少半透明残边) |
|
||
| **缩放** | `scale=400:-2`(宽 400px = 画面宽 37%,高度保持比例,约 541px) |
|
||
| **位置** | `overlay=x=0:y=H-h`(左对齐,底部与视频底边齐平) |
|
||
| **起始时间** | `enable='lte(t\,{voiceover_end})'`(第 0s 出场,口播结束即消失) |
|
||
| **图层顺序** | **数字人必须在最上层**(覆盖字幕层之上) |
|
||
| **幕色选择** | 默认绿幕 `#00FF00`;角色服装含绿色调时改用蓝幕 `#0000FF` |
|
||
|
||
**阶段七 filter_complex 片段:**
|
||
```
|
||
[2:v]chromakey=0x388A3D:0.10:0.02,scale=400:-2[pip_keyed];
|
||
[v_sub][pip_keyed]overlay=x=0:y=H-h:enable='lte(t\,13.735)':format=auto[v_out]
|
||
```
|
||
|
||
**similarity 诊断方法:**
|
||
```python
|
||
# 提取单帧检查抠像结果
|
||
ffmpeg -ss 3 -i talking_head_green.mp4 -frames:v 1 -vf "chromakey=COLOR:SIM:0.02" test.png
|
||
# 用 PIL 检查 non-zero alpha 像素数
|
||
# 正常:~55-65% 像素可见(人物占画面大部分区域)
|
||
# 过度抠像:< 5% 像素可见 → 降低 similarity
|
||
```
|
||
|
||
### 教程段规范
|
||
|
||
| 项目 | 规范 |
|
||
|------|------|
|
||
| tut_002 时长 | **1-1.5s,严禁超过 2s** |
|
||
| tut_004 | **不展示静态原图**;上传动画结束即为确认状态 |
|
||
| 教程 overlay | **必须预拼接为单一 tut_combined.mp4**,禁止分段 overlay |
|
||
| 教程结束衔接 | 教程段结束后立即切回写真照片,无缝衔接 |
|
||
|
||
### 音频规范
|
||
|
||
| 项目 | 规范 |
|
||
|------|------|
|
||
| 语音收尾 | **截断而非 fade out**:尾板开始时直接截断口播,**禁止 fade out** |
|
||
| BGM 音量 | `volume=0.15`,不可盖过口播 |
|
||
| voice_id | 根据角色性别,调用声音列表工具(如 `get_voice_id`)选择合适的声音 |
|
||
| 尾板音频 | 用 `amix` 混合,保留尾板自身声音 |
|
||
| **音效保留** | 原 skill 中的所有音效必须保留(如闪光灯音效、转场音效等) |
|
||
|
||
**口播超过 6s 时的画面循环处理**(数字人视频生成上限 ~5.88s):
|
||
|
||
```bash
|
||
ffmpeg -stream_loop N -i talking_head_green.mp4 -i voiceover.mp3 \
|
||
-map 0:v -map 1:a -c:v libx264 -c:a aac -shortest -t {实际音频时长} output.mp4
|
||
```
|
||
|
||
### 图片转场规范
|
||
|
||
- 写真图之间**必须使用 xfade 转场**(`slideleft` / `slideright` / `fadeblack`)
|
||
- **禁止硬切**
|
||
|
||
---
|
||
|
||
## 文件组织总览
|
||
|
||
```
|
||
{project_name}/
|
||
├── fonts/
|
||
├── brief.md # 需求摘要
|
||
├── character.jpg # 角色形象图
|
||
├── character_prompt.txt # 生图提示词
|
||
├── digital_person/
|
||
│ ├── green_screen.jpg # 绿幕肖像(Step 1)
|
||
│ ├── talking_head_green.mp4 # official 图音视频(口型同步,Step 2)
|
||
│ ├── pip_clean.mov # 抠像后透明视频(Step 3)
|
||
│ └── talking_head_preview.mp4 # 5s demo 预览
|
||
├── photos/ # 换脸写真
|
||
│ ├── photo_01.jpg
|
||
│ ├── photo_02.jpg
|
||
│ └── photo_03.jpg
|
||
├── voiceover.mp3 # 口播语音
|
||
├── voiceover_script.txt # 口播文案
|
||
├── voiceover_subtitle.json # 字幕时间戳
|
||
├── final.mp4 # 最终视频
|
||
└── final_4x5.mp4 # 4:5 裁切版
|
||
```
|
||
|
||
> ⚠️ **合成时音频处理**:数字人视频(talking_head_green.mp4)**自带音频**,合成时直接使用该音频,不要分离音频再混合,避免口型错位。
|
||
|
||
---
|
||
|
||
## 导出剪映/CapCut
|
||
|
||
使用 clip-export skill 与 `scripts/export_to_jianying.py` 导出工程到剪映/CapCut 进行精细调整。
|
||
|
||
脚本已内置多路径自动查找 `jy_wrapper`(同级 `../clip-export/scripts/`、`~/.claude/skills/clip-export/scripts/` 等),无需手动指定路径。
|
||
|
||
### 使用方式
|
||
|
||
```bash
|
||
python scripts/export_to_jianying.py \
|
||
--project-name "my_promo" \
|
||
--photos photos/photo_01.jpg photos/photo_02.jpg photos/photo_03.jpg \
|
||
--digital-person digital_person/talking_head_green.mp4 \
|
||
--voiceover voiceover.mp3 \
|
||
--bgm bgm.mp3 \
|
||
--subtitles voiceover_subtitle.json
|
||
```
|
||
|
||
> 若找不到 `jy_wrapper`,脚本会打印安装提示;也可通过 `JY_WRAPPER_PATH` 环境变量手动指定 `clip-export/scripts/` 目录路径。
|
||
|
||
---
|
||
|
||
## 合成命令参数
|
||
|
||
### 动作模板合成
|
||
|
||
```bash
|
||
python scripts/composite.py action \
|
||
--dance ./dance_video.mp4 \
|
||
--tutorial ./tutorial_segment.mp4 \
|
||
--voiceover ./voiceover.mp3 \
|
||
--bgm ./bgm.mp3 \
|
||
--subtitle-json ./voiceover_subtitle.json \
|
||
--endcard ./assets/endcard_en.mp4 \
|
||
--output ./final.mp4
|
||
```
|
||
|
||
### 图片模板合成
|
||
|
||
```bash
|
||
python scripts/composite.py image \
|
||
--photos ./photos/photo_01.jpg ./photos/photo_02.jpg ./photos/photo_03.jpg \
|
||
--tutorial ./tut_combined.mp4 \
|
||
--pip ./digital_person/pip_clean.mov \
|
||
--voiceover ./voiceover.mp3 \
|
||
--bgm ./bgm.mp3 \
|
||
--subtitle-json ./voiceover_subtitle.json \
|
||
--endcard ./assets/endcard_en.mp4 \
|
||
--output ./final.mp4
|
||
```
|
||
|
||
> 所有路径均相对于 `{project_name}/` 项目目录。`--photos` 传入所有写真图,顺序即展示顺序;`--pip` 仅图片模板需要传入。
|