查找视频中相同的人脸并保存(参考人物单张)

时间:2025-10-10 05:50:15  阅读量:  分类:标签:


import cv2
import os
import face_recognition
import sys
import imagehash
from PIL import Image
import numpy as np

# 这是一个基于图片整体相似度的视频帧提取程序
try:
    # 强制使用CPU,禁用所有硬件加速
    cv2.ocl.setUseOpenCL(False)
    os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
    os.environ["DLIB_USE_CUDA"] = "0"

    # 1. 加载参考图片(加载目录下所有图片)
    ref_dir = os.path.abspath("./file")  # 参考图片所在目录
    if not os.path.exists(ref_dir):
        print(f"参考图片目录不存在: {ref_dir}")
        sys.exit(1)

    ref_encodings = []  # 存储所有参考图片的人脸编码
    ref_hashes = []  # 存储所有参考图片的哈希值

    # 遍历目录下所有图片文件
    for filename in os.listdir(ref_dir):
        if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
            ref_path = os.path.join(ref_dir, filename)
            # 用OpenCV加载图片
            ref_img_cv = cv2.imread(ref_path)
            ref_img_rgb = cv2.cvtColor(ref_img_cv, cv2.COLOR_BGR2RGB)

            # 提取人脸编码用于匹配判断
            encodings = face_recognition.face_encodings(ref_img_rgb)
            if encodings:
                ref_encodings.append(encodings[0])

            # 计算图片哈希值用于整体相似度比较
            pil_img = Image.fromarray(ref_img_rgb)
            img_hash = imagehash.phash(pil_img)  # 使用感知哈希算法
            ref_hashes.append(img_hash)

            print(f"加载参考图片: {filename}")

    if not ref_encodings:
        print("没有有效的参考人脸数据,程序退出")
        sys.exit(1)

    # 2. 准备视频目录和输出文件夹(使用绝对路径)
    video_dir = os.path.abspath("./file")  # 视频所在目录
    if not os.path.exists(video_dir):
        print(f"视频目录不存在: {video_dir}")
        sys.exit(1)

    output_dir = "pic"
    os.makedirs(output_dir, exist_ok=True)

    # 存储已保存图片的哈希值,用于整体相似度去重
    saved_hashes = []
    # 图片相似度阈值(哈希差异值,越小越相似,通常8-10为宜)
    IMAGE_SIMILARITY_THRESHOLD = 15

    # 统计总匹配图片数量
    total_count = 0

    # 3. 遍历目录下所有视频文件
    video_extensions = ('.mp4', '.avi', '.mov', '.mkv', '.flv')
    for video_filename in os.listdir(video_dir):
        if video_filename.lower().endswith(video_extensions):
            video_path = os.path.join(video_dir, video_filename)
            print(f"\n开始处理视频: {video_filename}")

            # 打开视频(使用CV_CAP_FFMPEG后端)
            video = cv2.VideoCapture(video_path, cv2.CAP_FFMPEG)
            if not video.isOpened():
                print(f"无法打开视频,尝试更换解码器")
                # 尝试不同的后端
                video = cv2.VideoCapture(video_path, cv2.CAP_MSMF)
                if not video.isOpened():
                    print(f"所有解码器都无法打开视频: {video_filename},跳过该视频")
                    continue

            # 每段视频的帧计数(用于文件名)
            video_count = 0
            # 一共查找多少帧
            max_frames = 70000
            # 从第几帧开始查找
            current_frame = 4000

            # 获取视频总帧数,校验初始帧是否合法
            total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
            if current_frame >= total_frames:
                print(f"起始帧 {current_frame} 超过视频总帧数 {total_frames},跳过该视频")
                video.release()
                continue
            # 跳转到设定的初始帧
            video.set(cv2.CAP_PROP_POS_FRAMES, current_frame)

            while current_frame < max_frames:
                ret, frame = video.read()
                if not ret:
                    break

                print(f"处理第{current_frame}帧...")

                # 将图像的颜色通道从 BGR 格式转换为 RGB 格式
                rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

                # 检测人脸
                face_locations = face_recognition.face_locations(rgb_frame, model="hog", number_of_times_to_upsample=0)
                if face_locations:
                    # 提取第一个人脸的特征
                    face_encodings = face_recognition.face_encodings(rgb_frame, [face_locations[0]], num_jitters=1)
                    if face_encodings:
                        # 与所有参考人脸比对,只要有一个匹配即可
                        matches = face_recognition.compare_faces(ref_encodings, face_encodings[0])
                        if True in matches:  # 只要有一个参考人脸匹配
                            # 计算当前帧的哈希值
                            pil_frame = Image.fromarray(rgb_frame)
                            frame_hash = imagehash.phash(pil_frame)

                            # 判断是否与已保存的图片过于相似
                            is_duplicate = False
                            if saved_hashes:  # 如果已有保存的图片
                                # 计算与所有已保存图片的哈希差异
                                for saved_hash in saved_hashes:
                                    hash_diff = abs(frame_hash - saved_hash)
                                    if hash_diff < IMAGE_SIMILARITY_THRESHOLD:
                                        is_duplicate = True
                                        print(f"检测到相似图片(哈希差异: {hash_diff}),不重复保存")
                                        break

                            # 不是相似图片才保存
                            if not is_duplicate:
                                # 文件名包含总计数,确保唯一
                                save_path = f"{output_dir}/{total_count}.jpg"
                                cv2.imwrite(save_path, frame)
                                # 将新图片的哈希值加入已保存列表
                                saved_hashes.append(frame_hash)
                                video_count += 1
                                total_count += 1
                                print(f"找到新的匹配图片,已保存: {save_path}")

                # 强制释放
                del frame, rgb_frame, face_locations

                current_frame += 500
                video.set(cv2.CAP_PROP_POS_FRAMES, current_frame)

            video.release()
            print(f"视频{video_filename}处理完成,本视频找到{video_count}张匹配图片")

    print(f"\n所有视频处理完成,共找到{total_count}张不重复的匹配图片")

except Exception as e:
    print(f"错误详情: {str(e)}")
    sys.exit(1)