☰
为什么SRT白板动画srt-whiteboard-animation画复杂线稿不跳笔?墨流聚类与密度梯度游走详解
2026/9/27 3:10:11 网站建设 项目流程

为什么SRT白板动画srt-whiteboard-animation画复杂线稿不跳笔?墨流聚类与密度梯度游走详解

【免费下载链接】srt-whiteboard-animation将 SRT 字幕做成暖米黄纸张底的流式笔迹白板手绘动画 skill:mask 分区遮罩编排 + stream 连续笔迹(ink→color)。项目地址: https://gitcode.com/gh_mirrors/sr/srt-whiteboard-animation

srt-whiteboard-animation 是一个把 SRT 字幕变成暖米黄纸张底白板手绘动画的开源 Skill:它按字幕叙事顺序分区揭示画面,并用流式笔迹让笔尖沿线稿连续滑行落墨,再逐步添彩导出 MP4。很多人问:画面里线稿又多又碎,笔尖为什么从不"瞬移"?答案藏在渲染器的两层算法里——墨流聚类(ink stream clustering)与密度梯度游走(gradient walk),本文带你逐层看懂它。

先认识这张图:渲染器要解决的问题

动画的输入就是一张手绘线稿图加一份同名标注文件(例如 examples/scene-01-monkey-mountain-banana.annotation.json),渲染核心在 scripts/stream_render.py 与 scripts/render_stream_whiteboard.py。

"跳笔"的本质是一个排序问题:图上有成百上千个含墨像素格,如果按"从上到下、从左到右"机械遍历,笔尖就会在对象之间来回穿梭,观感支离破碎。srt-whiteboard-animation 的做法是把"先画哪、后画哪"交给算法:先聚类成语义完整的"墨流",再在每条墨流内部生成一条几乎不折返的连续路径。

第一步:把线稿量化成"墨迹格"

在 scripts/stream_render.py 中,渲染器先做三件事:

  • 自适应阈值:用adaptiveThreshold把灰度图二值化,灰度低于 10 的像素判定为"墨迹",不受纸张底色影响;
  • 网格量化:整张图切成 10×10 像素的格子(grid_edge=10),只要格内有墨迹就算"活跃格";
  • 背景染色:采样原图四角颜色,把接近背景的像素替换成画布底色,保证起笔与添彩阶段背景一致。

量化之后,"画哪里"从像素问题变成了格的游走问题,这是后面所有算法的输入。

墨流聚类:主体、文字、轮廓分开画

核心入口是cluster_ink_streams(scripts/stream_render.py),它把全部活跃格聚成若干条语义完整的"墨流",流水线如下:

  1. 连通域标记:对墨迹格做 8 连通域分析(_label_components),每个连通域就是一块"笔画团";
  2. 细桥断开:地面横线、箭头这类细长笔画会把本不相干的物体连成一大块。_split_bridge_connected_component(scripts/stream_render.py)通过垂直投影找"墨迹稀疏的峡谷",在最优走廊处把弱连接切开,避免笔尖在几个物体间反复横跳;
  3. 碎片归并:1~2 格的噪点碎片会被并入质心最近的大连通域(_merge_small_components),防止"画一块没画完就跳去补一个点";
  4. 语义分类:classify_stroke_groups(scripts/stream_render.py)把每块团判定为三类——主体(最大连通域)、文字(宽高比 ≥2.2 且填充密度 ≥0.5)、轮廓(其余细线)。绘制顺序固定为主体 → 文字 → 轮廓;
  5. 视觉区域合并:边框、字和箭头哪怕分属不同连通域,只要包围盒相邻就归入同一区域(_group_adjacent_stroke_groups),保证"一个视觉单元画完再离开"。

区域之间怎么接?算法记住每条墨流的出口,下一轮总是挑"入口离出口最近"的墨流继续画;若发现从墨流终点进入更近,还会把整条路径整体反向。这一步把跨区域的跳笔压到了最短。

密度梯度游走:笔尖从墨最浓处下笔

