谈恋爱的时候,最头疼的事情之一就是纪念日,我记得去年我和对象在一起一周年的时候,我翻了半天手机相册,发现照片零零散散,根本凑不出一个像样的视频,那时候我就想,要是能有个自动化的工具,把每天的照片按时间排好,配上音乐,卡着点剪出来,那该多好。
后来我琢磨了一下,用Go语言做了一个小工具,专门用来生成365天纪念日卡点视频,今天就把这个思路分享出来,希望对你们也有点用。
为什么是Go语言而不是Python或者别的?
说实话,最开始我也想用Python写,后来发现处理视频的时候,Go的并发能力是真香,纪念日视频要处理365张甚至更多的照片,还要按秒卡点,单线程跑起来太慢了。
Go的goroutine可以同时处理图片加载、音频分析和视频编码,效率能翻好几倍,而且Go编译出来的二进制文件很小,扔到服务器上就能跑,不用装一堆依赖库。
不过我也得承认,Go的生态在多媒体处理这块确实没有Python那么丰富,像OpenCV或者MoviePy这种库,Python用起来更顺手,但咱写的是卡点视频,重点是时间控制和批量处理,Go在这两个场景下表现更好。
卡点视频的核心:时间轴对齐
纪念日卡点视频最骚的操作就是让每一张照片在音乐的重拍上切换,这里我踩过一个坑:直接按固定的间隔切换图片,结果跟音乐完全对不上。
后来我用了FFmpeg的音频波形分析功能,先提取音乐的能量峰值点,再把这些峰值点作为图片切换的时间节点,Go语言里调用FFmpeg很方便,用os/exec包执行命令就行。
package main
import (
"fmt"
"os/exec"
)
func analyzeAudioPeaks(audioFile string) ([]float64, error) {
cmd := exec.Command("ffmpeg", "-i", audioFile, "-af", "astats=metadata=1:reset=1", "-f", "null", "-")
// 这里解析输出,提取峰值时间点
// 实际代码会比这个复杂,但思路就是读取每一帧的音量数据
}
拿到峰值时间点之后,再把这些时间点分配到365张照片上,比如一首歌3分钟,180秒,有45个重拍点,那就要把365张照片按顺序分配到45个时间区间里,每个区间放8张左右,每张照片停留的时间是动态计算的。
照片处理的那些坑
我刚开始写的时候,天真地以为直接resize图片就行了,结果出来的视频,有些照片是竖屏的,有些是横屏的,比例不对,显示出来要么被拉伸变形,要么上下有黑边。
最后我用了两种处理方式:
- 裁剪加缩放:把图片裁剪成16:9的比例,然后再缩放到1920x1080。
- 模糊背景填充:保持原图比例,上下或者左右用模糊后的图片填充。
我个人更喜欢第二种,看起来更自然,Go语言里可以用golang.org/x/image库来处理图片,再加上github.com/disintegration/imaging这个库,裁剪、缩放、模糊都很方便。
import (
"github.com/disintegration/imaging"
)
func processImage(inputPath string, outputPath string) error {
img, err := imaging.Open(inputPath)
if err != nil {
return err
}
// 先缩放到合适尺寸,保持宽高比
img = imaging.Fit(img, 1920, 1080, imaging.Lanczos)
// 然后创建16:9的背景,把图片放上去
// 这个背景可以是纯色,也可以是模糊后的原图
return imaging.Save(img, outputPath)
}
365天纪念日卡点视频的生成流程
整个流程走下来大概是这样的:
第一步:准备素材,把365张照片按日期命名,比如2024-01-01.jpg、2024-01-02.jpg,这一步虽然枯燥,但很有仪式感,我有个朋友更夸张,每天用同一个角度拍一张,最后做成视频,能看到两个人一年来的变化。
第二步:选择音乐,我推荐用一些节奏感比较强的歌,流行、电子或者纯音乐都行,音乐长度最好在2到4分钟之间,太短了放不下365张照片,太长了看着累。
第三步:分析音乐波形,获取峰值时间点。
第四步:把照片分配到每个时间区间里,计算每张照片的显示时长。
第五步:生成视频帧,这一步是最耗时的,但如果用Go的并发处理,能快很多。
第六步:用FFmpeg把图片序列和音频合成最终的视频。
视频生成速度对比
我之前用Python写过一个版本,处理365张照片配3分钟的音乐,大概要跑15分钟,换成Go之后,开8个goroutine并行处理,压缩到了5分钟左右,不过也得看机器性能,我这台笔记本是M1的MacBook Pro。
| 处理环节 | Python耗时 | Go耗时 | 对比 |
|---|---|---|---|
| 图片加载与缩放 | 8分钟 | 2分30秒 | Go快了3倍多 |
| 音频分析 | 2分钟 | 1分30秒 | 稍微快一点 |
| 视频编码 | 5分钟 | 1分钟 | FFmpeg调用几乎一样 |
| 总和 | 15分钟 | 5分钟 | 总体快3倍 |
一些实用的优化技巧
写这个工具的过程中,我摸索出几个小技巧:
第一:照片编号一定要按时间顺序,我一开始用文件名排序,结果有些照片的EXIF信息是乱的,导致最后视频里照片顺序全错了,后来我直接在照片文件名里加上时间戳,比如20240101_120000.jpg,这样按文件名排序就是对的。
第二:卡点不要太死板,全部卡在重拍上,看起来反而很机械,我一般会把60%的照片卡在重拍,剩下的在弱拍或者过渡段切换,这样节奏更有呼吸感。
第三:加上文字水印,在每张照片的角落加上日期,第100天”、“半年快乐”之类的,用Go的freetype库可以渲染中文字体,但要注意字体文件得你自己准备。

