最新口型同步技术EchoMimic部署

news2025/1/13 7:45:19

EchoMimic是由蚂蚁集团推出的一个 AI 驱动的口型同步技术项目,能够通过人像面部特征和音频来帮助人物“对口型”,生成逼真的动态肖像视频。

EchoMimic的技术亮点在于其创新的动画生成方法,它不仅能够通过音频和面部关键点单独驱动图像动画,还能结合这两种方式,通过音频信号和面部关键点的组合来生成逼真的“说话的头部”视频。

EchoMimic支持单独使用音频或面部标志点生成肖像视频,也支持将音频和人像照片相结合,实现更自然、流畅的对口型效果。

EchoMimic支持多语言,包括中文普通话、英语,以及适应唱歌等场景。

github项目地址:https://github.com/BadToBest/EchoMimic。

一、环境安装

1、python环境

建议安装python版本在3.10以上。

2、pip库安装

pip install torch==2.0.1+cu118 torchvision==0.15.2+cu118 torchaudio==2.0.2 --index-url https://download.pytorch.org/whl/cu118

pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

3、模型下载

git lfs install

git clone https://huggingface.co/BadToBest/EchoMimic

、功能测试

1、运行测试

(1)python代码调用测试audio2video

import argparse
import os
import random
import platform
import subprocess
from datetime import datetime
from pathlib import Path

import cv2
import numpy as np
import torch
from diffusers import AutoencoderKL, DDIMScheduler
from omegaconf import OmegaConf
from PIL import Image

from src.models.unet_2d_condition import UNet2DConditionModel
from src.models.unet_3d_echo import EchoUNet3DConditionModel
from src.models.whisper.audio2feature import load_audio_model
from src.pipelines.pipeline_echo_mimic import Audio2VideoPipeline
from src.utils.util import save_videos_grid, crop_and_pad
from src.models.face_locator import FaceLocator
from moviepy.editor import VideoFileClip, AudioFileClip
from facenet_pytorch import MTCNN

# Check and add FFmpeg path if necessary
ffmpeg_path = os.getenv('FFMPEG_PATH')
if ffmpeg_path is None and platform.system() in ['Linux', 'Darwin']:
    try:
        result = subprocess.run(['which', 'ffmpeg'], capture_output=True, text=True)
        if result.returncode == 0:
            ffmpeg_path = result.stdout.strip()
            print(f"FFmpeg is installed at: {ffmpeg_path}")
        else:
            print("FFmpeg is not installed. Please download ffmpeg-static and export to FFMPEG_PATH.")
            print("For example: export FFMPEG_PATH=/musetalk/ffmpeg-4.4-amd64-static")
    except Exception as e:
        print(f"Error finding ffmpeg: {e}")
else:
    if ffmpeg_path and ffmpeg_path not in os.getenv('PATH'):
        print("Adding FFMPEG_PATH to PATH")
        os.environ["PATH"] = f"{ffmpeg_path}:{os.environ['PATH']}"

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("--config", type=str, default="./configs/prompts/animation.yaml")
    parser.add_argument("-W", type=int, default=512)
    parser.add_argument("-H", type=int, default=512)
    parser.add_argument("-L", type=int, default=1200)
    parser.add_argument("--seed", type=int, default=420)
    parser.add_argument("--facemusk_dilation_ratio", type=float, default=0.1)
    parser.add_argument("--facecrop_dilation_ratio", type=float, default=0.5)
    parser.add_argument("--context_frames", type=int, default=12)
    parser.add_argument("--context_overlap", type=int, default=3)
    parser.add_argument("--cfg", type=float, default=2.5)
    parser.add_argument("--steps", type=int, default=30)
    parser.add_argument("--sample_rate", type=int, default=16000)
    parser.add_argument("--fps", type=int, default=24)
    parser.add_argument("--device", type=str, default="cuda")
    return parser.parse_args()

def select_face(det_bboxes, probs):
    """
    Select the largest face with a detection probability above 0.8.
    """
    if det_bboxes is None or probs is None:
        return None
    filtered_bboxes = [det_bboxes[i] for i in range(len(det_bboxes)) if probs[i] > 0.8]
    if not filtered_bboxes:
        return None
    return max(filtered_bboxes, key=lambda x: (x[3] - x[1]) * (x[2] - x[0]))