单条墨流内部的路径由_gradient_walk生成(scripts/stream_render.py),思路非常像真人写字:

  • 起笔点:_density_seed统计每格周围 5×5 邻域内的墨迹格数量,选邻居最密的格子下笔——模拟"从墨最浓处落笔";
  • 走法:每步在 8 个未访问邻居中,挑局部密度最高、且与来向转角最小的那格,形成"贴着墨迹走、少折返"的连续轨迹;
  • 断笔兜底:若四周已无可走邻居,才跳到全局最近的未访问格继续,跳的距离天然很短,肉眼几乎不可察。

对文字区域,算法换成更贴近书写的横向按段扫描(_text_scan_order,scripts/stream_render.py):把字格按列切成若干段,段内沿墨迹最近邻游走,段间起点尽量靠近上一段的出口——观感就像一笔一划写字,而不是栅格式刷扫。

骨架模式:复杂线稿的像素级追踪

网格模式默认稳定,但对线条更精细的插画,可以用--ink-path skeleton切到骨架追踪:

  • Zhang-Suen 细化:把墨迹细化到 1 像素宽的中轴线(scripts/stream_render.py);
  • 8 邻接最直边追踪:trace_8connected(scripts/stream_render.py)从端点出发,在 T 型/十字交叉处沿"最直的未访问边"继续走,用无向边集合记账,避免交叉点碎成三角形小笔;
  • 重采样 + Chaikin 平滑:去锯齿、去毛刺后再按弧长等距取点。

骨架追踪失败时会自动回退到网格路径(scripts/render_stream_whiteboard.py),保证任何线稿都能出片。

从路径到成片:插值、抬笔与遮罩双保险

拿到有序格路径后,scripts/render_stream_whiteboard.py 的_grid_plan把相邻格中心按 2 像素间距插值成连续的笔尖坐标;遇到非相邻格切换就标记一个"抬笔点",跳过插值——视觉上只是短暂提笔,绝非瞬移。每帧再把内置手部素材 assets/drawing-hand.png 的笔尖锚点精确对齐到当前落墨点,强化"笔在画"的错觉。

编排层还有一道保险:每个区域的允许掩码= 自身矩形区域 − 所有后续区域 −protectedRegions(scripts/render_stream_whiteboard.py),落墨被严格限制在掩码内,未开始的元素不会提前露线。这样"分区遮罩编排 + 流式笔迹"各司其职:遮罩管何时能画,聚类与游走管按什么顺序画。

快速上手:3 条命令验证效果

python scripts/prepare_env.py --check # 输出 ENV_PY=<解释器路径> python scripts/parse_srt.py <字幕.srt> --target-sec 30 <ENV_PY> scripts/render_stream_whiteboard.py <图片> <标注.json> <输出.mp4> assets/drawing-hand.png --ink-path grid

线稿清晰的场景把--ink-path换成skeleton即可。分镜、区域与字幕关联可以无服务器地在浏览器预览台 assets/preview.html 中拖拽调整;完整工作流见 SKILL.md,多幕成片用 scripts/merge_scenes.py 合并。

总结:不跳笔 = 好的排序 + 短兜底

环节做什么对应实现
墨迹量化自适应阈值 + 10px 网格_active_mask
墨流聚类连通域分类 + 细桥断开 + 碎片归并classify_stroke_groups
流间串联出口→入口最近邻,可整条反向cluster_ink_streams
流内路径密度种子 + 梯度游走(文字按段扫)_gradient_walk/_text_scan_order
精细模式骨架细化 + 最直边追踪 + 平滑trace_8connected
编排保险允许掩码限制落墨范围_allowed_mask

正是这套"先聚类成语义墨流、再在流内沿密度梯度连续游走"的设计,让 srt-whiteboard-animation 面对复杂线稿时,笔尖始终贴着画面走,跳笔只剩提笔级别的短停顿。

【免费下载链接】srt-whiteboard-animation将 SRT 字幕做成暖米黄纸张底的流式笔迹白板手绘动画 skill:mask 分区遮罩编排 + stream 连续笔迹(ink→color)。项目地址: https://gitcode.com/gh_mirrors/sr/srt-whiteboard-animation

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询