第四:输出视频的编码设置,我推荐用H.265编码,同样的画质下文件体积能小一半,不过有些老设备可能播放不了,这时候可以用H.264。
代码里最绕的一个点
写代码的时候最难处理的是照片数量跟音乐时长不匹配的问题,比如音乐只有180秒,但你有365张照片,最简单的办法是每张照片显示0.49秒,但这样太快了,根本看不清。
我后来想了一个办法:把照片分成多个组,每组照片对应一小段音乐,比如前30天的照片对应前15秒音乐,中间50天的照片对应中间30秒音乐,以此类推,这样每张照片的显示时间就变多了,而且重要时段(比如100天、200天)的照片能停留更久。
这个逻辑用Go写起来不算复杂,主要是要维护一个时间分配表,我用了map[int]float64来存储每个照片索引对应的显示时长,再根据这个时长生成视频帧。
没解决的问题
说实话,这个工具到现在还有一个bug没完全修好,有些照片的EXIF旋转信息没被正确读取,导致竖屏照片在视频里是横着的,我试过用github.com/rwcarlsen/goexif/exif库来读取旋转信息,但有些手机拍的照片标签乱七八糟的,还是会有问题。
目前我的解决办法是预处理的时候手动检查一下,或者干脆一律按文件名顺序强制旋转,不过这确实是个硬伤,希望以后有更好的库能出来。
最后说两句
写这个Go语言版的纪念日卡点视频工具,其实挺折腾的,但最后做成视频发给她的时候,她那句“这怎么做到的”就值了,现在每次路过那些拍照的地方,她还会说“这个角度我们拍过”,一年365天,每一天都是证据。
如果你也想试试,不妨从最简单的开始,先别追求完美,能跑出来就行,代码写得丑没关系,卡点对不上也没事,重要的是那个心意,毕竟纪念日这种东西,形式从来都是次要的,重要的是你愿意花时间去做。
工具嘛,总会越改越好的。
本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://www.uss1.cn/tiyu/1678.html
评论列表(4条)
我是be365的签约作者“kyadmin”!
希望本篇文章《365天纪念日卡点视频,用Go语言写出属于你们的浪漫代码》能对你有所帮助!
本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名
本文概览:谈恋爱的时候,最头疼的事情之一就是纪念日,我记得去年我和对象在一起一周年的时候,我翻了半天手机相册,发现照片零零散散,根本凑不出一个像样...