粗暴方法实现---根据场景视频自动剪辑

6 篇文章 0 订阅
2 篇文章 0 订阅

简介

由于自己没事在平台上发一些视频,但是每次都感觉写文案和剪辑最费时间。文案来感觉了一下子就出来了,但是剪辑视频是真废时间啊。于是想个办法能不能先自动把视频剪短,然后根据文案直接丢到某映里对应一下就好了。要求也不高只要把场景不同的分割开就好了!(全部代码会放在最后!)

方法

开始本来想着找个现成的改一改,但是基本都是不可用的,有自动剪辑的都是商业化了不可能给你源码对吧。于是根据自己的经验想了一个比较暴力简单的方法。
下列图片可以看出,场景不同场景相似度很低下列两张图相似度只有:0.34
请添加图片描述
请添加图片描述

而场景比较连续并且相近帧数的图片相似度很高,下列图片相似度:0.85如下:
请添加图片描述
请添加图片描述
所以根据以上特征我制定了如下步骤:

  1. 逐帧切割图片进行保存:
def cut_video(video):
    cap = cv2.VideoCapture(video)
    frame_count = 0
    while True:

        dir_num = frame_count // 2000
        cut_save_dir = f"{video.replace('.', '_')}/{dir_num}"
        if not os.path.exists(cut_save_dir):
            os.makedirs(cut_save_dir)
        ret, frame = cap.read()

        if not ret:
            break

        frame_name = os.path.join(cut_save_dir, f'{frame_count}.png')

        cv2.imwrite(frame_name, frame)
        frame_count += 1
    print(frame_name)
    cap.release()
    return f"切割结束地址为:'{cut_save_dir}' \n 共{frame_count}张图片, 请检查!"
  1. 抽取每一帧图片的特征,这里我用了resnet50
#尽量用显卡,cpu太慢
def exctract_resnet50(pic_dir):
    model = resnet50(weights=ResNet50_Weights.DEFAULT)
    model.eval()
    model = torch.nn.Sequential(*list(model.children())[:-1]).cuda(0)

    to_tensor = transforms.Compose([transforms.Resize((512, 512)),
                                    # transforms.CenterCrop(224),
                                    transforms.ToTensor(),
                                    transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
                                    ])

    imgs_list = sorted(glob(f"{pic_dir}/*/*"), key=int)
    fetures, paths = [], []
    for img_item in tqdm(imgs_list):
        # print(img_item)
        img = Image.open(img_item)
        img_tensor = to_tensor(img)

        with torch.no_grad():
            res = model(img_tensor.unsqueeze(0).cuda(0))
        res = res.detach().cpu().numpy().reshape(-1)


        fetures.append(res)
        paths.append(img_item)

    df = pd.DataFrame(fetures)
    df["path"] = paths
    save_path = f"{pic_dir}/frame.csv"
    df.to_csv(save_path, index=False)
    return f"提取特征完毕:\n {save_path}"
  1. 提取特征之后就需要对比当前图片和下一张图片的相似度,这里为了好评采用余弦距离作为标准!并且筛选相似度低于0.6的地方作为切割点!
def compute_similarly(path):
    df = pd.read_csv(path)
    try:
        del df["Unnamed: 0"]
        del df["index"]
    except:
        pass
    df["rank"] = df.apply(lambda x: int(x["path"].split("/")[-1].split(".")[0]), axis=1)
    df = df.sort_values("rank", ascending=True)
    df_np = np.array(df.iloc[:, :-2])

    diff_similary = []
    cut_point = []
    for index, row in tqdm(enumerate(df_np)):
        feature_1 = row
        num = index + 1
        if num == len(df_np):
            cut_point.append(index)
            break
        feature_2 = df_np[num]
        similary = cos_similary(feature_1, feature_2)
        if similary <=0.6:
            cut_point.append(index)
        diff_similary.append(similary)

    #检测是否第一个为0
    if cut_point[0] == 0:
        pass
    else:
        cut_point.insert(0, 0)
    print(cut_point)

    save_video_dir = path.replace("frame.csv", "")

    try:
        num = merge_video(save_video_dir, cut_point)
        return f"视频分割成功,分割了{num}条!\t 地址为:{save_video_dir}/video"
    except Exception as e:
        print(e)
        return "任务失败请看后台!"
  1. 根据切割点,将图片组合成视频即可。
def merge_video(save_path, cut_points):
    # cap = cv2.VideoCapture(video)
    # fps = cap.get(cv2.CAP_PROP_FPS)
    # width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
    # height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
    fourcc = cv2.VideoWriter_fourcc(*"mp4v")



    cut_points_tuple = []
    for index, row in enumerate(cut_points):
        if index+1 == len(cut_points):
            break
        cut_points_tuple.append([row+1, cut_points[index+1]])

    total = 0
    for f1, f2 in tqdm(cut_points_tuple):
        if f2 - f1 < 30:
            continue
        video_path = f"{save_path}/video"
        if not os.path.exists(video_path):
            os.makedirs(video_path)
        frame = cv2.imread(f"{save_path}/0/0.png")
        frame_w, frame_h = frame.shape[1], frame.shape[0]
        out = cv2.VideoWriter(f"{video_path}/video_{f1}_{f2}.mp4", fourcc, 30, (frame_w, frame_h))
        for index in tqdm(range(f1, f2)):
            count = index // 2000
            frame = cv2.imread(f"{save_path}/{count}/{index}.png")
            out.write(frame)
        total += 1
    out.release()
    return total

代码说明

以上都是初版本的代码例子,为了方便更好的使用,临时用gradio布置了一个界面下载,使用方法如下:

  1. 上传图片然后点击切割,结束后会给出地址。
    在这里插入图片描述
  2. 将得到的地址,去掉最后那个数字(这里忘改了。。。),添加到下一栏:

在这里插入图片描述

  1. 将提取好的文件地址填入。
    在这里插入图片描述
    这就是我的方法,如果有好的希望可以更多的交流哈!

网上教程千千万,只有这里最全面,看完记得点个赞!!!!

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值