def main():
    args = parse_args()

    config = OmegaConf.load(args.config)
    weight_dtype = torch.float16 if config.weight_dtype == "fp16" else torch.float32

    device = args.device
    if "cuda" in device and not torch.cuda.is_available():
        device = "cpu"

    infer_config = OmegaConf.load(config.inference_config)

    ############# Initialize models #############

    vae = AutoencoderKL.from_pretrained(config.pretrained_vae_path).to("cuda", dtype=weight_dtype)

    reference_unet = UNet2DConditionModel.from_pretrained(config.pretrained_base_model_path, subfolder="unet").to(dtype=weight_dtype, device=device)
    reference_unet.load_state_dict(torch.load(config.reference_unet_path, map_location="cpu"))

    unet_kwargs = infer_config.unet_additional_kwargs or {}
    denoising_unet = EchoUNet3DConditionModel.from_pretrained_2d(
        config.pretrained_base_model_path,
        config.motion_module_path if os.path.exists(config.motion_module_path) else "",
        subfolder="unet",
        unet_additional_kwargs=unet_kwargs
    ).to(dtype=weight_dtype, device=device)
    denoising_unet.load_state_dict(torch.load(config.denoising_unet_path, map_location="cpu"), strict=False)

    face_locator = FaceLocator(320, conditioning_channels=1, block_out_channels=(16, 32, 96, 256)).to(dtype=weight_dtype, device="cuda")
    face_locator.load_state_dict(torch.load(config.face_locator_path))

    audio_processor = load_audio_model(model_path=config.audio_model_path, device=device)
    face_detector = MTCNN(image_size=320, margin=0, min_face_size=20, thresholds=[0.6, 0.7, 0.7], factor=0.709, post_process=True, device=device)

    ############# Initiate pipeline #############

    scheduler = DDIMScheduler(**OmegaConf.to_container(infer_config.noise_scheduler_kwargs))
    pipe = Audio2VideoPipeline(
        vae=vae,
        reference_unet=reference_unet,
        denoising_unet=denoising_unet,
        audio_guider=audio_processor,
        face_locator=face_locator,
        scheduler=scheduler,
    ).to("cuda", dtype=weight_dtype)

    date_str = datetime.now().strftime("%Y%m%d")
    time_str = datetime.now().strftime("%H%M")
    save_dir_name = f"{time_str}--seed_{args.seed}-{args.W}x{args.H}"
    save_dir = Path(f"output/{date_str}/{save_dir_name}")
    save_dir.mkdir(exist_ok=True, parents=True)

    for ref_image_path, audio_paths in config["test_cases"].items():
        for audio_path in audio_paths:
            seed = args.seed if args.seed is not None and args.seed > -1 else random.randint(100, 1000000)
            generator = torch.manual_seed(seed)

            ref_name = Path(ref_image_path).stem
            audio_name = Path(audio_path).stem
            final_fps = args.fps

            #### Prepare face mask
            face_img = cv2.imread(ref_image_path)
            face_mask = np.zeros((face_img.shape[0], face_img.shape[1]), dtype='uint8')

            det_bboxes, probs = face_detector.detect(face_img)
            select_bbox = select_face(det_bboxes, probs)

            if select_bbox is None:
                face_mask[:, :] = 255
            else:
                xyxy = np.round(select_bbox[:4]).astype('int')
                rb, re, cb, ce = xyxy[1], xyxy[3], xyxy[0], xyxy[2]
                r_pad = int((re - rb) * args.facemusk_dilation_ratio)
                c_pad = int((ce - cb) * args.facemusk_dilation_ratio)
                face_mask[rb - r_pad : re + r_pad, cb - c_pad : ce + c_pad] = 255

                r_pad_crop = int((re - rb) * args.facecrop_dilation_ratio)
                c_pad_crop = int((ce - cb) * args.facecrop_dilation_ratio)
                crop_rect = [max(0, cb - c_pad_crop), max(0, rb - r_pad_crop), min(ce + c_pad_crop, face_img.shape[1]), min(re + r_pad_crop, face_img.shape[0])]
                face_img = crop_and_pad(face_img, crop_rect)
                face_mask = crop_and_pad(face_mask, crop_rect)
                face_img = cv2.resize(face_img, (args.W, args.H))
                face_mask = cv2.resize(face_mask, (args.W, args.H))

            ref_image_pil = Image.fromarray(face_img[:, :, [2, 1, 0]])
            face_mask_tensor = torch.Tensor(face_mask).to(dtype=weight_dtype, device="cuda").unsqueeze(0).unsqueeze(0).unsqueeze(0) / 255.0

            video = pipe(
                ref_image_pil,
                audio_path,
                face_mask_tensor,
                width=args.W,
                height=args.H,
                duration=args.L,
                num_inference_steps=args.steps,
                cfg_scale=args.cfg,
                generator=generator,
                audio_sample_rate=args.sample_rate,
                context_frames=args.context_frames,
                fps=final_fps,
                context_overlap=args.context_overlap
            ).videos

            video_save_path = save_dir / f"{ref_name}_{audio_name}_{args.H}x{args.W}_{int(args.cfg)}_{time_str}.mp4"
            save_videos_grid(video, str(video_save_path), n_rows=1, fps=final_fps)

            # Add audio to generated video
            with_audio_path = save_dir / f"{ref_name}_{audio_name}_{args.H}x{args.W}_{int(args.cfg)}_{time_str}_withaudio.mp4"
            video_clip = VideoFileClip(str(video_save_path))
            audio_clip = AudioFileClip(audio_path)
            final_video = video_clip.set_audio(audio_clip)
            final_video.write_videofile(str(with_audio_path), codec="libx264", audio_codec="aac")
            print(f"Saved video with audio to {with_audio_path}")

