GitHub - video2pdfslides
video2pdfslides 库主要功能:从视频中提取图片,并去重后,转为PDF.
1. 依赖与安装
依赖项:
imutils==0.5.4
opencv_python==4.5.2.52
img2pdf==0.4.1
安装:
git clone https://github.com/kaushikj/video2pdfslides
cd video2pdfslides
pip install -r requirements.txt
运行:
python video2pdfslides.py <video_path>
2. video2pdfslides
https://github.com/kaushikj/video2pdfslides/blob/main/video2pdfslides.py
#!/usr/bin/python3
#!--*-- coding: utf-8 --*--
import os
import time
import cv2
import imutils
import shutil
import img2pdf
import glob
import argparse
############# Define constants
OUTPUT_SLIDES_DIR = f"./output"
FRAME_RATE = 3 # no.of frames per second that needs to be processed, fewer the count faster the speed
WARMUP = FRAME_RATE # initial number of frames to be skipped
FGBG_HISTORY = FRAME_RATE * 15 # no.of frames in background object
VAR_THRESHOLD = 16 # Threshold on the squared Mahalanobis distance between the pixel and the model to decide whether a pixel is well described by the background model.
DETECT_SHADOWS = False # If true, the algorithm will detect shadows and mark them.
MIN_PERCENT = 0.1 # min % of diff between foreground and background to detect if motion has stopped
MAX_PERCENT = 3 # max % of diff between foreground and background to detect if frame is still in motion
#视频抽帧
def get_frames(video_path):
'''
A fucntion to return the frames from a video located at video_path
this function skips frames as defined in FRAME_RATE
'''
# open a pointer to the video file initialize the width and height of the frame
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)
# loop over the frames of the video
while True:
# grab a frame from the video
vs.set(cv2.CAP_PROP_POS_MSEC, frame_time * 1000) # move frame to a timestamp
frame_time += 1/FRAME_RATE
(_, frame) = vs.read()
# if the frame is None, then we have reached the end of the video file
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):
'''
Initialize fgbg a Background object with Parameters
history = The number of frames history that effects the background subtractor
varThreshold = Threshold on the squared Mahalanobis distance between the pixel and the model to decide whether a pixel is well described by the background model. This parameter does not affect the background update.
detectShadows = If true, the algorithm will detect shadows and mark them. It decreases the speed a bit, so if you do not need this feature, set the parameter to 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()
frame = imutils.resize(frame, width=600)
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 = os.path.join(output_folder_screenshot_path, filename)
print("saving {}".format(path))
cv2.imwrite(path, orig)
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
'''
output_folder_screenshot_path = f"{OUTPUT_SLIDES_DIR}/{video_path.rsplit('/')[-1].split('.')[0]}"
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):
output_pdf_path = f"{OUTPUT_SLIDES_DIR}/{video_path.rsplit('/')[-1].split('.')[0]}" + '.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__":
parser = argparse.ArgumentParser("video_path")
parser.add_argument("video_path", help="path of video to be converted to pdf slides", type=str)
args = parser.parse_args()
video_path = args.video_path
print('video_path', video_path)
output_folder_screenshot_path = initialize_output_folder(video_path)
detect_unique_screenshots(video_path, output_folder_screenshot_path)
print('Please Manually verify screenshots and delete duplicates')
while True:
choice = input("Press y to continue and n to terminate")
choice = choice.lower().strip()
if choice in ['y', 'n']:
break
else:
print('please enter a valid choice')
if choice == 'y':
convert_screenshots_to_pdf(output_folder_screenshot_path)