数字人形象实时切换所遇问题与解决
目标:在不重启后端的状况下,通过前端选择切换数字人 参考 这个页面
LipReal类中添加方法如下: ` def change_avatar(self, avatar): self.frame_list_cycle, self.face_list_cycle, self.coord_list_cycle = avatar
# 当切换数字人形象时,旧形象相关的帧数据不再有用
while not self.res_frame_queue.empty():
try:
self.res_frame_queue.get_nowait()
except queue.Empty:
break
logger.info('数字人形象切换完成')`
在前端切换数字人形象,会出现两个问题
- 切换后,数字人说话,贴图会是最初始的数字人脸部,这是100多张切换到900多张数字人情况
- 切换失败,报错list index out of range,这是900多张切换到100多张数字人情况
Exception in thread Thread-3 (process_frames): Traceback (most recent call last): File "E:\tools\anaconda3\envs\nerfstream\lib\threading.py", line 1016, in _bootstrap_inner self.run() File "E:\tools\anaconda3\envs\nerfstream\lib\threading.py", line 953, in run self._target(*self._args, **self._kwargs) File "E:\LZ\project\LiveTalking\lipreal.py", line 227, in process_frames combine_frame = self.frame_list_cycle[idx] IndexError: list index out of range
原因在lipreal.py中的render()函数中推理线程仍在用旧的图像进行推理。
解决:
-
停止当前运行的 inference 线程
-
更新 avatar 数据
-
启动新的 inference 线程使用新的 avatar 数据
在def __init__(self, opt, model, avatar):,初始化新的变量
def __init__(self, opt, model, avatar): # 原先代码 # 添加用于渲染线程的退出事件 self._inference_thread = None self._process_thread = None self._render_quit_event = None
修改render函数,修改成如下 ` def render(self, quit_event, loop=None, audio_track=None, video_track=None): self.loop = loop self.audio_track = audio_track self.video_track = video_track
self.tts.render(quit_event)
self.init_customindex()
# 使用新的退出事件
self._render_quit_event = Event()
self._start_threads(self._render_quit_event)
# 主渲染循环
count = 0
totaltime = 0
_starttime = time.perf_counter()
while not quit_event.is_set():
t = time.perf_counter()
self.asr.run_step()
if video_track._queue.qsize() >= 5:
logger.debug('sleep qsize=%d', video_track._queue.qsize())
time.sleep(0.04 * video_track._queue.qsize() * 0.8)
# 渲染结束时清理
if self._render_quit_event:
self._render_quit_event.set()
logger.info('lipreal thread stop')`
添加新函数 ` def change_avatar(self, avatar): # 1. 停止所有处理线程 if self._render_quit_event: self._render_quit_event.set()
# 等待线程结束
if self._inference_thread and self._inference_thread.is_alive():
self._inference_thread.join(timeout=1.0)
if self._process_thread and self._process_thread.is_alive():
self._process_thread.join(timeout=1.0)
# 2. 清空所有队列
self._clear_queues()
# 3. 更新avatar数据
self.frame_list_cycle, self.face_list_cycle, self.coord_list_cycle = avatar
# 4. 重新创建退出事件和启动线程
self._render_quit_event = Event()
self._start_threads(self._render_quit_event)
logger.info('数字人形象切换完成')
def _clear_queues(self):
"""清空所有相关队列"""
while not self.res_frame_queue.empty():
try:
self.res_frame_queue.get_nowait()
except queue.Empty:
break
# 清空ASR相关队列
while not self.asr.feat_queue.empty():
try:
self.asr.feat_queue.get_nowait()
except queue.Empty:
break
while not self.asr.output_queue.empty():
try:
self.asr.output_queue.get_nowait()
except queue.Empty:
break
def _start_threads(self, quit_event):
"""启动所有处理线程"""
# 启动处理线程
self._process_thread = Thread(
target=self.process_frames,
args=(quit_event, self.loop, self.audio_track, self.video_track)
)
self._process_thread.start()
# 启动推理线程
self._inference_thread = Thread(
target=inference,
args=(quit_event, self.batch_size, self.face_list_cycle,
self.asr.feat_queue, self.asr.output_queue, self.res_frame_queue,
self.model)
)
self._inference_thread.start()`
在app.py中添加新的异步函数 `#切换数字人形象 async def change_avatar(request): try: params = await request.json() sessionid = params.get('sessionid', 0) new_avatar_id = params.get('avatar_id')
if sessionid not in nerfreals:
return web.Response(
content_type="application/json",
text=json.dumps({"code": -1, "msg": "Session not found"}),
status=404
)
# 加载新的形象数据
new_avatar = await asyncio.get_event_loop().run_in_executor(
None,
lambda: load_avatar(new_avatar_id)
)
# 更新当前会话的数字人形象
nerfreals[sessionid].change_avatar(new_avatar)
return web.Response(
content_type="application/json",
text=json.dumps({"code": 0, "msg": "Avatar changed successfully"})
)
except FileNotFoundError as e:
return web.Response(
content_type="application/json",
text=json.dumps({"code": -1, "msg": f"Avatar not found: {str(e)}"}),
status=404
)
except Exception as e:
return web.Response(
content_type="application/json",
text=json.dumps({"code": -1, "msg": str(e)}),
status=500
)`
主函数对应位置添加路由
appasync.router.add_post("/change_avatar", change_avatar) #数字人形象切换
所用模型为wav2lip @lipku 在我修改 def render(self, quit_event, loop=None, audio_track=None, video_track=None) 后 会对整体其他的功能带来影响吗
似乎切换人物后有延迟,请问修改后的render函数中,使用的还是 while not quit_event.is_set() 么,那它下面的内容似乎仍在多线程运行
@Chengyang852 你的意思点击切换后,需要等待时间才能切换好新的数字人形象,还是说,数字人在说话时有延迟?
@Chengyang852 你的意思点击切换后,需要等待时间才能切换好新的数字人形象,还是说,数字人在说话时有延迟?
说话时有延迟。虽然在调用 change_avatar 后,已经对 process_frames 和 inference 两个部分进行了控制,但 render 函数中的 self.asr.run_step() 仍然持续运行,没有被停止。此外,quit_event 似乎是用来控制停止流程的,但现在的实现方式可能导致 stop 功能失效。不知道我理解的对不对
@Chengyang852 说话有延迟问题也未解决,无论切换前后,大约延迟2-3s,数字人才张口说话。如果你成功解决,非常乐意你在此分享。感谢
@Chengyang852 说话有延迟问题也未解决,无论切换前后,大约延迟2-3s,数字人才张口说话。如果你成功解决,非常乐意你在此分享。感谢
不好意思,是我前面表述有问题,我想说的是形象切换后,声音和画面不匹配,画面显示有延迟(先有声音)
@Chengyang852 关于音画不同步问题,我没遇到,这边目前状况是音画同步,但是会在文字显示出来后2-3s才播放音频与视频。建议你搜搜音画不同步的议题。😀
大佬,请问前端是怎么设置的,小白不太懂,求指教
俺也一样,遇到了音画不同步的问题,声音先行,画面延后出现
俺也一样,遇到了音画不同步的问题,声音先行,画面延后出现
有一个sleep,你把系数调小一点就好了
@dksodjo 请教老哥,是哪里的sleep做调整?
请问老哥有试过musetalk模式下的切换么? 我参考你的方法切换后脸花了
请问老哥有试过musetalk模式下的切换么? 我参考你的方法切换后脸花了
musetalk下的切换其实很简单,同一个进程下启动二个MuseReal实例,每个实例的角色不一样就可以,但是前提是启动推流那块代码不要和启动数字人流程放在一起
目标:在不重启后端的状况下,通过前端选择切换数字人 参考 这个页面
LipReal类中添加方法如下: ` def change_avatar(self, avatar): self.frame_list_cycle, self.face_list_cycle, self.coord_list_cycle = avatar
# 当切换数字人形象时,旧形象相关的帧数据不再有用 while not self.res_frame_queue.empty(): try: self.res_frame_queue.get_nowait() except queue.Empty: break logger.info('数字人形象切换完成')`在前端切换数字人形象,会出现两个问题
- 切换后,数字人说话,贴图会是最初始的数字人脸部,这是100多张切换到900多张数字人情况
- 切换失败,报错list index out of range,这是900多张切换到100多张数字人情况
Exception in thread Thread-3 (process_frames): Traceback (most recent call last): File "E:\tools\anaconda3\envs\nerfstream\lib\threading.py", line 1016, in _bootstrap_inner self.run() File "E:\tools\anaconda3\envs\nerfstream\lib\threading.py", line 953, in run self._target(*self._args, **self._kwargs) File "E:\LZ\project\LiveTalking\lipreal.py", line 227, in process_frames combine_frame = self.frame_list_cycle[idx] IndexError: list index out of range原因在lipreal.py中的render()函数中推理线程仍在用旧的图像进行推理。
解决:
- 停止当前运行的 inference 线程
- 更新 avatar 数据
- 启动新的 inference 线程使用新的 avatar 数据
我照你的方法实现了一遍,效果挺好的,就是会存在当数字人说话的时候去切换,会存在短暂的当前人物的脸上是上个人物的下半张脸。请问一下这个该怎么解决呢,我尝试让推理的线程完全结束再开启新的,还是会有这种
目标:在不重启后端的状况下,通过前端选择切换数字人 参考 这个页面
LipReal类中添加方法如下: ` def change_avatar(self, avatar): self.frame_list_cycle, self.face_list_cycle, self.coord_list_cycle = avatar
# 当切换数字人形象时,旧形象相关的帧数据不再有用 while not self.res_frame_queue.empty(): try: self.res_frame_queue.get_nowait() except queue.Empty: break logger.info('数字人形象切换完成')`在前端切换数字人形象,会出现两个问题
- 切换后,数字人说话,贴图会是最初始的数字人脸部,这是100多张切换到900多张数字人情况
- 切换失败,报错list index out of range,这是900多张切换到100多张数字人情况
Exception in thread Thread-3 (process_frames): Traceback (most recent call last): File "E:\tools\anaconda3\envs\nerfstream\lib\threading.py", line 1016, in _bootstrap_inner self.run() File "E:\tools\anaconda3\envs\nerfstream\lib\threading.py", line 953, in run self._target(*self._args, **self._kwargs) File "E:\LZ\project\LiveTalking\lipreal.py", line 227, in process_frames combine_frame = self.frame_list_cycle[idx] IndexError: list index out of range原因在lipreal.py中的render()函数中推理线程仍在用旧的图像进行推理。
解决:
- 停止当前运行的 inference 线程
- 更新 avatar 数据
- 启动新的 inference 线程使用新的 avatar 数据
我照你的方法实现了一遍,效果挺好的,就是会存在当数字人说话的时候去切换,会存在短暂的当前人物的脸上是上个人物的下半张脸。请问一下这个该怎么解决呢,我尝试让推理的线程完全结束再开启新的,还是会有这种情况。
老哥想问一下,我想实现的每个窗口的数字人互不干扰,每个端口都能自由切换各自的数字人这个有思路么?