if __name__ == "__main__":
    main()

(2)python代码调用测试audio2pose

未完......

更多详细的内容欢迎关注:杰哥新技术

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1980161.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

翻译: 可视化深度学习反向传播原理二

顺便一提 这有一点点像描述生物中 神经元的网络如何学习的一个理论 “赫布理论” 总结起来就是“一同激活的神经元关联在一起” 这里 权重的最大增长 即连接变得更强的部分 就会发生在已经最活跃的神经元 和想要更多激发的神经元之间 可以说 看见一个“2”时激发的神经元 会和…

【Git】git 不跟踪和gitignore区别

文章目录 不跟踪(Untracked):.gitignore 文件:总结 在 Git 中,不跟踪(untracked)和 .gitignore 文件有不同的作用和用途: 不跟踪(Untracked): 不…

【Material-UI】Autocomplete中的高亮功能(Highlights)详解

文章目录 一、简介二、实现高亮功能示例代码代码解释 三、实际应用场景1. 搜索功能2. 表单自动完成 四、总结 在现代Web开发中,提供清晰的用户反馈是提升用户体验的重要组成部分。Material-UI的Autocomplete组件通过高亮功能,帮助用户快速识别搜索结果中…

Javaweb项目|ssm基于ssm的宠物医院管理系统的设计与实现vue

收藏点赞不迷路 关注作者有好处 文末获取源码 一、系统展示 二、万字文档展示 基于ssm基于ssm的宠物医院管理系统的设计与实现vue 开发语言:Java 数据库:MySQL 技术:SpringSpringMVCMyBatisVue 工具:IDEA/Ecilpse、Navicat、Ma…

Java线程池的核心参数与工作原理

Java线程池的核心参数与工作原理 1、线程池工作原理2、核心参数 💖The Begin💖点点关注,收藏不迷路💖 1、线程池工作原理 Java的ThreadPoolExecutor是线程池的核心实现,负责管理一组线程以异步方式执行提交的任务。其…

keil编译报错error:#8:missing closing quote 处理

MDK5采用UTF-8,提示很多个这样的error:#8:missing closing quote 的错误信息。提供以下几种方式解决: 在KEIL中Options for Target Flash -> C/C -> Misc Controls添加“--localeenglish”。

AI智能名片小程序在促销性内容营销中的创新应用与策略分析

摘要:在数字化时代,企业营销手段日益丰富多元,促销性内容作为吸引顾客、促进消费的关键手段之一,其形式与效率不断被革新。随着人工智能(AI)技术的飞速发展,AI智能名片小程序作为一种新兴的营销…

React(四):DOCX文件在线预览

