我闺女刚过完周岁生日,翻手机里那些照片和短视频,突然想做一个出生365天的纪念视频,网上看了一圈,发现那些“数字滚动”效果特别酷——就是那种宝宝从第1天到第365天的照片切换时,日子数字像老式计程器一样“唰唰”滚动过去,但搜到的教程不是用AE模板就是Premiere插件,对于我这种喜欢写代码的爹来说,总觉得差点意思。
后来一想,我天天用Go写后端,何不自己搞一个?其实核心就是把视频帧处理、数字动画和图片合成这三件事串起来,下面我把自己踩过的坑和最终方案掰开了说,不整那些虚的。
数字滚动到底在滚动什么?
说白了就是逐帧替换数字图片,比如视频每秒30帧,你想让数字从1滚动到365,那每一帧上显示的数字都不一样,手动P图肯定不行,得用代码逐帧生成。
我一开始想用Go的image包直接画数字,但发现字体渲染很麻烦,后来改用预先生成数字图片序列的方式:
- 每个数字(0到9)提前做成透明背景的PNG
- 在每一帧里,把当前要显示的数字拆成个位、十位、百位
- 把对应的数字图片贴到视频帧的指定位置
你看,拆解完是不是就不那么吓人了?
先造数字图片库(这一步偷懒会后悔)
// 用Go的image包生成数字0-9的图片
func generateDigitImages() {
for i := 0; i <= 9; i++ {
// 创建100x150的画布
img := image.NewRGBA(image.Rect(0, 0, 100, 150))
// 用draw.DrawMask画白色数字
// 保存为.png文件
}
}
关键点:字体文件要嵌入到程序里,不然换台电脑就崩了,我用的是golang.org/x/image/font包,搭配一个免费商用字体NotoSansSC-Bold.otf,数字图片的大小要统一,我是100像素宽、150像素高,后面合成视频时对齐就省事。
视频帧处理:Go里最折腾的部分
视频处理我用了github.com/asticode/go-astiav,这玩意儿是基于FFmpeg的Go绑定,说实话,刚开始看文档差点劝退,但撑过两天就能用了。
核心逻辑长这样:
func processFrame(day int, baseFrame *image.RGBA) *image.RGBA {
// 拷贝原始视频帧
newFrame := copyFrame(baseFrame)
// 把day拆成三位数字
hundreds := day / 100
tens := (day % 100) / 10
ones := day % 10
// 加载对应数字图片,贴到新帧的右下角
pasteDigit(newFrame, digitImages[hundreds], x, y)
pasteDigit(newFrame, digitImages[tens], x+110, y)
pasteDigit(newFrame, digitImages[ones], x+220, y)
return newFrame
}
踩坑记录:一开始我每帧都从磁盘读数字图片,结果一秒钟30帧卡成PPT,后来改成启动时把所有数字图片加载到[]*image.RGBA切片里,速度直接飞起。
让数字“滚动”起来的核心算法
光贴数字不够,“滚动感”才是灵魂,我做的是每一帧的数字都在变化,而且变化时有位移偏移。
比如第100天到第101天:
第100天:显示“100”
第100.5天(下一帧):显示“100”但数字往上漂移5像素,101”从下方挤进来
第101天:完全显示“101”
代码实现其实就一个公式:
func rollingDigit(oldDigit, newDigit int, progress float64) (img *image.RGBA, offset int) {
// progress: 0.0到1.0,表示过渡进度
offset = int(progress * digitHeight) // 偏移量
// 上半截画oldDigit的裁剪部分
// 下半截画newDigit的裁剪部分
return compositeImage, offset
}
别追求完美:我试过精确到像素的滚动对齐,结果视频出来反而显得生硬,后来故意加了一点随机抖动(±2像素),看起来反而更像老式机械计数器,不完美”才是生活。
最终输出视频的参数配置
| 参数项 | 我的设置 | 说明 |
|---|---|---|
| 分辨率 | 1920x1080 | 全高清,手机上看着清楚 |
| 帧率 | 30fps | 滚动效果流畅,文件也不太大 |
| 码率 | 8Mbps | 再高没必要,手机播放会卡 |
| 数字位置 | 右下角,距边20px | 不遮挡宝宝脸部 |
| 背景素材 | 宝宝每天一张照片 | 按日期排序,循环播放 |
那些文档里不会写的“脏活”
-
时间对齐:365天视频不是真的一秒一天,而是365张照片循环播放30秒,每张大约0.082秒,数字变化要跟照片切换同步,不然会出现“照片是第50天,数字却是60”的乌龙。
-
内存泄漏:处理1080p视频时,
image.NewRGBA频繁调用会造成内存暴涨,我用的是sync.Pool来复用帧缓冲区,GC压力小了很多。
-
跨平台字体路径:Windows和Mac的字体目录不一样,我干脆把字体文件用
//go:embed嵌入到二进制里,省得用户还要手动装字体。
跑起来之后的效果
第一次完整跑通时,我看着屏幕上数字从001滚到365,宝宝照片从出生第一天的皱巴巴脸变成周岁时满屋子爬的样子,说实话有点眼眶发热,代码写的再烂,这个视频是独一无二的。
对了,如果你只想要快速出片,也可以直接用现有工具,但用Go写一遍的好处是——以后结婚纪念日、闺蜜认识多少天,改改参数就能复用,我老婆已经预订了下一个项目:“我们恋爱1000天”视频,数字要滚到1000,好吧,又是一个周末的活。
代码没放全,因为每个项目的素材路径、字体大小、滚动速度都不一样,但核心逻辑就这些,剩下的你对着文档啃两天,肯定能跑通,要是实在卡住了,记住一点:先让数字正常显示出来,再考虑滚动动画。
本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://www.hljbesthome.com/jiankang/1027.html
评论列表(4条)
我是be365的签约作者“kyadmin”!
希望本篇文章《用Go语言实现宝宝出生365天视频的数字滚动效果,其实没那么难》能对你有所帮助!
本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名
本文概览:我闺女刚过完周岁生日,翻手机里那些照片和短视频,突然想做一个出生365天的纪念视频,网上看了一圈,发现那些“数字滚动”效果特别酷——就是...