提取弹幕密集视频片段:双窗口法

任务描述

给定一个视频的媒体文件和弹幕文件,你需要提取其中弹幕爆发的片段,并剪辑成一个新视频。

思路分析

这个问题太开放了。本文 YY 了一种 naive 的方法来近似完成这一功能。

采用 [2] 中的计算方法,取两个不同的窗口宽度 (Delta_1 <Delta_2)

设定一个阈值 (T)。对于每个整数时刻,如果 ( ho(t,Delta_1)>Tcdot ho(t,Delta_2)),则我们认为 (t) 是一个弹幕爆发点。截取其时间邻域,利用 [3] 中的方法剪辑输出即可。

代码实现

import math
import ffmpeg
import json
import matplotlib.pyplot as plt

import biliDanmu
import biliMedia
import myMediaEdit


def readDanmuList(bid):
    with open("outputdanmu/{bid}.danmu.json".format(bid=bid), "r", encoding="utf-8") as f:
        str_danmu = f.read()
    return json.loads(str_danmu)


def getMediaDuration(bid):
    duration = ffmpeg.probe(
        "output/{bid}.mp4".format(bid=bid))["format"]["duration"]
    return duration


def calcDanmuDensity(danmu_list, duration, Delta=10):
    lim = int(math.ceil(float(duration)))
    ans = [0]*lim
    for i in range(lim):
        for danmu in danmu_list:
            if abs(float(danmu["time"])-i) < Delta/2:
                ans[i] += 1/min(i+1, lim-i, Delta)
    return ans


if __name__ == '__main__':
    bid = "BV1ds411k74N"
    biliDanmu.saveDanmuByBid(bid)
    biliMedia.getMP4ByBid(bid)

    danmu_list = readDanmuList(bid)
    duration = getMediaDuration(bid)

    density1 = calcDanmuDensity(danmu_list, duration, Delta=15)
    density2 = calcDanmuDensity(danmu_list, duration, Delta=60)

    ratio = [(density1[i]/(density2[i]+1e-6)) for i in range(len(density1))]

    print(ratio)
    plt.plot(ratio)
    plt.show()

    T=1.2
    hot_times = [i for i in range(len(ratio)) if ratio[i]>T]

    clip_desc_list = []
    for i in hot_times:
        clip_desc_list+=[{"filename":"output/{bid}.mp4".format(bid=bid),"start":i,"duration":1}];
    
    print(clip_desc_list)
    myMediaEdit.edit(clip_desc_list,"editByDensity.mp4")

测试结果

某视频的 ratio

References

[1] Python + ffmpeg 视频剪辑 - Mollnn - 博客园 (cnblogs.com)

[2] 视频弹幕时间密度分析与作图 - Mollnn - 博客园 (cnblogs.com)

[3] Bilibili 弹幕爬取初探 - Mollnn - 博客园 (cnblogs.com)

原文地址:https://www.cnblogs.com/mollnn/p/14967400.html