效果 注意 ⚠️注意:部分文件预览存在问题 依赖 $ yarn add docx-preview $ yarn add jszip源码 import ./index.scss; import {useRef} from react; import type {UploadRequestOption} from rc-upload/lib/interface; import {Upload, Button, message} from an…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 会议室占用时间(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题…

力扣-46.全排列

刷力扣热题–第二十六天:46.全排列 新手第二十六天 奋战敲代码,持之以恒,见证成长 1.题目简介 2.题目解答 这道题目想了会,思路比较好想,但一直没调试成功,所以就参考了力扣官网的代码,积累一下回溯算法的实现和基本实现思路,即先试探后回溯,结果在下面…

R语言中的宽长数据转换:tidyr包的使用指南

在数据分析中,数据的存储方式直接影响分析过程的效率和准确性。常见的数据存储形式有宽型数据(wide format)和长型数据(long format)。宽型数据适合人类查看和理解,而长型数据则更适合计算机处理和分析。为…

AI生成PPT?三款工具让总结更轻松

哎呀,职场新人们,你们是不是也跟我一样,刚开始做PPT的时候,感觉像是走进了一个大迷宫,脑袋里装满了想法,但就是不知道怎么把它们变成一页页漂亮的幻灯片?别急,今天咱们就来聊聊三个超…

JavaEE-多线程编程阻塞队列

目录 生产者消费者模型 生产者消费者模型优势 通过代码看一下生产者消费者模型(使用阻塞队列) 自己实现阻塞队列 之前在数据结构中学的队列是最基础的队列,在实际开发中针对队列还有很多形式:(1)普通队…

PyCharm | PyCharm中创建带有注释的py文件

文章目录 0 问题引入1 问题解决1.1 在Pycharm里进行设置1.2 模板1.3 可选参数 2 效果展示 0 问题引入 想要创建带注释的py文件,该如何解决呢? 1 问题解决 1.1 在Pycharm里进行设置 打开Pycharm的Seetings按照如图所示来操作 1.2 模板 # -*- codin…

【大模型从入门到精通3】openAI api的入门介绍3

这里写目录标题 理论问题实践问题任务 1: 基本的 API 请求任务 2: 安全处理 API 密钥任务 3: 解释 API 响应任务 4: 强大的错误处理任务 5: 交互式聊天界面任务 6: 响应后处理任务 7: 动态内容生成任务 8: 优化与监控 理论问题 整合OpenAI的API到应用中为机器学习工程师、数据…

python运行js之execjs基本使用

python运行js之execjs基本使用 现在大部分网站都使用JS加密和JS加载的情况,数据并不能直接被抓取出来,这时候就需要使用第三方类库来执行JS语句。 官网:https://pypi.org/project/PyExecJS/ 使用前提:电脑需要安装 Node.js 一、安…

20240805 每日AI必读资讯

世界首例!AI机器人做牙科手术,8倍速诊疗比人类医生更精准 - Perceptive:让人工智能控制的自主机器人,首次对人类患者进行了全过程的牙科手术,速度大约是人类牙医的8倍。 - 两项新技术 1、OCT 3D成像系统:…

【MySQL进阶】MySQL主从复制

目录 MySQL主从复制 概念 主从形式 一主多从 多主一从 双主复制 主从级联复制 主从复制原理 三个线程 两个日志文件 主从复制的主要工作模式 异步复制 半同步复制 全同步复制 MySQL主从复制 概念 MySQL主从复制是一种数据分布机制,允许从一个数据库服…

Chapter 26 Python魔术方法

欢迎大家订阅【Python从入门到精通】专栏,一起探索Python的无限可能! 文章目录 前言一、什么是魔术方法?二、常见的魔术方法① __init__构造方法② __str__字符串方法③ __lt__比较方法④ __le__比较方法⑤ __eq__比较方法 前言 本章将详细讲…

RabbitMQ高级特性 - 消费者消息确认机制

文章目录 RabbitMQ 消息确认机制背景消费者消息确认机制概述手动确认(RabbitMQ 原生 SDK)手动确认(Spring-AMQP 封装 RabbitMQ SDK)AcknowledgeMode.NONEAcknowledgeMode.AUTO(默认)AcknowledgeMode.MANUAL…