用Golang给TA写个一周年纪念日,365天视频,我把它变成了一个命令行程序

说实话,一开始我真没打算用代码来搞什么浪漫,但当我打开相册,看到手机里存着的那365张每天随手拍的照片时——有早上匆忙咬了一口的煎饼,有...

说实话,一开始我真没打算用代码来搞什么浪漫,但当我打开相册,看到手机里存着的那365张每天随手拍的照片时——有早上匆忙咬了一口的煎饼,有深夜加班时窗外零星的灯光,有TA睡眼惺忪的样子,也有我们吵架后谁都不理谁但最后又偷偷笑出来的瞬间——我突然觉得,这些碎片不该只是躺在手机里吃灰。

于是我想,要不,用我最熟悉的方式,把这365天做成一个视频?用Go。

为什么是Go,不是剪映,不是Final Cut Pro?

因为我只会这个,哈哈,开个玩笑,其实是因为我想要的不是那种套模板的精致感,我想要的是那种带点笨拙的、真实的东西,就像我们的感情,不完美,但每帧都是真实的。

Go语言处理视频的库不多,但够用,我用的主要是 github.com/anthonynsimon/bild 来做图像处理,github.com/golang/freetype 来渲染文字,最后用 ffmpeg 的命令行工具把帧合成视频,是的,Go不是万能的,它需要借助外部工具,但这就像两个人在一起,各自发挥长处嘛。

核心思路:把每一天变成一帧

我的想法很简单:365张照片,每张照片上叠加当天的一句心情或一个日期,然后按顺序播放,每秒30帧,每张照片停留3秒,这样整个视频就是 365 × 3 = 1095秒,大约18分钟,有点长,但一周年嘛,值得。

第一步:收集和整理素材

我先写了个小脚本把手机里的照片导出来,统一命名成 day001.jpgday365.jpg,这个步骤用Go写特别直观,就是个简单的文件遍历和重命名:

func renamePhotos(srcDir, dstDir string) error {
    files, _ := os.ReadDir(srcDir)
    count := 1
    for _, f := range files {
        if strings.HasSuffix(f.Name(), ".jpg") || strings.HasSuffix(f.Name(), ".jpeg") {
            newName := fmt.Sprintf("day%03d.jpg", count)
            os.Rename(filepath.Join(srcDir, f.Name()), filepath.Join(dstDir, newName))
            count++
        }
    }
    return nil
}

注意,这里有个坑:照片顺序得按时间排,文件名前缀不能保证顺序,所以我加了个按修改时间排序的步骤,不然你看到的第5天可能其实是100天前拍的,那可就乱了。

第二步:照片预处理

手机拍的竖屏照片太多,直接放视频里会变形,所以我用 bild 库做了个简单的处理:把每张照片缩放到统一尺寸,1920×1080,然后用高斯模糊填充两侧的空白部分,形成那种“背景虚化+前景居中”的效果,虽然比不上剪映的自动美颜,但至少看起来是那么回事。

func preprocessImage(img image.Image) *image.RGBA {
    // 目标尺寸
    const width, height = 1920, 1080
    resized := bild.Resize(img, width, height, bild.Linear)
    // 这里有个小技巧:如果图片比例不对,先按比例缩放,再居中裁剪
    return resized
}

这里我得说句实话,bild 库的裁剪质量一般,如果你想要更好的效果,可以直接调 ffmpeg 的缩放滤镜,够用了。

第三步:写日期和心情

这是最花心思的部分,我准备了一个 notes.json 文件,里面记录了每天想说的话,有些是当天的备忘录,有些是我后来补的,格式大概是这样:

用Golang给TA写个一周年纪念日,365天视频,我把它变成了一个命令行程序

{
  "day001": "第一次约会,你迟到了8分钟",
  "day002": "你做的番茄炒蛋,咸了",
  ...
}

然后在每张照片上渲染这段文字,Go的 freetype 库可以用来画字,但说实话,它的字体渲染效果一般,中文还容易出乱码,我折腾了半天,最后妥协了,用 github.com/golang/freetype/truetype 加载了一个中文字体文件,把文字画在图片底部。

