feat:add blink and breath cycle

This commit is contained in:
zpeng11 committed 2025-12-30 12:53:17 -05:00
1 parent d9bb9d5a62
commit 90c2749bce
4 files changed
+61 -5

No files matched your search

+21 -1
View File
@@ -51,7 +51,9 @@ default_arg = {
'preset': 'Low',
'mouse_audio_input': False,
'audio_sensitivity': '0.02',
'audio_threshold': '10.0'
'audio_threshold': '10.0',
'blink_interval': '5.0',
'breath_cycle': 'inf'
}
try:
@@ -308,11 +310,18 @@ class LauncherPanel(wx.Panel):
addOption('mouse_audio_input', title='Audio Input', desc='启用WASAPI音频输入控制嘴部动作', type=1)
addOption('audio_sensitivity', title='Audio Sensitivity', desc='音频灵敏度,控制音频对嘴部动作的影响程度', type=2)
addOption('audio_threshold', title='Audio Threshold', desc='音频阈值,低于此值的音频将被忽略', type=2)
addOption('blink_interval', title='Blink Interval', desc='设置眨眼间隔时间',
choices=['No Blink', '3 seconds', '5 seconds', '7 seconds'],
mapping=['inf', '3.0', '5.0', '7.0'])
addOption('min_cutoff', title='Min CutOff', desc='输入滤波频率截断,\n越小越平滑,越大静止时越灵敏',
type=3, mapper=min_cutoff_mapper)
addOption('beta', title='Beta', desc='输入滤波速度补偿,\n越小越平滑,越大运动时越灵敏',
type=3, mapper=beta_mapper)
addOption('breath_cycle', title='Breath Cycle', desc='设置呼吸循环时间',
choices=['No Breath', '3 seconds', '5 seconds', '7 seconds'],
mapping=['inf', '3.0', '5.0', '7.0'])
addOption('output', title='Output', desc='选择输出目标',
choices=['Spout2', 'OBS VirtualCam', 'Debug Output'],
mapping=[0, 1, 2])
@@ -390,8 +399,10 @@ class LauncherPanel(wx.Panel):
self.optionSizer.Hide(self.optionDict['mouse_audio_input'])
self.optionSizer.Hide(self.optionDict['audio_sensitivity'])
self.optionSizer.Hide(self.optionDict['audio_threshold'])
self.optionSizer.Hide(self.optionDict['blink_interval'])
else:
self.optionSizer.Show(self.optionDict['mouse_audio_input'])
self.optionSizer.Show(self.optionDict['blink_interval'])
# Update audio fields based on checkbox state
audioInputChoice()
@@ -533,11 +544,20 @@ class LauncherPanel(wx.Panel):
if args['audio_threshold']:
run_args.append('--audio_threshold')
run_args.append(str(args['audio_threshold']))
# Add blink interval for mouse input
if args['blink_interval']:
run_args.append('--blink_interval')
run_args.append(str(args['blink_interval']))
elif args['input'] == 4:
if len(args['osf']):
run_args.append('--osf_input')
run_args.append(args['osf'])
# Add breath cycle option
if args['breath_cycle']:
run_args.append('--breath_cycle')
run_args.append(str(args['breath_cycle']))
if args['output'] == 0:
run_args.append('--output_spout2')
elif args['output'] == 1:
+2
View File
@@ -24,6 +24,8 @@ parser.add_argument('--osf_input', type=str)
parser.add_argument('--mouse_audio_input', action='store_true')
parser.add_argument('--audio_sensitivity', type=float, default=0.02)
parser.add_argument('--audio_threshold', type=float, default=10.0)
parser.add_argument('--blink_interval', type=float, default=5.0)
parser.add_argument('--breath_cycle', type=float, default=float('inf'))
parser.add_argument('--output_virtual_cam', action='store_true')
parser.add_argument('--output_spout2', action='store_true')
+2 -2
View File
@@ -67,8 +67,8 @@ class FaceMeshClientProcess(Process):
mouth_eye_vector = [0.0] * 27
pose_vector = [0.0] * 6
mouth_eye_vector[2] = min(eye_l_h_temp, eye_r_h_temp)
mouth_eye_vector[3] = min(eye_l_h_temp, eye_r_h_temp)
mouth_eye_vector[2] = max(eye_l_h_temp, eye_r_h_temp)
mouth_eye_vector[3] = max(eye_l_h_temp, eye_r_h_temp)
mouth_eye_vector[14] = mouth_ratio * 2.0
+36 -2
View File
@@ -79,6 +79,14 @@ class MouseClientProcess(Process):
}
last_time : float = time.perf_counter()
interval : float = 1.0 / 60 # 60 FPS
# 眨眼相关参数
blink_interval = args.blink_interval # 眨眼间隔时间(秒)
blink_duration = 0.25 # 眨眼持续时间(秒)
last_blink_time = time.perf_counter()
# 呼吸循环参数
breath_start_time = time.perf_counter()
print("Mouse Input Running at 60 FPS")
position_vector = np.array([0, 0, 0, 1], dtype=np.float32)
@@ -89,11 +97,36 @@ class MouseClientProcess(Process):
head_eye_reduce = 0.6
head_slowness = 0.2
# 计算眨眼效果
current_time = time.perf_counter()
time_since_last_blink = current_time - last_blink_time
# 每隔 blink_interval 秒眨一次眼
if time_since_last_blink >= blink_interval:
last_blink_time = current_time
time_since_last_blink = 0
# 计算眨眼动画(使用尖锐的 sin 函数)
if time_since_last_blink < blink_duration:
# 在眨眼持续时间内,使用 sin 函数从 0 升到接近 1 再返回 0
# 使用 sin^2 使曲线更尖锐
blink_progress = time_since_last_blink / blink_duration * np.pi
blink_value = np.sin(blink_progress) ** 2
eye_close = blink_value * 1.0 # 升到1,返回到 0
else:
eye_close = 0.0
# 计算呼吸效果(使用 sin 函数,在 breath_cycle 时间内从 0 到 1 再到 0)
breath_elapsed = (current_time - breath_start_time) % args.breath_cycle
# 使用 sin 函数,让值在一个周期内从 0 -> 1 -> 0
# sin 在 0 到 π 之间从 0 到 1 到 0
breath_value = np.sin(breath_elapsed / args.breath_cycle * np.pi)
# 获取音频音量并映射到 mouth_ratio
current_mouth_ratio = audio_filter(self.audio_volume.value, time.perf_counter()) if self.audio_running else 0
mouse_data = {
'eye_l_h_temp': 0,
'eye_r_h_temp': 0,
'eye_l_h_temp': eye_close,
'eye_r_h_temp': eye_close,
'mouth_ratio': current_mouth_ratio,
'eye_y_ratio': np.interp(pos[1], [posLimit[1], posLimit[3]], [1, -1]) * eye_limit[1],
'eye_x_ratio': np.interp(pos[0], [posLimit[0], posLimit[2]], [1, -1]) * eye_limit[0],
@@ -136,6 +169,7 @@ class MouseClientProcess(Process):
pose_vector[2] = z_angle
pose_vector[3] = pose_vector[1]
pose_vector[4] = pose_vector[2]
pose_vector[5] = breath_value
model_input_arr = eyebrow_vector
model_input_arr.extend(mouth_eye_vector)