Skip to content

Latest commit

 

History

History
178 lines (148 loc) · 5.75 KB

AI项目实战03:自己动手实现视频换脸.md

File metadata and controls

178 lines (148 loc) · 5.75 KB

图片换脸、视频换脸早几年就出现了,也一直活跃在各大视频媒体。今天我也自己动手来实现下图片和视频换脸。

1. 使用的模型

模型使用的是insightface开源的模型文件,

image-20241130191503384

其中buffalo_l是人脸检测模型,inswapper_128.onnx是执行换脸的核心模型文件,128是换脸后的像素。分辨率比较低。后续我们可以使用超分辨率模型进行分辨率提升。

2.代码

2.1 视频帧提取

使用opencv将视频文件转换为系列图片

def video2imgs(input_video_path, output_dir, interval=1):
    """

    :param ipnut_video_path:
    :param output_dir:
    :param interval:
    :return: 视频的帧率,用于视频合成
    """
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)  # 目标文件夹不存在,则创建
    cap = cv2.VideoCapture(input_video_path)  # 获取视频
    judge = cap.isOpened()  # 判断是否能打开成功
    print(judge)
    fps = cap.get(cv2.CAP_PROP_FPS)
    # global fps  # 帧率,视频每秒展示多少张图片
    print("该视频的fps:", fps)

    frames = 1  # 用于统计所有帧数
    count = 1  # 用于统计保存的图片数量

    while judge:
        flag, frame = cap.read()  # 读取每一张图片 flag表示是否读取成功,frame是图片
        if not flag:
            print(flag)
            print("Process finished!")
            break
        else:
            if (frames + 1) % interval == 0:  # 每隔 interval 帧抽一张
                imgname = "pngs_" + str(count).rjust(3, "0") + ".png"
                newPath = os.path.join(output_dir, imgname)
                # print(imgname)
                cv2.imwrite(newPath, frame, [cv2.IMWRITE_JPEG_QUALITY, 100])
                # cv2.imencode('.jpg', frame)[1].tofile(newPath)
                count += 1
        frames += 1
    cap.release()
    print("共有 %d 张图片,%d 帧" % (count - 1, frames - 1))
    return fps

2.2 换脸后的图片进行合成视频

def image2video_2(images_dir: str, video_saved_path: str, fps: int = 30):
    videoWriter = VideoWriter(video_saved_path, fps)
    image_files = os.listdir(images_dir)
    image_files = [
        image_name
        for image_name in image_files
        if image_name.split(".")[-1] in ["png", "jpg", "jpeg"]
    ]
    image_list = []
    for image in tqdm(sorted(image_files)):
        if image.lower().split(".")[-1] in ["jpg", "png", "jpeg"]:
            image_path = os.path.join(images_dir, image)
            img = cv2.imread(image_path)
            # videoWriter.write(img)
            image_list.append(img)

    videoWriter.write(image_list)
    videoWriter.close()

2.3 单张图片执行换脸

def swapface(
    input_video_path: str,
    target_face_path: str,
    swapper: insightface.model_zoo.inswapper.INSwapper,
    app: FaceAnalysis,
):
    """
    单人的视频换脸
    1.视频抽取帧
    2.每张图片进行人脸转换
    3.对人脸转换后的图片进行视频合成
    :param input_video_path:输入视频路径
    :param target_face_path:包含目标face的图片
    :return: 换脸后的视频路径
    """
    video_name = os.path.splitext(os.path.basename(input_video_path))[0]
    output_frames_dir = os.path.join(BASE_DIR, video_name, "output_frames")
    swap_image_dir = os.path.join(BASE_DIR, video_name, "swapped_frames")
    if not os.path.exists(output_frames_dir):
        os.makedirs(output_frames_dir)
    if not os.path.exists(swap_image_dir):
        os.makedirs(swap_image_dir)
    interval = 1
    fps = 30
    fps = video_images.video2imgs(
        input_video_path, output_frames_dir, interval=interval
    )
    faceswap_insightface.swap_faces_batch(
        output_frames_dir, target_face_path, swap_image_dir, swapper, app
    )
    output_video_path = os.path.join(
        BASE_DIR,
        target_face_path.split(".")[0].split("/")[-1]
        + "_"
        + video_name
        + "_swapped.mp4",
    )
    video_images.image2video_2(swap_image_dir, output_video_path, fps=fps / interval)
    # 删除中间目录
    shutil.rmtree(os.path.join(BASE_DIR, video_name))
    print(f"换脸后的视频保存在:{output_video_path}")
    return output_video_path

2.3 Gradio Web

def gradio_interface(input_video, target_image):
    # 初始化FaceAnalysis和Swapper
    app = FaceAnalysis(
        name="buffalo_l",
        root="checkpoints",
        providers=["CUDAExecutionProvider", "CPUExecutionProvider"],
        provider_options=[{"device_id": 0}, {}],
    )
    app.prepare(ctx_id=0, det_size=(640, 640))
    swapper = insightface.model_zoo.get_model(
        "./checkpoints/models/inswapper_128.onnx", download=True, download_zip=True
    )

    # 进行换脸操作
    output_video_path = swapface(input_video, target_image, swapper, app)
    # 返回换脸后的视频路径
    return output_video_path


# Gradio界面
iface = gr.Interface(
    fn=gradio_interface,
    inputs=[
        gr.Video(label="Input Video"),
        gr.Image(type="filepath", label="Target Face Image"),
    ],
    outputs=gr.Video(label="Swapped Video"),
    title="Video Face Swap",
    description="Upload a video and a target face image to swap faces.",
)

# 启动Gradio界面
iface.launch(server_name="0.0.0.0", debug=True)

3. 效果

  • 刘亦菲视频换脸

image-20241130193134847

github代码: https://github.com/chongzicbo/ai-master/tree/main

github文章合集:https://github.com/chongzicbo/ReadWriteThink

二维码