从视频中提取为ppt或图片

从视频中提取为ppt或图片

    • 缘由
    • 简介
    • 使用
    • 安装与运行
    • 改进-批量

缘由

在观看视频或会议讲座时,有时觉得演讲者的ppt内容做的不错,想要去下载视频中主持人的ppt演示幻灯片,但由于某些原因,无法下载,有时只能去录制视频去截图某一帧然后最后合成pdf保存。
下面的软件可以帮助从视频中使用算法识别自动提取其中的ppt

简介

这个软件是video2pdfslides, 它的主要作用就是从一个视频里提取有效图片, 去重后并转为 PDF。
官方地址

使用

这是一款开源代码,不属于模块,所以无法通过pip直接安装调用。

安装与运行

安装:

git clone https://github.com/kaushikj/video2pdfslides

cd video2pdfslides

pip install -r requirements.txt

使用命令行运行

python video2pdfslides.py video/test.mp4

python video2pdfslides.py "./input/Test Video 1.mp4"

改进-批量

官方提供代码每次只能转换一个文件(也可能可以批量,但是我没发现调用方法),所以我们可以对其进行改进。

import os
import pathlib
import time
import cv2
import imutils
import shutil
import img2pdf
import glob
import argparse

############# 定义常量

OUTPUT_SLIDES_DIR = f"./output"  # 默认输出文件夹

FRAME_RATE = 3  # 帧率:每秒需要处理的帧数越少,速度越快
WARMUP = FRAME_RATE  # 被跳过的初始帧数
FGBG_HISTORY = FRAME_RATE * 15  # 背景对象中的帧数
VAR_THRESHOLD = 16  # 方差阈值,用于判断当前像素是前景还是背景。一般默认为 16,如果光照变化明显,如阳光下的水面,建议设为 25,值越大灵敏度越低
DETECT_SHADOWS = False  # 是否检测影子,设为 True 为检测,False 为不检测,检测影子会增加程序时间复杂度,一般设置为 False
MIN_PERCENT = 0.1  # 在前景和背景之间的最小差值百分比,以检测运动是否已经停止
MAX_PERCENT = 3  # 在前景和背景之间的最大百分比的差异,以检测帧是否仍在运动


def get_frames(video_path):
    '''从位于 video_path 的视频返回帧的函数
    此函数跳过 FRAME_RATE 中定义的帧'''

    # 打开指向视频文件的指针初始化帧的宽度和高度
    vs = cv2.VideoCapture(video_path)
    if not vs.isOpened():
        raise Exception(f'unable to open file {video_path}')

    total_frames = vs.get(cv2.CAP_PROP_FRAME_COUNT)
    frame_time = 0
    frame_count = 0
    print("total_frames: ", total_frames)
    print("FRAME_RATE", FRAME_RATE)

    # 循环播放视频的帧
    while True:
        # 从视频中抓取一帧

        vs.set(cv2.CAP_PROP_POS_MSEC, frame_time * 1000)  # 将帧移动到时间戳
        frame_time += 1 / FRAME_RATE

        (_, frame) = vs.read()
        # 如果帧为None,那么我们已经到了视频文件的末尾
        if frame is None:
            break

        frame_count += 1
        yield frame_count, frame_time, frame

    vs.release()


def detect_unique_screenshots(video_path, output_folder_screenshot_path):
    ''''''
    # 用参数初始化 fgbg 一个背景对象
    # history = 影响背景减法器的帧历史数
    # varThreshold = 像素与模型之间的平方马氏距离的阈值,以决定像素是否被背景模型很好地描述。该参数不影响后台更新。
    # detectShadows = 如果为真,算法将检测阴影并标记它们。它会稍微降低速度,因此如果您不需要此功能,请将参数设置为 false。

    fgbg = cv2.createBackgroundSubtractorMOG2(history=FGBG_HISTORY, varThreshold=VAR_THRESHOLD,
                                              detectShadows=DETECT_SHADOWS)

    captured = False
    start_time = time.time()
    (W, H) = (None, None)

    screenshoots_count = 0
    for frame_count, frame_time, frame in get_frames(video_path):
        orig = frame.copy()  # clone the original frame (so we can save it later),
        frame = imutils.resize(frame, width=600)  # resize the frame
        mask = fgbg.apply(frame)  # apply the background subtractor

        # apply a series of erosions and dilations to eliminate noise
        #            eroded_mask = cv2.erode(mask, None, iterations=2)
        #            mask = cv2.dilate(mask, None, iterations=2)

        # if the width and height are empty, grab the spatial dimensions
        if W is None or H is None:
            (H, W) = mask.shape[:2]

        # compute the percentage of the mask that is "foreground"
        p_diff = (cv2.countNonZero(mask) / float(W * H)) * 100

        # if p_diff less than N% then motion has stopped, thus capture the frame

        if p_diff < MIN_PERCENT and not captured and frame_count > WARMUP:
            captured = True
            filename = f"{screenshoots_count:03}_{round(frame_time / 60, 2)}.png"

            path = str(pathlib.Path(output_folder_screenshot_path, filename))

            print("saving {}".format(path))
            cv2.imencode('.png', orig)[1].tofile(path)  # 防止imwrite中文乱码
            screenshoots_count += 1

        # otherwise, either the scene is changing or we're still in warmup
        # mode so let's wait until the scene has settled or we're finished
        # building the background model
        elif captured and p_diff >= MAX_PERCENT:
            captured = False
    print(f'{screenshoots_count} screenshots Captured!')
    print(f'Time taken {time.time() - start_time}s')
    return


def initialize_output_folder(video_path):
    '''Clean the output folder if already exists'''
    (filesname, extension) = os.path.splitext(video_path)
    output_folder_screenshot_path = f"{OUTPUT_SLIDES_DIR}/{video_path.rsplit('/')[-1].replace(extension, '')}"

    if os.path.exists(output_folder_screenshot_path):
        shutil.rmtree(output_folder_screenshot_path)

    os.makedirs(output_folder_screenshot_path, exist_ok=True)
    print('initialized output folder', output_folder_screenshot_path)
    return output_folder_screenshot_path


def convert_screenshots_to_pdf(output_folder_screenshot_path):
    (filesname, extension) = os.path.splitext(video_path)
    output_pdf_path = f"{OUTPUT_SLIDES_DIR}/{video_path.rsplit('/')[-1].replace(extension, '')}" + '.pdf'
    print('output_folder_screenshot_path', output_folder_screenshot_path)
    print('output_pdf_path', output_pdf_path)
    print('converting images to pdf..')
    with open(output_pdf_path, "wb") as f:
        f.write(img2pdf.convert(sorted(glob.glob(f"{output_folder_screenshot_path}/*.png"))))
    print('Pdf Created!')
    print('pdf saved at', output_pdf_path)


if __name__ == "__main__":

    input_dir_name = 'test'#要转换的视频所在的文件夹

    for file_name in os.listdir(input_dir_name):
        print(f"正在转换:{file_name}")
        video_path = str(pathlib.Path(input_dir_name, file_name))
        output_folder_screenshot_path = initialize_output_folder(video_path)
        detect_unique_screenshots(video_path, output_folder_screenshot_path)

        # 提取的图片转换为pdf
        convert_screenshots_to_pdf(output_folder_screenshot_path)

食用方法:

input_dir_name 代表要转换的视频所在文件夹

修改后直接运行即可,默认是提取后的图片自动转为pdf

你可能感兴趣的:(python,python,开发语言,视频)