func addText(img *image.RGBA, text string) error {
    fontBytes, _ := os.ReadFile("fonts/simhei.ttf")
    font, _ := freetype.ParseFont(fontBytes)
    c := freetype.NewContext()
    c.SetDPI(72)
    c.SetFont(font)
    c.SetFontSize(24)
    c.SetClip(img.Bounds())
    c.SetDst(img)
    c.SetSrc(image.NewUniform(color.RGBA{255, 255, 255, 255}))
    pt := freetype.Pt(50, 1030)
    _, err := c.DrawString(text, pt)
    return err
}

第四步:合成视频

照片处理好了,文字也写好了,接下来就是用 ffmpeg 把每一帧串起来,这一步我直接在Go里调用系统命令:

func generateVideo(imageDir string) error {
    cmd := exec.Command("ffmpeg", "-framerate", "1/3", "-i", filepath.Join(imageDir, "day%03d.jpg"), "-c:v", "libx264", "-pix_fmt", "yuv420p", "anniversary.mp4")
    return cmd.Run()
}

这里 -framerate 1/3 的意思就是每张图片展示3秒,如果你觉得太慢,可以改成 1/21,每张3秒刚好够看一眼,想起那天的事情。

避坑指南:我用血泪踩出来的三个坑

文件命名一定要补零

我用 %03d 格式化,但如果你照片数量超过999张,就得改成 %04d,我一开始没注意,第366天的时候发现排序乱了,还好发现了早。

中文字体别用系统默认

Mac和Linux的默认字体都不支持中文,你得指定一个包含中文字符的TTF,我最后用了思源黑体,效果好很多。

视频编码参数

如果你不加 -pix_fmt yuv420p,很多播放器会花屏,我之前没加,手机上看全是绿色的条纹,还以为自己拍出了赛博朋克风。

最终效果:365帧,18分钟

视频做出来了,我看了三遍,第一遍是检查有没有漏掉照片,第二遍是看文字有没有错别字,第三遍是……看哭了,不是因为感动,是因为我发现自己居然记得那么多细碎的小事,比如第58天,我们吵架后你偷偷在楼下等我下班,我在照片上写了句“今天没带伞,但淋得挺开心”。

你可能会问,用Go写这个是不是太折腾了?是挺折腾的,但当你把那个18分钟的视频发给TA,TA看到第200天的时候突然笑了,说“哎,那天你做的面真的很难吃”——那一刻,你觉得写代码的辛苦都值了。

代码之外的碎碎念

这个程序总共写了大概400行Go代码,不算多,但其中光是为了处理各种图片异常,我就折腾了两个晚上,比如有的照片旋转角度不对,有的照片模糊得根本看不清,还有两张照片因为手抖拍成了重影,最后我干脆加了个简单的滤波,把模糊的照片滤掉,用替代图片顶上。

其实仔细想想,这不就像两个人的生活吗?总有不完美的部分,总有需要修修补补的地方,但正是那些不精致、不流畅、甚至有点绊脚的瞬间,才组成了真实的365天。

如果你也想给另一半做个类似的视频,别怕麻烦,Go也好,Python也好,哪怕用Excel生成一堆txt文件再手动合成,只要是你亲手做的,就比任何付费模板都更有意义,反正我这个程序,现在还在我电脑桌面上躺着,等明年纪念日,我打算改成“365天+365天=730天”的版本,希望能更新到那个版本,而不是因为写了篇教程被同事嘲笑一通然后放弃。

写到这里,我回头看了一下视频里第300天那张照片——是傍晚窗外的云,暗橙色和蓝色混在一起,那天我们刚收拾完搬家后的纸箱,你坐在阳台上,风把你的头发吹乱了,嗯,这个画面,确实值得用Go代码来标记一下。

本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://hljbesthome.com/tiyu/2469.html

(1)

文章推荐

发表回复

本站作者才能评论

评论列表(4条)

  • kyadmin
    kyadmin 2026-08-24

    我是be365的签约作者“kyadmin”!

  • kyadmin
    kyadmin 2026-08-24

    希望本篇文章《用Golang给TA写个一周年纪念日,365天视频,我把它变成了一个命令行程序》能对你有所帮助!

  • kyadmin
    kyadmin 2026-08-24

    本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名

  • kyadmin
    kyadmin 2026-08-24

    本文概览:说实话,一开始我真没打算用代码来搞什么浪漫,但当我打开相册,看到手机里存着的那365张每天随手拍的照片时——有早上匆忙咬了一口的煎饼,有...

    联系我们

    工作时间:周一至周五,9:30-18:30,节假日休息

    关注我们