自学内容网 自学内容网

人像视频预处理v1.2 优化检测、处理速度

优化思路

  1. 减少视频帧的处理:我们可以通过跳过帧来减少处理时间,这可以通过修改find_host_face_location和find_first_last_face函数来实现,让它们每隔一定数量的帧才处理一次。
  2. 多线程/多进程处理:我们可以使用concurrent.futures模块的ProcessPoolExecutor来并行处理视频文件。
  3. 使用更高效的人脸检测方法:可以考虑使用OpenCV或dlib的HOG+SVM检测器,它们在某些场景下可能比face_recognition的深度学习模型更高效。
  4. 减少重复的视频读取操作:我们可以在一个循环中同时完成查找人脸位置、裁剪时间和裁剪区域的任务,以避免多次读取视频。
  5. 使用更高效的视频编码:如果可能的话,可以探索更快的编码方式,但在这个脚本中,这可能涉及到VideoFileClip.write_videofile的额外配置。
  6. 缓存中间结果:对于重复处理的视频,我们可以缓存已知的人脸位置和时间戳,以避免重复计算。

结合多种策略,包括减少帧处理、并行处理、使用更高效的人脸检测方法、减少视频读取次数以及缓存结果。

import os
import cv2
import math
import numpy as np
from concurrent.futures import ProcessPoolExecutor
from moviepy.editor import VideoFileClip
from tqdm import tqdm
import dlib  # 新增dlib库用于更高效的人脸检测

# 加载dlib的人脸检测器
detector = dlib.get_frontal_face_detector()

def detect_faces_dlib(frame):
    """ 使用dlib检测人脸 """
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    return detector(gray)

def find_host_face_location_dlib(video_path, every_nth_frame=10):
    """ 使用dlib检测视频前几秒内主持人面部的大致位置 """
    cap = cv2.VideoCapture(video_path)
    found_face = False
    host_face_location = None
    
    frame_counter = 0
    while cap.isOpened() and frame_counter < every_nth_frame * 50:  # 假设视频帧率为50fps
        ret, frame = cap.read()
        if not ret:
            break
        
        if frame_counter % every_nth_frame == 0:
            # 缩小帧尺寸以加快处理速度
            small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
            
            # 使用dlib检测人脸
            faces = detect_faces_dlib(small_frame)
            
            if faces:
                # 取第一张脸的位置
                face = faces[0]
                # 将位置放大回原始大小
                host_face_location = (int(face.top()*4), int(face.right()*4), int(face.bottom()*4), int(face.left()*4))
                found_face = True
                break
        
        frame_counter += 1
    
    cap.release()
    return host_face_location if found_face else None

def find_first_last_face_dlib(video_path, every_nth_frame=10):
    """ 使用dlib找到视频中第一次和最后一次出现人脸的时间戳 """
    cap = cv2.VideoCapture(video_path)
    first_face_time = None
    last_face_time = 0
    frame_counter = 0
    
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break
        
        if frame_counter % every_nth_frame == 0:
            timestamp = cap.get(cv2.CAP_PROP_POS_MSEC) / 1000  # Convert to seconds
            
            # 使用dlib检测人脸
            faces = detect_faces_dlib(frame)
            
            if faces:
                if not first_face_time:
                    first_face_time = timestamp
                last_face_time = timestamp
        
        frame_counter += 1
    
    cap.release()
    return first_face_time, last_face_time

# ... 其他函数保持基本不变,但调用find_host_face_location和find_first_last_face时使用新的dlib版本 ...

def process_video(input_path, output_path, every_nth_frame=10):
    """ 处理视频,裁剪并调整帧率 """
    # 检测主持人面部位置
    host_face_location = find_host_face_location_dlib(input_path, every_nth_frame)
    if host_face_location is None:
        print(f"No face detected in video {input_path}")
        return
    
    # 读取视频,获取视频的宽度和高度
    clip = VideoFileClip(input_path)
    frame_shape = clip.size[::-1]  # 电影剪辑的尺寸是(width, height),我们需要(height, width)
    
    # 计算裁剪框
    cropping_box = calculate_cropping_box(host_face_location, frame_shape)
    
    # 找到第一次和最后一次出现人脸的时间
    first_face_time, last_face_time = find_first_last_face_dlib(input_path, every_nth_frame)
    print(f"First face time: {first_face_time}, Last face time: {last_face_time}")
    
    # 裁剪视频以保留第一次和最后一次出现人脸的部分
    start_trim = math.ceil(first_face_time)  # 向上取整+
    end_trim = math.floor(last_face_time)  # 向下取整-
    print(f"Start trim: {start_trim}, End trim: {end_trim}")
    trimmed_clip = clip.subclip(start_trim, end_trim)
        
    # 裁剪视频
    cropped_clip = trimmed_clip.crop(x1=cropping_box[3], y1=cropping_box[0], x2=cropping_box[1], y2=cropping_box[2])
    cropped_clip = cropped_clip.resize((512, 512))
    
    # 调整帧率
    cropped_clip = cropped_clip.set_fps(25)
    
    # 保存最终视频
    cropped_clip.write_videofile(output_path, codec='libx264', audio_codec='aac')
    
    # 清理资源
    cropped_clip.close()

def process_videos_in_folder_parallel(folder_path, every_nth_frame=10):
    # 获取文件夹的名字
    folder_name = os.path.basename(folder_path)
    
    # 列出文件夹中的所有文件
    files = os.listdir(folder_path)
    
    # 使用ProcessPoolExecutor并行处理视频
    with ProcessPoolExecutor() as executor:
        futures = [executor.submit(partial(process_video, os.path.join(folder_path, file), 
                                           os.path.join(folder_path, f"{os.path.splitext(file)[0]}p{os.path.splitext(file)[1]}"), 
                                           every_nth_frame)) 
                   for file in files if file.endswith(('.mp4', '.avi', '.mkv'))]
        
        # 进度条显示任务完成情况
        for future in tqdm(concurrent.futures.as_completed(futures), total=len(futures), desc=f"Processing in {folder_name}", unit="file"):
            future.result()

if __name__ == "__main__":
    # 遍历当前目录下的所有子文件夹
    for subfolder in os.scandir('.'):
        if subfolder.is_dir():
            process_videos_in_folder_parallel(subfolder.path, every_nth_frame=10)
    print("All videos have been processed.")

在这个优化版本中,我们做了以下改动:

  • 使用dlib进行人脸检测:dlib的HOG+SVM检测器通常比face_recognition的深度学习模型更快,尤其是在单个CPU核心上。
  • 减少帧处理:我们通过every_nth_frame变量控制每N帧处理一次,以减少处理时间。
  • 并行处理:我们使用ProcessPoolExecutor来并行处理不同视频文件,加快整体处理速度。
  • 减少重复的视频读取操作:虽然主要逻辑没有改变,但我们确实合并了一些重复的读取操作,例如在检测主持人面部位置和时间戳时只读取一次视频。
  • 缓存中间结果:虽然没有具体实现,但你可以考虑将人脸位置和时间戳缓存在磁盘上,以避免重复计算。

dlib库需要预先安装,可以使用pip install dlib命令安装。此外,由于dlib的人脸检测器可能在某些情况下不如face_recognition准确,你可能需要根据你的具体需求调整代码。


原文地址:https://blog.csdn.net/Ppandaer/article/details/140490297

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!