cmvr-biohead/biohead/utils.py
2025-08-21 13:49:41 +08:00

140 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import cv2
from biohead.algo import *
import numpy as np
def calc_feature(color, landmarks, ray_origins, ray_directions):
h, w, _ = color.shape
face = landmarks.multi_face_landmarks[0].landmark
idxs = {"left":234, "right":454, "top":10, "bottom":152, "front":1}
kp = {k: np.array([face[v].x*w, face[v].y*h, face[v].z*w]) for k, v in idxs.items()}
left, right, top, bottom, front = kp.values()
right_axis = right - left
right_axis /= np.linalg.norm(right_axis)
up_axis = top - bottom
up_axis /= np.linalg.norm(up_axis)
forward = np.cross(right_axis, up_axis)
forward /= np.linalg.norm(forward)
forward = -forward
center = (left + right + top + bottom + front) / 5
ray_origins.append(center)
ray_directions.append(forward)
origin = np.mean(ray_origins, axis=0)
forward = np.mean(ray_directions, axis=0)
forward /= np.linalg.norm(forward)
uv = {}
for j in FaceMap().to_dict().values():
p = np.array([face[j].x*w, face[j].y*h, face[j].z*w])
vec = p - origin
u = np.dot(vec, right_axis)
v = np.dot(vec, up_axis)
z = np.dot(vec, forward)
uv[j] = (u, v, z)
x2d, y2d = int(p[0]), int(p[1])
cv2.circle(color, (x2d, y2d), 2, (0, 255, 255), -1)
left_mouth_corner = np.array([face[FaceMap.left_mouth_corner].x * w, face[FaceMap.left_mouth_corner].y * h, face[FaceMap.left_mouth_corner].z * w])
right_mouth_corner = np.array([face[FaceMap.right_mouth_corner].x * w, face[FaceMap.right_mouth_corner].y * h, face[FaceMap.right_mouth_corner].z * w])
# 计算唇角的z值可以取左右唇角的z值的平均值
lip_corners_z = (left_mouth_corner[2] + right_mouth_corner[2]) / 2 # 计算左右唇角的z值的平均值
print(f"唇角的z值为: {lip_corners_z}")
return uv
def update_text(color, result: HeadJoints):
y0 = 30
dy = 30
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.7
color_text = (0, 255, 255)
# 眉毛
r_brow_text = f"R_eyebrow out:{result.right_eyebrow_outside_y:.2f} in:{result.right_eyebrow_inside_y:.2f}"
l_brow_text = f"L_eyebrow out:{result.left_eyebrow_outside_y:.2f} in:{result.left_eyebrow_inside_y:.2f}"
cv2.putText(color, r_brow_text, (30, y0), font, font_scale, color_text, 2)
cv2.putText(color, l_brow_text, (30, y0 + dy), font, font_scale, color_text, 2)
y0 += 2*dy
# 眼睛
r_eye_text = f"R_eyelid U:{result.right_eye_upper_lid_y:.2f} L:{result.right_eye_lower_lid_y:.2f}"
l_eye_text = f"L_eyelid U:{result.left_eye_upper_lid_y:.2f} L:{result.left_eye_lower_lid_y:.2f}"
cv2.putText(color, r_eye_text, (30, y0), font, font_scale, color_text, 2)
cv2.putText(color, l_eye_text, (30, y0 + dy), font, font_scale, color_text, 2)
y0 += 2 * dy
# ---- 眼球位置 ----
l_eyeball_text = f"L_eyeBall X:{result.left_eye_ball_x:.2f} Y:{result.left_eye_ball_y:.2f}"
r_eyeball_text = f"R_eyeBall X:{result.right_eye_ball_x:.2f} Y:{result.right_eye_ball_y:.2f}"
cv2.putText(color, r_eyeball_text, (30, y0), font, font_scale, color_text, 2)
cv2.putText(color, l_eyeball_text, (30, y0 + dy), font, font_scale, color_text, 2)
y0 += 2*dy
# ---- 嘴部中央上下 ----
upper_text = f"UpperLip Y:{result.upper_lip_y:.2f} Z:{result.upper_lip_z:.2f}"
lower_text = f"LowerLip Y:{result.lower_lip_y:.2f} Z:{result.lower_lip_z:.2f}"
cv2.putText(color, upper_text, (30, y0), font, font_scale, color_text, 2)
y0 += dy
cv2.putText(color, lower_text, (30, y0), font, font_scale, color_text, 2)
y0 += dy
# ---- 嘴部左右详细6点 ----
mouth_points = [
("upper_left_lip", result.upper_left_lip_x, result.upper_left_lip_y),
("upper_right_lip", result.upper_right_lip_x, result.upper_right_lip_y),
("left_corner_lip", result.left_corner_lip_x, result.left_corner_lip_y,result.left_corner_lip_z),
("right_corner_lip", result.right_corner_lip_x, result.right_corner_lip_y,result.right_corner_lip_z),
("lower_left_lip", result.lower_left_lip_x, result.lower_left_lip_y),
("lower_right_lip", result.lower_right_lip_x, result.lower_right_lip_y),
]
print(f"Left corner lip Z: {result.left_corner_lip_z}")
print(f"Right corner lip Z: {result.right_corner_lip_z}")
for name, x_val, y_val,z_val in mouth_points:
text = f"{name}: X:{x_val:.2f} Y:{y_val:.2f} Z:{z_val:.2f}"
cv2.putText(color, text, (30, y0), font, font_scale, color_text, 2)
y0 += dy
# Jaw
jaw_text = f"Jaw X:{result.jaw_x:.2f} Y:{result.jaw_y:.2f}"
cv2.putText(color, jaw_text, (30, y0), font, font_scale, color_text, 2)
y0 += dy
def norm(result: HeadJoints, interval, clamp=True):
"""
Normalize result fields to 0-1 range based on interval spec.
Args:
result: HeadJoints instance
interval: dict with key -> [min, max]
clamp: if True, limit outputs to [0,1]
Returns:
dict of normalized values
"""
result_dict = result.to_dict()
normalized = HeadJoints()
for key, value in result_dict.items():
if key in interval:
min_val, max_val = interval[key]
denom = max_val - min_val + 1e-6
norm_val = (value - min_val) / denom
if clamp:
norm_val = max(0.0, min(1.0, norm_val))
setattr(normalized, key, norm_val)
else:
setattr(normalized, key, value)
return normalized