Merge pull request #1 from yuyuyzl/dev

Dev
This commit is contained in:
yuzhongliang22 authored and GitHub committed 2022-05-27 14:25:57 +08:00
commit ece67bb57b
9 files changed
+656 -116

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021 Gunwoo Han
Copyright (c) 2021 Gunwoo Han, yuyuyzl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+16
View File
@@ -9,6 +9,8 @@ Fork自 https://github.com/GunwooHan/EasyVtuber
并且省略了ifacialmocap pc端,通过UDP直连的方式使ios面捕刷新率达到30fps,解决了面捕刷新率的瓶颈
最后,将EasyVtuber中使用的OBS虚拟摄像头方案切换为Unity Capture,解锁RGBA输出能力,无需绿背即可直接使用
[视频介绍和安装说明](https://www.bilibili.com/video/BV1uu411r7DR)
## Requirements
### 硬件
@@ -87,6 +89,20 @@ https://github.com/pkhungurn/talking-head-anime-2-demo#download-the-model
### 输入输出设备
#### OBS Virtual Camera
目前更推荐这个方案,UnityCapture存在未查明的性能瓶颈
如果你选择自己进行抠像你可以直接输出到obs,如果你需要RGBA支持则需要额外使用一个Shader
下载并安装StreamFX https://github.com/Xaymar/obs-StreamFX
下载Shader(感谢树根的协助) https://github.com/shugen002/shader/blob/master/merge%20alpha2.hlsl
之后,使用`--alpha_split`参数运行
![alpha split](assets/alphasplit.png)
你会看到这样的输出画面,透明通道单独使用灰度方式发送了
之后对OBS中的视频采集设备添加滤镜-着色器-选择你下载的`merge alpha2.hlsl`-关闭
这样透明通道就应用回左边的图像了
你可能需要手动调整一下裁剪把右侧的无用画面切掉
(看不到着色器滤镜的话就是StreamFX没装好或者OBS不是最新版)
#### UnityCapture
如果需要使用透明通道输出,参考 https://github.com/schellingb/UnityCapture#installation 安装好UnityCapture
+41
View File
@@ -0,0 +1,41 @@
import argparse
import re
def convert_to_byte(size):
result = re.search('(\d+\.?\d*)(b|kb|mb|gb|tb)', size.lower())
if (result and result.groups()):
unit = result.groups()[1]
amount = float(result.groups()[0])
index = ['b', 'kb', 'mb', 'gb', 'tb'].index(unit)
return amount * pow(1024, index)
raise ValueError("Invalid size provided, value is " + size)
parser = argparse.ArgumentParser()
parser.add_argument('--debug', action='store_true')
parser.add_argument('--extend_movement', type=float)
parser.add_argument('--input', type=str, default='cam')
parser.add_argument('--character', type=str, default='y')
parser.add_argument('--output_dir', type=str)
parser.add_argument('--output_webcam', type=str)
parser.add_argument('--output_size', type=str, default='256x256')
parser.add_argument('--debug_input', action='store_true')
parser.add_argument('--perf', action='store_true')
parser.add_argument('--skip_model', action='store_true')
parser.add_argument('--ifm', type=str)
parser.add_argument('--anime4k', action='store_true')
parser.add_argument('--alpha_split', action='store_true')
parser.add_argument('--cache', type=str, default='256mb')
parser.add_argument('--gpu_cache', type=str, default='512mb')
parser.add_argument('--simplify', type=int, default=1)
args = parser.parse_args()
args.output_w = int(args.output_size.split('x')[0])
args.output_h = int(args.output_size.split('x')[1])
if args.cache is not None:
args.max_cache_len=int(convert_to_byte(args.cache)/262144)
else:
args.max_cache_len=0
if args.gpu_cache is not None:
args.max_gpu_cache_len=int(convert_to_byte(args.gpu_cache)/589824)
else:
args.max_gpu_cache_len=0
if args.output_webcam is None and args.output_dir is None: args.debug = True
Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

+48 -7
View File
@@ -6,20 +6,26 @@ import tkinter as tk
from tkinter import ttk
import json
try:
f = open('launcher.json')
args = json.load(f)
f.close()
except:
args = {
default_arg={
'character': 'y',
'input': 2,
'output': 2,
'ifm': None,
'is_extend_movement': False,
'is_anime4k': False,
'is_alpha_split': False,
'cache_simplify': 1,
'cache_size': 1,
}
try:
f = open('launcher.json')
args = json.load(f)
default_arg.update(args)
f.close()
finally:
args = default_arg
p = None
characterList = []
@@ -52,6 +58,25 @@ ttk.Label(launcher, text="iFacialMocap IP:Port").pack(fill='x', expand=True)
ifm = tk.StringVar(value=args['ifm'])
ttk.Entry(launcher, textvariable=ifm, state=False).pack(fill='x', expand=True)
ttk.Label(launcher, text="Facial Input Simplify").pack(fill='x', expand=True)
cache_simplify = tk.IntVar(value=args['cache_simplify'])
ttk.Radiobutton(launcher, text='Off', value=0, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Radiobutton(launcher, text='Low', value=1, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Radiobutton(launcher, text='Medium', value=2, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Radiobutton(launcher, text='High', value=3, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Radiobutton(launcher, text='Higher', value=4, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Radiobutton(launcher, text='Highest', value=6, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Radiobutton(launcher, text='Gaming', value=8, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Label(launcher, text="Cache Size (RAM+VRAM)").pack(fill='x', expand=True)
cache_size = tk.IntVar(value=args['cache_size'])
ttk.Radiobutton(launcher, text='Off', value=0, variable=cache_size).pack(fill='x', expand=True)
ttk.Radiobutton(launcher, text='256M+128M', value=1, variable=cache_size).pack(fill='x', expand=True)
ttk.Radiobutton(launcher, text='1GB+512M', value=2, variable=cache_size).pack(fill='x', expand=True)
ttk.Radiobutton(launcher, text='2GB+1GB', value=3, variable=cache_size).pack(fill='x', expand=True)
ttk.Radiobutton(launcher, text='4GB+2GB', value=4, variable=cache_size).pack(fill='x', expand=True)
ttk.Radiobutton(launcher, text='8GB+4GB', value=5, variable=cache_size).pack(fill='x', expand=True)
ttk.Label(launcher, text="Extra Options").pack(fill='x', expand=True)
is_extend_movement = tk.BooleanVar(value=args['is_extend_movement'])
ttk.Checkbutton(launcher, text='Extend Movement', variable=is_extend_movement).pack(fill='x', expand=True)
@@ -59,6 +84,9 @@ ttk.Checkbutton(launcher, text='Extend Movement', variable=is_extend_movement).p
is_anime4k = tk.BooleanVar(value=args['is_anime4k'])
ttk.Checkbutton(launcher, text='Anime4K', variable=is_anime4k).pack(fill='x', expand=True)
is_alpha_split = tk.BooleanVar(value=args['is_alpha_split'])
ttk.Checkbutton(launcher, text='Alpha Split', variable=is_alpha_split).pack(fill='x', expand=True)
output = tk.IntVar(value=args['output'])
ttk.Label(launcher, text="Output").pack(fill='x', expand=True)
ttk.Radiobutton(launcher, text='Unity Capture', value=0, variable=output).pack(fill='x', expand=True)
@@ -76,6 +104,9 @@ def launch():
'ifm': ifm.get(),
'is_extend_movement': is_extend_movement.get(),
'is_anime4k': is_anime4k.get(),
'is_alpha_split': is_alpha_split.get(),
'cache_simplify': cache_simplify.get(),
'cache_size': cache_size.get(),
}
f = open('launcher.json', mode='w')
json.dump(args, f)
@@ -111,12 +142,22 @@ def launch():
run_args.append('--debug')
if args['is_anime4k']:
run_args.append('--anime4k')
if args['is_alpha_split']:
run_args.append('--alpha_split')
if args['is_extend_movement']:
run_args.append('--extend_movement')
run_args.append('1')
if args['cache_simplify'] is not None:
run_args.append('--simplify')
run_args.append(str(args['cache_simplify']))
if args['cache_size'] is not None:
run_args.append('--cache')
run_args.append(['0b','256mb','1gb','2gb','4gb','8gb'][args['cache_size']])
run_args.append('--gpu_cache')
run_args.append(['0b','128mb','512mb','1gb','2gb','4gb'][args['cache_size']])
run_args.append('--output_size')
run_args.append('512x512')
print(run_args)
p = subprocess.Popen(run_args)
launch_btn.config(text='Stop')
+336 -79
View File
@@ -1,5 +1,3 @@
import argparse
import cv2
import torch
import pyvirtualcam
@@ -19,30 +17,32 @@ import queue
import socket
import time
import math
import re
from collections import OrderedDict
from multiprocessing import Value, Process, Queue
import pyanime4k
from pyanime4k import ac
from tha2.mocap.ifacialmocap_constants import *
parser = argparse.ArgumentParser()
parser.add_argument('--debug', action='store_true')
parser.add_argument('--extend_movement', type=float)
parser.add_argument('--input', type=str, default='cam')
parser.add_argument('--character', type=str, default='y')
parser.add_argument('--output_dir', type=str)
parser.add_argument('--output_webcam', type=str)
parser.add_argument('--output_size', type=str, default='256x256')
parser.add_argument('--debug_input', action='store_true')
parser.add_argument('--ifm', type=str)
parser.add_argument('--anime4k', action='store_true')
args = parser.parse_args()
args.output_w = int(args.output_size.split('x')[0])
args.output_h = int(args.output_size.split('x')[1])
if args.output_webcam is None and args.output_dir is None: args.debug = True
from args import args
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
import collections
class FPS:
def __init__(self, avarageof=50):
self.frametimestamps = collections.deque(maxlen=avarageof)
def __call__(self):
self.frametimestamps.append(time.time())
if len(self.frametimestamps) > 1:
return len(self.frametimestamps) / (self.frametimestamps[-1] - self.frametimestamps[0])
else:
return 0.0
device = torch.device('cuda') if torch.cuda.is_available() and not args.skip_model else torch.device('cpu')
def create_default_blender_data():
@@ -69,13 +69,17 @@ def create_default_blender_data():
return data
class ClientProcess(Process):
ifm_converter = tha2.poser.modes.mode_20_wx.IFacialMocapPoseConverter20()
class IFMClientProcess(Process):
def __init__(self):
super().__init__()
self.queue = Queue()
self.should_terminate = Value('b', False)
self.address = args.ifm.split(':')[0]
self.port = int(args.ifm.split(':')[1])
self.ifm_fps_number = Value('f', 0.0)
self.perf_time = 0
def run(self):
@@ -92,6 +96,8 @@ class ClientProcess(Process):
self.socket.setblocking(False)
self.socket.bind(("", self.port))
self.socket.settimeout(0.1)
ifm_fps = FPS()
pre_socket_string = ''
while True:
if self.should_terminate.value:
break
@@ -104,14 +110,13 @@ class ClientProcess(Process):
else:
raise e
socket_string = socket_bytes.decode("utf-8")
if args.debug and pre_socket_string != socket_string:
self.ifm_fps_number.value = ifm_fps()
pre_socket_string = socket_string
# print(socket_string)
# blender_data = json.loads(socket_string)
data = self.convert_from_blender_data(socket_string)
# cur_time = time.perf_counter()
# fps = 1 / (cur_time - self.perf_time)
# self.perf_time = cur_time
# print(fps)
try:
self.queue.put_nowait(data)
except queue.Full:
@@ -148,12 +153,200 @@ class ClientProcess(Process):
return data
class ModelClientProcess(Process):
def __init__(self, input_image):
super().__init__()
self.should_terminate = Value('b', False)
self.updated = Value('b', False)
self.data = None
self.input_image = input_image
self.output_queue = Queue()
self.input_queue = Queue()
self.model_fps_number = Value('f', 0.0)
self.gpu_fps_number = Value('f', 0.0)
self.cache_hit_ratio = Value('f', 0.0)
self.gpu_cache_hit_ratio= Value('f', 0.0)
def run(self):
model = None
if not args.skip_model:
model = TalkingAnimeLight().to(device)
model = model.eval()
model = model
print("Pretrained Model Loaded")
mouth_eye_vector = torch.empty(1, 27)
pose_vector = torch.empty(1, 3)
input_image = self.input_image.to(device)
mouth_eye_vector = mouth_eye_vector.to(device)
pose_vector = pose_vector.to(device)
model_cache = OrderedDict()
tot = 0
hit = 0
model_fps = FPS()
gpu_fps = FPS()
while True:
model_input = None
try:
while not self.input_queue.empty():
model_input = self.input_queue.get_nowait()
except queue.Empty:
continue
if model_input is None: continue
simplify_arr = [1000] * ifm_converter.pose_size
if args.simplify >= 1:
simplify_arr = [200] * ifm_converter.pose_size
simplify_arr[ifm_converter.eye_wink_left_index] = 50
simplify_arr[ifm_converter.eye_wink_right_index] = 50
simplify_arr[ifm_converter.eye_happy_wink_left_index] = 50
simplify_arr[ifm_converter.eye_happy_wink_right_index] = 50
simplify_arr[ifm_converter.eye_surprised_left_index] = 30
simplify_arr[ifm_converter.eye_surprised_right_index] = 30
simplify_arr[ifm_converter.iris_rotation_x_index] = 25
simplify_arr[ifm_converter.iris_rotation_y_index] = 25
simplify_arr[ifm_converter.eye_raised_lower_eyelid_left_index] = 10
simplify_arr[ifm_converter.eye_raised_lower_eyelid_right_index] = 10
simplify_arr[ifm_converter.mouth_lowered_corner_left_index] = 5
simplify_arr[ifm_converter.mouth_lowered_corner_right_index] = 5
simplify_arr[ifm_converter.mouth_raised_corner_left_index] = 5
simplify_arr[ifm_converter.mouth_raised_corner_right_index] = 5
if args.simplify >= 2:
simplify_arr[ifm_converter.head_x_index] = 100
simplify_arr[ifm_converter.head_y_index] = 100
simplify_arr[ifm_converter.eye_surprised_left_index] = 10
simplify_arr[ifm_converter.eye_surprised_right_index] = 10
model_input[ifm_converter.eye_wink_left_index - 12] += model_input[
ifm_converter.eye_happy_wink_left_index - 12]
model_input[ifm_converter.eye_happy_wink_left_index - 12] = model_input[
ifm_converter.eye_wink_left_index - 12] / 2
model_input[ifm_converter.eye_wink_left_index - 12] = model_input[
ifm_converter.eye_wink_left_index - 12] / 2
model_input[ifm_converter.eye_wink_right_index - 12] += model_input[
ifm_converter.eye_happy_wink_right_index - 12]
model_input[ifm_converter.eye_happy_wink_right_index - 12] = model_input[
ifm_converter.eye_wink_right_index - 12] / 2
model_input[ifm_converter.eye_wink_right_index - 12] = model_input[
ifm_converter.eye_wink_right_index - 12] / 2
uosum = model_input[ifm_converter.mouth_uuu_index - 12] + \
model_input[ifm_converter.mouth_ooo_index - 12]
model_input[ifm_converter.mouth_ooo_index - 12] = uosum
model_input[ifm_converter.mouth_uuu_index - 12] = 0
is_open = (model_input[ifm_converter.mouth_aaa_index - 12] + model_input[
ifm_converter.mouth_iii_index - 12] + uosum) > 0
model_input[ifm_converter.mouth_lowered_corner_left_index - 12] = 0
model_input[ifm_converter.mouth_lowered_corner_right_index - 12] = 0
model_input[ifm_converter.mouth_raised_corner_left_index - 12] = 0.5 if is_open else 0
model_input[ifm_converter.mouth_raised_corner_right_index - 12] = 0.5 if is_open else 0
simplify_arr[ifm_converter.mouth_lowered_corner_left_index] = 0
simplify_arr[ifm_converter.mouth_lowered_corner_right_index] = 0
simplify_arr[ifm_converter.mouth_raised_corner_left_index] = 0
simplify_arr[ifm_converter.mouth_raised_corner_right_index] = 0
if args.simplify >= 3:
simplify_arr[ifm_converter.iris_rotation_x_index] = 20
simplify_arr[ifm_converter.iris_rotation_y_index] = 20
simplify_arr[ifm_converter.eye_wink_left_index] = 32
simplify_arr[ifm_converter.eye_wink_right_index] = 32
simplify_arr[ifm_converter.eye_happy_wink_left_index] = 32
simplify_arr[ifm_converter.eye_happy_wink_right_index] = 32
if args.simplify >= 4:
simplify_arr[ifm_converter.head_x_index] = 50
simplify_arr[ifm_converter.head_y_index] = 50
simplify_arr[ifm_converter.neck_z_index] = 100
model_input[ifm_converter.eye_raised_lower_eyelid_left_index - 12] = 0
model_input[ifm_converter.eye_raised_lower_eyelid_right_index - 12] = 0
simplify_arr[ifm_converter.iris_rotation_x_index] = 10
simplify_arr[ifm_converter.iris_rotation_y_index] = 10
simplify_arr[ifm_converter.eye_wink_left_index] = 24
simplify_arr[ifm_converter.eye_wink_right_index] = 24
simplify_arr[ifm_converter.eye_happy_wink_left_index] = 24
simplify_arr[ifm_converter.eye_happy_wink_right_index] = 24
simplify_arr[ifm_converter.eye_surprised_left_index] = 8
simplify_arr[ifm_converter.eye_surprised_right_index] = 8
model_input[ifm_converter.eye_wink_left_index - 12] += model_input[
ifm_converter.eye_wink_right_index - 12]
model_input[ifm_converter.eye_wink_right_index - 12] = model_input[
ifm_converter.eye_wink_left_index - 12] / 2
model_input[ifm_converter.eye_wink_left_index - 12] = model_input[
ifm_converter.eye_wink_left_index - 12] / 2
model_input[ifm_converter.eye_surprised_left_index - 12] += model_input[
ifm_converter.eye_surprised_right_index - 12]
model_input[ifm_converter.eye_surprised_right_index - 12] = model_input[
ifm_converter.eye_surprised_left_index - 12] / 2
model_input[ifm_converter.eye_surprised_left_index - 12] = model_input[
ifm_converter.eye_surprised_left_index - 12] / 2
model_input[ifm_converter.eye_happy_wink_left_index - 12] += model_input[
ifm_converter.eye_happy_wink_right_index - 12]
model_input[ifm_converter.eye_happy_wink_right_index - 12] = model_input[
ifm_converter.eye_happy_wink_left_index - 12] / 2
model_input[ifm_converter.eye_happy_wink_left_index - 12] = model_input[
ifm_converter.eye_happy_wink_left_index - 12] / 2
model_input[ifm_converter.mouth_aaa_index - 12] = min(
model_input[ifm_converter.mouth_aaa_index - 12] +
model_input[ifm_converter.mouth_ooo_index - 12] / 2 +
model_input[ifm_converter.mouth_iii_index - 12] / 2 +
model_input[ifm_converter.mouth_uuu_index - 12] / 2, 1
)
model_input[ifm_converter.mouth_ooo_index - 12] = 0
model_input[ifm_converter.mouth_iii_index - 12] = 0
model_input[ifm_converter.mouth_uuu_index - 12] = 0
for i in range(4, args.simplify):
simplify_arr = [max(math.ceil(x * 0.8), 5) for x in simplify_arr]
for i in range(12, len(simplify_arr)):
if simplify_arr[i] > 0:
model_input[i - 12] = round(model_input[i - 12] * simplify_arr[i]) / simplify_arr[i]
input_hash = hash(tuple(model_input))
cached = model_cache.get(input_hash)
tot += 1
mouth_eye_vector_c=[0.0]*27
if not cached is None:
self.output_queue.put_nowait(cached)
model_cache.move_to_end(input_hash)
hit += 1
else:
if args.perf:
tic = time.perf_counter()
for i in range(27):
mouth_eye_vector[0, i] = model_input[i]
mouth_eye_vector_c[i]=model_input[i]
for i in range(3):
pose_vector[0, i] = model_input[i + 27]
if model is None:
output_image = input_image
else:
output_image = model(input_image, mouth_eye_vector, pose_vector, mouth_eye_vector_c,self.gpu_cache_hit_ratio)
if args.perf:
torch.cuda.synchronize()
print("model", (time.perf_counter() - tic) * 1000)
tic = time.perf_counter()
postprocessed_image = output_image.cpu()
if args.perf:
print("cpu()", (time.perf_counter() - tic) * 1000)
tic = time.perf_counter()
postprocessed_image = postprocessing_image(postprocessed_image)
if args.perf:
print("postprocess", (time.perf_counter() - tic) * 1000)
tic = time.perf_counter()
self.output_queue.put_nowait(postprocessed_image)
if args.debug:
self.gpu_fps_number.value = gpu_fps()
if args.max_cache_len > 0:
model_cache[input_hash] = postprocessed_image
if len(model_cache) > args.max_cache_len:
model_cache.popitem(last=False)
if args.debug:
self.model_fps_number.value = model_fps()
self.cache_hit_ratio.value = hit / tot
@torch.no_grad()
def main():
model = TalkingAnimeLight().to(device)
model = model.eval()
model = model
print("Pretrained Model Loaded")
img = Image.open(f"character/{args.character}.png")
wRatio = img.size[0] / 256
img = img.resize((256, int(img.size[1] / wRatio)))
@@ -163,17 +356,16 @@ def main():
extra_image = np.array(img.crop((0, 256, img.size[0], img.size[1])))
print("Character Image Loaded:", args.character)
ifm_converter = None
cap = None
output_fps = FPS()
if not args.debug_input:
if args.ifm is not None:
client_process = ClientProcess()
client_process = IFMClientProcess()
client_process.daemon = True
client_process.start()
ifm_converter = tha2.poser.modes.mode_20_wx.create_ifacialmocap_pose_converter()
print("IFM Service Running:", args.ifm)
else:
@@ -193,9 +385,13 @@ def main():
if args.output_webcam:
cam_scale = 1
cam_width_scale = 1
if args.anime4k:
cam_scale = 2
cam = pyvirtualcam.Camera(width=args.output_w * cam_scale, height=args.output_h * cam_scale, fps=30,
if args.alpha_split:
cam_width_scale = 2
cam = pyvirtualcam.Camera(width=args.output_w * cam_scale * cam_width_scale, height=args.output_h * cam_scale,
fps=60,
backend=args.output_webcam,
fmt=
{'unitycapture': pyvirtualcam.PixelFormat.RGBA, 'obs': pyvirtualcam.PixelFormat.RGB}[
@@ -221,21 +417,15 @@ def main():
a.set_arguments(parameters)
print("Anime4K Loaded")
mouth_eye_vector = torch.empty(1, 27)
pose_vector = torch.empty(1, 3)
# input_image = input_image.half()
# mouth_eye_vector = mouth_eye_vector.half()
# pose_vector = pose_vector.half()
input_image = input_image.to(device)
mouth_eye_vector = mouth_eye_vector.to(device)
pose_vector = pose_vector.to(device)
position_vector = [0, 0, 0, 1]
pose_queue = []
blender_data = create_default_blender_data()
tic=0
model_output = None
model_process = ModelClientProcess(input_image)
model_process.daemon = True
model_process.start()
print("Ready. Close this console to exit.")
@@ -244,21 +434,24 @@ def main():
# input_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# results = facemesh.process(input_frame)
if args.perf:
tic = time.perf_counter()
if args.debug_input:
mouth_eye_vector[0, :] = 0
pose_vector[0, :] = 0
mouth_eye_vector_c = [0.0] * 27
pose_vector_c = [0.0] * 3
mouth_eye_vector[0, 2] = math.sin(time.perf_counter()*3)
mouth_eye_vector[0, 3] = math.sin(time.perf_counter()*3)
mouth_eye_vector_c[2] = math.sin(time.perf_counter() * 3)
mouth_eye_vector_c[3] = math.sin(time.perf_counter() * 3)
mouth_eye_vector[0, 14] = 0
mouth_eye_vector_c[14] = 0
mouth_eye_vector[0, 25] = math.sin(time.perf_counter()*2.2)*0.2
mouth_eye_vector[0, 26] = math.sin(time.perf_counter()*3.5)*0.8
mouth_eye_vector_c[25] = math.sin(time.perf_counter() * 2.2) * 0.2
mouth_eye_vector_c[26] = math.sin(time.perf_counter() * 3.5) * 0.8
pose_vector_c[0] = math.sin(time.perf_counter() * 1.1)
pose_vector_c[1] = math.sin(time.perf_counter() * 1.2)
pose_vector_c[2] = math.sin(time.perf_counter() * 1.5)
pose_vector[0, 0] = math.sin(time.perf_counter()*1.1)
pose_vector[0, 1] = math.sin(time.perf_counter()*1.2)
pose_vector[0, 2] = math.sin(time.perf_counter()*1.5)
elif args.ifm is not None:
@@ -292,11 +485,15 @@ def main():
# - ifacialmocap_pose[EYE_LOOK_DOWN_RIGHT]
# + ifacialmocap_pose[EYE_LOOK_DOWN_LEFT]) / 2.0 / 0.75
position_vector = blender_data[HEAD_BONE_QUAT]
mouth_eye_vector_c = [0.0] * 27
pose_vector_c = [0.0] * 3
for i in range(12, 39):
mouth_eye_vector[0, i - 12] = ifacialmocap_pose_converted[i]
mouth_eye_vector_c[i - 12] = ifacialmocap_pose_converted[i]
for i in range(39, 42):
pose_vector[0, i - 39] = ifacialmocap_pose_converted[i]
pose_vector_c[i - 39] = ifacialmocap_pose_converted[i]
position_vector = blender_data[HEAD_BONE_QUAT]
else:
ret, frame = cap.read()
@@ -332,30 +529,57 @@ def main():
y_angle = np_pose[6]
z_angle = np_pose[7]
mouth_eye_vector[0, :] = 0
pose_vector[0, :] = 0
mouth_eye_vector_c = [0.0] * 27
pose_vector_c = [0.0] * 3
mouth_eye_vector[0, 2] = eye_l_h_temp
mouth_eye_vector[0, 3] = eye_r_h_temp
mouth_eye_vector_c[2] = eye_l_h_temp
mouth_eye_vector_c[3] = eye_r_h_temp
mouth_eye_vector[0, 14] = mouth_ratio * 1.5
mouth_eye_vector_c[14] = mouth_ratio * 1.5
mouth_eye_vector[0, 25] = eye_y_ratio
mouth_eye_vector[0, 26] = eye_x_ratio
mouth_eye_vector_c[25] = eye_y_ratio
mouth_eye_vector_c[26] = eye_x_ratio
pose_vector[0, 0] = (x_angle - 1.5) * 1.6
pose_vector[0, 1] = y_angle * 2.0 # temp weight
pose_vector[0, 2] = (z_angle + 1.5) * 2 # temp weight
pose_vector_c[0] = (x_angle - 1.5) * 1.6
pose_vector_c[1] = y_angle * 2.0 # temp weight
pose_vector_c[2] = (z_angle + 1.5) * 2 # temp weight
model_input_arr = mouth_eye_vector_c
model_input_arr.extend(pose_vector_c)
model_process.input_queue.put_nowait(model_input_arr)
has_model_output = 0
try:
new_model_output = model_output
while not model_process.output_queue.empty():
has_model_output += 1
new_model_output = model_process.output_queue.get_nowait()
model_output = new_model_output
except queue.Empty:
pass
if model_output is None:
time.sleep(1)
continue
# print(has_model_output)
# should_output=should_output or has_model_output
# if not should_output:
# continue
postprocessed_image = model_output
if args.perf:
print('===')
print("input", time.perf_counter() - tic)
tic = time.perf_counter()
output_image = model(input_image, mouth_eye_vector, pose_vector)
postprocessed_image = postprocessing_image(output_image.cpu())
if extra_image is not None:
postprocessed_image = cv2.vconcat([postprocessed_image, extra_image])
k_scale=1
rotate_angle=0
dx=0
dy=0
k_scale = 1
rotate_angle = 0
dx = 0
dy = 0
if args.extend_movement is not None:
k_scale = position_vector[2] * math.sqrt(args.extend_movement) + 1
rotate_angle = -position_vector[0] * 40 * args.extend_movement
@@ -370,6 +594,12 @@ def main():
rm,
(args.output_w, args.output_h))
if args.perf:
print("extendmovement", (time.perf_counter() - tic) * 1000)
tic = time.perf_counter()
output_fps_number = output_fps()
if args.anime4k:
alpha_channel = postprocessed_image[:, :, 3]
alpha_channel = cv2.resize(alpha_channel, None, fx=2, fy=2)
@@ -383,15 +613,40 @@ def main():
postprocessed_image = a.save_image_to_numpy()
postprocessed_image = cv2.merge((postprocessed_image, alpha_channel))
postprocessed_image = cv2.cvtColor(postprocessed_image, cv2.COLOR_BGRA2RGBA)
if args.perf:
print("anime4k", (time.perf_counter() - tic) * 1000)
tic = time.perf_counter()
if args.alpha_split:
alpha_image = cv2.merge(
[postprocessed_image[:, :, 3], postprocessed_image[:, :, 3], postprocessed_image[:, :, 3]])
alpha_image = cv2.cvtColor(alpha_image, cv2.COLOR_RGB2RGBA)
postprocessed_image = cv2.hconcat([postprocessed_image, alpha_image])
if args.debug:
output_frame = cv2.cvtColor(postprocessed_image, cv2.COLOR_RGBA2BGRA)
# resized_frame = cv2.resize(output_frame, (np.min(debug_image.shape[:2]), np.min(debug_image.shape[:2])))
# output_frame = np.concatenate([debug_image, resized_frame], axis=1)
toc = time.perf_counter()
fps = 1 / (toc - tic)
tic = toc
cv2.putText(output_frame, str('%.1f' % fps),(0,16),cv2.FONT_HERSHEY_PLAIN,1,(0,255,0),1)
cv2.putText(output_frame, str('OUT_FPS:%.1f' % output_fps_number), (0, 16), cv2.FONT_HERSHEY_PLAIN, 1,
(0, 255, 0), 1)
if args.max_cache_len > 0:
cv2.putText(output_frame, str(
'GPU_FPS:%.1f / %.1f' % (model_process.model_fps_number.value, model_process.gpu_fps_number.value)),
(0, 32),
cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 0), 1)
else:
cv2.putText(output_frame, str(
'GPU_FPS:%.1f' % (model_process.model_fps_number.value)),
(0, 32),
cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 0), 1)
if args.ifm is not None:
cv2.putText(output_frame, str('IFM_FPS:%.1f' % client_process.ifm_fps_number.value), (0, 48),
cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 0), 1)
if args.max_cache_len > 0:
cv2.putText(output_frame, str('MEMCACHED:%.1f%%' % (model_process.cache_hit_ratio.value * 100)), (0, 64),
cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 0), 1)
if args.max_gpu_cache_len > 0:
cv2.putText(output_frame, str('GPUCACHED:%.1f%%' % (model_process.gpu_cache_hit_ratio.value * 100)), (0, 80),
cv2.FONT_HERSHEY_PLAIN, 1, (0, 255, 0), 1)
cv2.imshow("frame", output_frame)
# cv2.imshow("camera", debug_image)
cv2.waitKey(1)
@@ -404,7 +659,9 @@ def main():
result_image = cv2.cvtColor(result_image, cv2.COLOR_RGBA2RGB)
cam.send(result_image)
cam.sleep_until_next_frame()
if args.perf:
print("output", (time.perf_counter() - tic) * 1000)
tic = time.perf_counter()
if __name__ == '__main__':
+34 -2
View File
@@ -1,7 +1,12 @@
import time
import torch.nn as nn
from tha2.poser.modes.mode_20 import load_face_morpher, load_face_rotater, load_combiner
from args import args
from collections import OrderedDict
class TalkingAnimeLight(nn.Module):
def __init__(self):
@@ -9,13 +14,40 @@ class TalkingAnimeLight(nn.Module):
self.face_morpher = load_face_morpher('pretrained/face_morpher.pt')
self.two_algo_face_rotator = load_face_rotater('pretrained/two_algo_face_rotator.pt')
self.combiner = load_combiner('pretrained/combiner.pt')
self.face_cache = OrderedDict()
self.tot = 0
self.hit = 0
def forward(self, image, mouth_eye_vector, pose_vector):
def forward(self, image, mouth_eye_vector, pose_vector, mouth_eye_vector_c, ratio=None):
x = image.clone()
mouth_eye_morp_image = self.face_morpher(image[:, :, 32:224, 32:224], mouth_eye_vector)
if args.perf:
tic=time.perf_counter()
input_hash = hash(tuple(mouth_eye_vector_c))
cached = self.face_cache.get(input_hash)
self.tot+=1
if cached is None:
mouth_eye_morp_image = self.face_morpher(image[:, :, 32:224, 32:224], mouth_eye_vector)
self.face_cache[input_hash]=mouth_eye_morp_image.detach()
if len(self.face_cache) > args.max_gpu_cache_len:
self.face_cache.popitem(last=False)
else:
self.hit+=1
mouth_eye_morp_image=cached
self.face_cache.move_to_end(input_hash)
if args.debug and ratio is not None:
ratio.value=self.hit / self.tot
if args.perf:
print(" - face_morpher",(time.perf_counter()-tic)*1000)
tic=time.perf_counter()
x[:, :, 32:224, 32:224] = mouth_eye_morp_image
rotate_image = self.two_algo_face_rotator(x, pose_vector)[:2]
if args.perf:
print(" - rotator",(time.perf_counter()-tic)*1000)
tic=time.perf_counter()
output_image = self.combiner(rotate_image[0], rotate_image[1], pose_vector)
if args.perf:
print(" - combiner",(time.perf_counter()-tic)*1000)
tic=time.perf_counter()
return output_image
+174 -23
View File
@@ -1,27 +1,65 @@
from pyanime4k import ac
import pyanime4k
parameters = ac.Parameters()
# enable HDN for ACNet
parameters.HDN = True
a = ac.AC(
type=ac.ProcessorType.CPU_ACNet,
)
a.set_arguments(parameters)
# from pyanime4k import ac
# import pyanime4k
#
# parameters = ac.Parameters()
# # enable HDN for ACNet
# parameters.HDN = True
#
# a = ac.AC(
# type=ac.ProcessorType.CPU_ACNet,
# )
# a.set_arguments(parameters)
import cv2
#
# img = cv2.imread("character/test41.png")
#
# a.load_image_from_numpy(img,input_type=ac.AC_INPUT_BGR)
#
# a.get_processor_info()
#
# # start processing
# a.process()
#
# a.show_image()
img = cv2.imread("character/test41.png")
a.load_image_from_numpy(img,input_type=ac.AC_INPUT_BGR)
a.get_processor_info()
# start processing
a.process()
a.show_image()
# import numpy as np
# import zlib
# import bz2
# import lzma
# import time
#
# postprocessed_image=np.load('out.npy')
# tic=time.perf_counter()
# res=None
# for i in range(100):
# res=zlib.compress(postprocessed_image)
# print("zlib", len(res),(time.perf_counter() - tic) * 1000)
# tic = time.perf_counter()
# for i in range(100):
# zlib.decompress(res)
# print("zlib", len(res),(time.perf_counter() - tic) * 1000)
# tic = time.perf_counter()
# for i in range(100):
# res=bz2.compress(postprocessed_image)
# print("bz2",len(res), (time.perf_counter() - tic) * 1000)
# tic = time.perf_counter()
# for i in range(100):
# bz2.decompress(res)
# print("bz2", len(res),(time.perf_counter() - tic) * 1000)
# tic = time.perf_counter()
# for i in range(100):
# res=lzma.compress(postprocessed_image)
# print("lzma",len(res), (time.perf_counter() - tic) * 1000)
# tic = time.perf_counter()
# for i in range(100):
# lzma.decompress(res)
# print("lzma", len(res),(time.perf_counter() - tic) * 1000)
# tic = time.perf_counter()
#
#
# output_frame = cv2.cvtColor(postprocessed_image, cv2.COLOR_RGBA2BGRA)
# cv2.imshow("frame", output_frame)
# cv2.waitKey(100000)
# preview upscaled image
# img=a.save_image_to_numpy()
#
@@ -42,4 +80,117 @@ a.show_image()
# [1,2,3,4],
# ]
#
# print(cv2.merge((np.array(rgb),np.array(a))))
# print(cv2.merge((np.array(rgb),np.array(a))))
from multiprocessing import Value, Process, Queue
import queue
import socket
import errno
import time
import tha2.poser.modes.mode_20_wx
from tha2.mocap.ifacialmocap_constants import *
ifm='192.168.31.182:49983'
class ClientProcess(Process):
def __init__(self):
super().__init__()
self.queue = Queue()
self.should_terminate = Value('b', False)
self.address = ifm.split(':')[0]
self.port = int(ifm.split(':')[1])
self.perf_time = 0
self.ifm_converter = tha2.poser.modes.mode_20_wx.create_ifacialmocap_pose_converter()
self.a_min=None
self.a_max=None
def run(self):
udpClntSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
data = "iFacialMocap_sahuasouryya9218sauhuiayeta91555dy3719"
data = data.encode('utf-8')
udpClntSock.sendto(data, (self.address, self.port))
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket.setblocking(False)
self.socket.bind(("", self.port))
self.socket.settimeout(0.1)
while True:
if self.should_terminate.value:
break
try:
socket_bytes = self.socket.recv(8192)
except socket.error as e:
err = e.args[0]
if err == errno.EAGAIN or err == errno.EWOULDBLOCK or err == 'timed out':
continue
else:
raise e
socket_string = socket_bytes.decode("utf-8")
# print(socket_string)
# blender_data = json.loads(socket_string)
data = self.convert_from_blender_data(socket_string)
data=self.ifm_converter.convert(data)
# print(data)
if(self.a_max is None):
self.a_max=[x for x in data]
else:
self.a_max=[max(data[i],self.a_max[i]) for i in range(len(data))]
if(self.a_min is None):
self.a_min=[x for x in data]
else:
self.a_min=[min(data[i],self.a_min[i]) for i in range(len(data))]
print([[self.a_min[i],self.a_max[i]] for i in range(len(data))])
cur_time = time.perf_counter()
fps = 1 / (cur_time - self.perf_time)
self.perf_time = cur_time
# print(fps)
try:
self.queue.put_nowait(data)
except queue.Full:
pass
self.queue.close()
self.socket.close()
@staticmethod
def convert_from_blender_data(blender_data):
data = {}
for item in blender_data.split('|'):
if item.find('#') != -1:
k, arr = item.split('#')
arr = [float(n) for n in arr.split(',')]
data[k.replace("_L", "Left").replace("_R", "Right")] = arr
elif item.find('-') != -1:
k, v = item.split("-")
data[k.replace("_L", "Left").replace("_R", "Right")] = float(v) / 100
to_rad = 57.3
data[HEAD_BONE_X] = data["=head"][0] / to_rad
data[HEAD_BONE_Y] = data["=head"][1] / to_rad
data[HEAD_BONE_Z] = data["=head"][2] / to_rad
data[HEAD_BONE_QUAT] = [data["=head"][3], data["=head"][4], data["=head"][5], 1]
# print(data[HEAD_BONE_QUAT][2],min(data[EYE_BLINK_LEFT],data[EYE_BLINK_RIGHT]))
data[RIGHT_EYE_BONE_X] = data["rightEye"][0] / to_rad
data[RIGHT_EYE_BONE_Y] = data["rightEye"][1] / to_rad
data[RIGHT_EYE_BONE_Z] = data["rightEye"][2] / to_rad
data[LEFT_EYE_BONE_X] = data["leftEye"][0] / to_rad
data[LEFT_EYE_BONE_Y] = data["leftEye"][1] / to_rad
data[LEFT_EYE_BONE_Z] = data["leftEye"][2] / to_rad
return data
if __name__ == '__main__':
client_process = ClientProcess()
client_process.daemon = True
client_process.start()
while True:
pass
+6 -4
View File
@@ -86,7 +86,7 @@ class IFacialMocapPoseConverter20(IFacialMocapPoseConverter):
self.eyebrow_serious_left_index = pose_parameters.get_parameter_index("eyebrow_serious_left")
self.eyebrow_serious_right_index = pose_parameters.get_parameter_index("eyebrow_serious_right")
self.eye_surprised_left_index = pose_parameters.get_parameter_index("eye_surprised_left")
self.eye_surprised_left_index = pose_parameters.get_parameter_index("eye_surprised_left") # 0
self.eye_surprised_right_index = pose_parameters.get_parameter_index("eye_surprised_right")
self.eye_wink_left_index = pose_parameters.get_parameter_index("eye_wink_left")
self.eye_wink_right_index = pose_parameters.get_parameter_index("eye_wink_right")
@@ -95,13 +95,13 @@ class IFacialMocapPoseConverter20(IFacialMocapPoseConverter):
self.eye_relaxed_left_index = pose_parameters.get_parameter_index("eye_relaxed_left")
self.eye_relaxed_right_index = pose_parameters.get_parameter_index("eye_relaxed_right")
self.eye_raised_lower_eyelid_left_index = pose_parameters.get_parameter_index("eye_raised_lower_eyelid_left")
self.eye_raised_lower_eyelid_right_index = pose_parameters.get_parameter_index("eye_raised_lower_eyelid_right")
self.eye_raised_lower_eyelid_right_index = pose_parameters.get_parameter_index("eye_raised_lower_eyelid_right") # 9
self.iris_small_left_index = pose_parameters.get_parameter_index("iris_small_left")
self.iris_small_left_index = pose_parameters.get_parameter_index("iris_small_left")#10
self.iris_small_right_index = pose_parameters.get_parameter_index("iris_small_right")
self.iris_rotation_x_index = pose_parameters.get_parameter_index("iris_rotation_x")
self.iris_rotation_y_index = pose_parameters.get_parameter_index("iris_rotation_y")
self.iris_rotation_y_index = pose_parameters.get_parameter_index("iris_rotation_y")#13
self.head_x_index = pose_parameters.get_parameter_index("head_x")
self.head_y_index = pose_parameters.get_parameter_index("head_y")
@@ -270,9 +270,11 @@ class IFacialMocapPoseConverter20(IFacialMocapPoseConverter):
pose[self.mouth_iii_index] = restricted_decomp[1]
mouth_funnel_denom = self.args.mouth_funnel_max_value - self.args.mouth_funnel_min_value
ooo_alpha = clamp((mouth_funnel - self.args.mouth_funnel_min_value) / mouth_funnel_denom, 0.0, 1.0)
# ooo_alpha = 1
uo_value = clamp(restricted_decomp[2] + restricted_decomp[3], 0.0, 1.0)
pose[self.mouth_uuu_index] = uo_value * (1.0 - ooo_alpha)
pose[self.mouth_ooo_index] = uo_value * ooo_alpha
# print(pose[self.mouth_aaa_index:self.mouth_ooo_index+1])
# pose[28] = restricted_decomp[2]
# pose[30] = restricted_decomp[3]