前言
- 目前數(shù)字人實現(xiàn)技術(shù)眾多,我這里采用基于ER-NeRF,在這里可以看到其介紹:ICCV 2023 | ER-NeRF: 用于合成高保真Talking Portrait的高效區(qū)域感知神經(jīng)輻射場-https://zhuanlan.zhihu.com/p/644520609
- ER-NeRF的項目地址:https://github.com/Fictionarry/ER-NeRF
- ER-NeRF,RAD-NeRF,他們都繼承自AD-NeRF,都有一個基于dearpygui的GUI界面
- 但是很遺憾,這個GUI很難跑起來,而且本地一般沒有大GPU的機(jī)器,我們需要一個在云GPU服務(wù)器上能跑的webui
- ER-NeRF訓(xùn)練很簡單,所需素材也很少,訓(xùn)練的步驟不需要GUI
- 推理時,需要一個推理界面,方便一般用戶使用的同時,使用UI界面能實現(xiàn)一邊推理一邊播放視頻,優(yōu)化用戶體驗
- 基于此,在調(diào)研一圈之后,計劃使用Gradio來構(gòu)建webui,改造推理代碼,推理生成的幀圖像直接存儲為ts格式視頻,web前端使用hls協(xié)議來加載m3u8文件,流式的播放推理出的結(jié)果
最終效果
- 運(yùn)行圖
- 推理圖
實現(xiàn)步驟
Gradio
很常規(guī)的操作,一個左右分欄布局:
with gr.Blocks() as page:
with gr.Row():
with gr.Column():
model = gr.Dropdown(
choices=models, value=models[0], label="選擇模型", elem_id="modelSelectDom"
)
audType = gr.Dropdown(
choices=['deepspeech', 'hubert', 'esperanto'], value='deepspeech', label="模型音頻處理方式"
)
with gr.Tab('錄音'):
audio1 = gr.Audio(source="microphone", label='如果不能正常錄音請直接上傳音頻文件!')
with gr.Tab('上傳錄音'):
audio2 = gr.File(label='上傳錄音文件', file_types=['audio'])
btn = gr.Button("提交", variant="primary", elem_id="submitBtn")
with gr.Column():
msg = gr.Label(label='運(yùn)行狀態(tài)', elem_id="logShowDiv", value='')
gr.Label(label='推理視頻', elem_id="resultVideoDiv", value='')
btn.click(
action,
inputs=[
model, audType, audio1, audio2
],
outputs=[msg],
)
可以看到,output配置了一個msg的label組件,就是用來顯示服務(wù)器現(xiàn)在運(yùn)行的日志信息的。
那么本項目第一個問題就是:如何實時的顯示服務(wù)器運(yùn)行日志呢?
看代碼:
def log_out(new_log):
print(new_log)
return new_log
def action(model, audType, audio1, audio2):
# 存儲音頻文件
yield log_out('存儲音頻文件...')
wavFilePath = os.path.join(modelBasePath, model, str(time.time()).replace('.', '') + '.wav')
if audio1:
rate, data = audio1
write(wavFilePath, rate, data.astype(np.int32))
elif audio2:
suffix = audio2.name.split('.')[-1]
shutil.copy2(audio2.name, wavFilePath.replace('.wav', '.' + suffix))
if not os.path.exists(wavFilePath):
yield log_out('存儲音頻文件失?。?)
else:
yield log_out('存儲音頻文件完成.')
# 執(zhí)行音頻預(yù)處理
yield log_out('音頻預(yù)處理開始...')
if audType == 'deepspeech':
cmd = f'python data_utils/deepspeech_features/extract_ds_features.py --input {wavFilePath}'
elif audType == 'hubert':
cmd = f'python data_utils/hubert.py --wav {wavFilePath}'
else:
cmd = f'python data_utils/wav2vec.py --wav {wavFilePath} --save_feats'
yield log_out(f'命令:{cmd}')
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while True:
output = process.stdout.readline()
if output == b'' and process.poll() is not None:
break
if output:
yield log_out(output.strip().decode('utf-8'))
time.sleep(0.5)
process.wait()
yield log_out(f'音頻預(yù)處理完成.')
# 確認(rèn)音頻預(yù)處理是否完成
npyPath = '.'.join(wavFilePath.split('.')[:-1]) + '.npy'
stop = False
if not os.path.exists(npyPath):
yield log_out(f'未找到音頻預(yù)處理后的npy文件,程序?qū)⒁顺觯?)
stop = True
if stop:
return
# 構(gòu)建推理命令
yield log_out(f'準(zhǔn)備執(zhí)行推理...')
cmd = f'python main.py {os.path.join(modelBasePath, model)} --workspace trial_{model}_torso -O --torso --test --test_train --aud {npyPath} --smooth_path --fps 25'
yield log_out(f'推理命令:{cmd}')
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while True:
output = process.stdout.readline()
if output == b'' and process.poll() is not None:
break
if output:
yield log_out(output.strip().decode('utf-8'))
time.sleep(0.5)
process.wait()
可以看到,直接使用yield關(guān)鍵字,就可以讓服務(wù)器的輸出多次響應(yīng)。
但是這樣操作最終看到的界面效果就是日志隨著一次yield一次變化,歷史的累計日志信息都被直接覆蓋了。
為了讓輸出能夠累計歷史日志信息一起顯示,我們需要將日志記錄下來,這個也很簡單,增加一個history_log即可:
history_log=''
def log_out(new_log):
global history_log
history_log += new_log+'<br>'
print(new_log)
return history_log
.......
現(xiàn)在看到,日志確實累計輸出了,顯示效果卻不夠好看,而且每次輸出一次日志就會頁面組件就會重繪,日志過多也影響服務(wù)器內(nèi)存。
有沒有辦法做成shell命令窗那種類似效果呢,日志輸出時,滾動條在底部,永遠(yuǎn)保持當(dāng)前輸出的日志能夠可視?
一番艱苦的探尋,終于找到了解決辦法。
核心思路是:yield持續(xù)輸出,頁面中用一個input元素接收,然后重寫input的setvalue的方法,在方法中提取到本次輸出的日志值,然后將值添加到一個div尾部,使用js讓div的滾動條保持在底部。
核心代碼:文章來源:http://www.zghlxwxcb.cn/news/detail-751494.html
_script = '''
async()=>{
.......
//監(jiān)控日志輸出及顯示
let output = document.querySelector("#logDivText .border-none");
if(!output){
return false;
}
let show = document.querySelector('#logShowDiv .container')
show.style.height='200px'
show.style.overflowY='scroll'
show.innerHTML=""
Object.defineProperty(output, "value", {
set: function (log) {
if(log && log!=''){
show.innerHTML = show.innerHTML+'<br>'+log
show.scrollTop=show.scrollHeight
}
}
return this.textContent = log;
}
});
......
}
'''
#在page頁面加載的時候,將自定義的js加載進(jìn)去
page.load(_js=_script)
這樣就實現(xiàn)了監(jiān)控服務(wù)器日志輸出的效果了,效果如下:
代碼已放在gitee,有不解的可私信。
下一篇講解如何將內(nèi)存中的序列圖通過pipeline寫成hls協(xié)議的ts文件保存。文章來源地址http://www.zghlxwxcb.cn/news/detail-751494.html
到了這里,關(guān)于給數(shù)字人生成加上界面,基于ER-NeRF/RAD-NeRF/AD-NeRF,Gradio框架構(gòu)建WEBUI,使用HLS流媒體,實現(xiàn)邊推理邊播放——之一:在WEBUI中實時輸出服務(wù)器控制臺日志的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!