如何在ffmpeg中使用混合过滤器叠加两个视频

How to overlay two videos with blend filter in ffmpeg

我需要制作大量具有以下规格的视频:

我用不同的命令做了很多测试,但总是显示错误。

好吧,我想我在下一个命令中得到了它:

ffmpeg -ss 00:00:18.300 -i music.mp3 -loop 1 -i bg.mp4 -i ac%d.png -i dust.mp4 -filter_complex "[1:0]scale=1600:ih*1200/iw, crop=1600:900[a];[a][2:0] overlay=0:0[b]; [3:0] scale=1600:ih*1600/iw, crop=1600:900,setsar=1[c]; [b][c] blend=all_mode='overlay':all_opacity=0.2" -shortest -y output.mp4

为了分享我的发现,我将进行解释:

  • 声明输入:

ffmpeg -ss 00:00:18.300 -i music.mp3 -loop 1 -i bg.mp4 -i ac%d.png -i dust.mp4

  • 添加过滤器复合体。第一部分:[1,0] 是输入的第二个元素 (bg.mp4) 和缩放以获得最大值,然后以我需要的大小裁剪,此操作的结果在 [a] 元素中.

[1:0]scale=1600:ih*1600/iw, crop=1600:900, setsar=1[a];

  • 第二部分:将 PNG 序列放在调整大小的视频(bg.mp4,现在是 [a])上并将结果保存在 [b] 元素中。

[a][2:0] 叠加=0:0[b];

  • 缩放和裁剪第四个输入 (overlay.mp4) 并保存在 [c] 元素中。

[3:0]scale=1600:ih*1600/iw,crop=1600:900,setsar=1[c];

  • 使用 "overlay" 混合模式将第一个结果与覆盖视频混合,并且不透明度为 0.1,因为视频具有灰色调并使结果很暗。

[b][c] 混合=all_mode='overlay':all_opacity=0.1

就是这样。 如果有人能解释一下这个缩放过滤器是如何工作的,我将不胜感激!

我需要处理一堆图像并且无法让 ffmpeg 可靠地为我工作,所以我构建了一个 Python 工具来帮助调解该过程:

#!/usr/bin/env python3
import functools
import numpy as np
import os
from PIL import Image, ImageChops, ImageFont, ImageDraw
import re
import sys
import multiprocessing
import time

def get_trim_box(image_name):
  im = Image.open(image_name)
  bg = Image.new(im.mode, im.size, im.getpixel((0,0)))
  diff = ImageChops.difference(im, bg)
  diff = ImageChops.add(diff, diff, 2.0, -100)
  #The bounding box is returned as a 4-tuple defining the left, upper, right, and lower pixel coordinate. If the image is completely empty, this method returns None.
  return diff.getbbox()



def rect_union(rect1, rect2):
  left1, upper1, right1, lower1 = rect1
  left2, upper2, right2, lower2 = rect2
  return (
    min(left1,left2),
    min(upper1,upper2),
    max(right1,right2),
    max(lower1,lower2)
  )



def blend_images(img1, img2, steps):
  return [Image.blend(img1, img2, alpha) for alpha in np.linspace(0,1,steps)]



def make_blend_group(options):
  print("Working on {0}+{1}".format(options["img1"], options["img2"]))
  font = ImageFont.truetype(options["font"], size=options["fontsize"])
  img1 = Image.open(options["img1"], mode='r').convert('RGB')
  img2 = Image.open(options["img2"], mode='r').convert('RGB')
  img1.crop(options["trimbox"])
  img2.crop(options["trimbox"])
  blends = blend_images(img1, img2, options["blend_steps"])
  for i,img in enumerate(blends):
    draw = ImageDraw.Draw(img)
    draw.text(options["textloc"], options["text"], fill=options["fill"], font=font)
    img.save(os.path.join(options["out_dir"],"out_{0:04}_{1:04}.png".format(options["blendnum"],i)))



if len(sys.argv)<3:
  print("Syntax: {0} <Output Directory> <Images...>".format(sys.argv[0]))
  sys.exit(-1)

out_dir     = sys.argv[1]
image_names = sys.argv[2:]

pool = multiprocessing.Pool()

image_names = sorted(image_names)
image_names.append(image_names[0]) #So we can loop the animation

#Assumes image names are alphabetic with a UNIX timestamp mixed in.
image_times = [re.sub('[^0-9]','', x) for x in image_names]
image_times = [time.strftime('%Y-%m-%d (%a) %H:%M', time.localtime(int(x))) for x in image_times]

#Crop off the edges, assuming upper left pixel is representative of background color
print("Finding trim boxes...")
trimboxes = pool.map(get_trim_box, image_names)
trimboxes = [x for x in trimboxes if x is not None]
trimbox   = functools.reduce(rect_union, trimboxes, trimboxes[0])

# #Put dates on images
testimage = Image.open(image_names[0])
font      = ImageFont.truetype('DejaVuSans.ttf', size=90)
draw      = ImageDraw.Draw(testimage)
tw, th    = draw.textsize("2019-04-04 (Thu) 00:30", font) 
tx, ty    = (50, trimbox[3]-1.1*th)   # starting position of the message

options = {
  "blend_steps": 10,
  "trimbox":     trimbox,
  "fill":        (255,255,255),
  "textloc":     (tx,ty),
  "out_dir":     out_dir,
  "font":        'DejaVuSans.ttf',
  "fontsize":    90
}

#Generate pairs of images to blend
pairs = zip(image_names, image_names[1:])
#Tuple of (Image,Image,BlendGroup,Options)
pairs = [{**options, "img1": x[0], "img2": x[1], "blendnum": i, "text": image_times[i]} for i,x in enumerate(pairs)]

#Run in parallel
pool.map(make_blend_group, pairs)

这会生成一系列图像,可以制作成这样的视频:

ffmpeg -pattern_type glob -i "/z/out_*.png" -pix_fmt yuv420p -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" -r 30 /z/out.mp4