眨眼频率、眼动分析、心率、视频录制
This commit is contained in:
@@ -5,71 +5,115 @@ import cv2
|
||||
LEFT_EYE = [33, 160, 158, 133, 153, 144]
|
||||
# 右眼
|
||||
RIGHT_EYE = [362, 385, 387, 263, 373, 380]
|
||||
# 左眼虹膜关键点索引
|
||||
LEFT_EYE_GAZE_IDXS = [33, 133, 159, 145, 468]
|
||||
# 右眼虹膜关键点索引
|
||||
RIGHT_EYE_GAZE_IDXS = [263, 362, 386, 374, 473]
|
||||
# 嘴唇 (内圈)
|
||||
LIPS = [78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308, 415, 310, 311, 312, 13]
|
||||
|
||||
|
||||
def _euclidean_distance(point1, point2):
|
||||
return np.linalg.norm(point1 - point2)
|
||||
|
||||
|
||||
def calculate_ear(landmarks, width, height):
|
||||
"""计算眼睛纵横比 EAR"""
|
||||
# 坐标转换
|
||||
points = np.array([(p.x * width, p.y * height) for p in landmarks])
|
||||
|
||||
|
||||
# 垂直距离
|
||||
v1 = _euclidean_distance(points[1], points[5])
|
||||
v2 = _euclidean_distance(points[2], points[4])
|
||||
# 水平距离
|
||||
h = _euclidean_distance(points[0], points[3])
|
||||
|
||||
|
||||
ear = (v1 + v2) / (2.0 * h)
|
||||
return ear
|
||||
|
||||
|
||||
def calculate_iris_pos(landmarks, indices, width, height):
|
||||
p_left = np.array([landmarks[indices[0]].x * width, landmarks[indices[0]].y * height])
|
||||
p_right = np.array([landmarks[indices[1]].x * width, landmarks[indices[1]].y * height])
|
||||
p_top = np.array([landmarks[indices[2]].x * width, landmarks[indices[2]].y * height])
|
||||
p_bottom = np.array([landmarks[indices[3]].x * width, landmarks[indices[3]].y * height])
|
||||
p_iris = np.array([landmarks[indices[4]].x * width, landmarks[indices[4]].y * height])
|
||||
|
||||
# 修改为欧几里得距离计算
|
||||
eye_width = _euclidean_distance(p_left, p_right)
|
||||
iris_left = _euclidean_distance(p_iris, p_left)
|
||||
if eye_width < 1e-3:
|
||||
raw_x = 0.5
|
||||
else :
|
||||
raw_x = iris_left / eye_width
|
||||
eye_height = _euclidean_distance(p_bottom, p_top)
|
||||
iris_top = _euclidean_distance(p_iris, p_top)
|
||||
if eye_height < 1e-3:
|
||||
raw_y = 0.5
|
||||
else:
|
||||
raw_y = iris_top / eye_height
|
||||
|
||||
# x_min = 0.4
|
||||
# x_max = 0.6
|
||||
# ratio_x = abs(raw_x - x_min) / (x_max - x_min)
|
||||
# y_min = 0.2
|
||||
# y_max = 0.8
|
||||
# ratio_y = abs(raw_y - y_min) / (y_max - y_min)
|
||||
ratio_x = raw_x
|
||||
ratio_y = raw_y
|
||||
|
||||
return max(0.0, min(1.0, ratio_x)), max(0.0, min(1.0, ratio_y))
|
||||
|
||||
|
||||
def calculate_mar(landmarks, width, height):
|
||||
"""计算嘴巴纵横比 MAR"""
|
||||
points = np.array([(p.x * width, p.y * height) for p in landmarks])
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
def calculate_mar_simple(top, bottom, left, right):
|
||||
h = _euclidean_distance(top, bottom)
|
||||
w = _euclidean_distance(left, right)
|
||||
return h / w
|
||||
|
||||
|
||||
# geometry_utils.py 中的 estimate_head_pose 函数替换为以下内容
|
||||
|
||||
|
||||
def estimate_head_pose(landmarks, width, height):
|
||||
"""
|
||||
计算头部姿态 (Pitch, Yaw, Roll)
|
||||
返回单位:角度 (Degree)
|
||||
"""
|
||||
# 3D 模型点 (标准人脸模型)
|
||||
model_points = np.array([
|
||||
(0.0, 0.0, 0.0), # Nose tip
|
||||
(0.0, -330.0, -65.0), # Chin
|
||||
(-225.0, 170.0, -135.0), # Left eye left corner
|
||||
(225.0, 170.0, -135.0), # Right eye right corner
|
||||
(-150.0, -150.0, -125.0), # Left Mouth corner
|
||||
(150.0, -150.0, -125.0) # Right mouth corner
|
||||
])
|
||||
model_points = np.array(
|
||||
[
|
||||
(0.0, 0.0, 0.0), # Nose tip
|
||||
(0.0, -330.0, -65.0), # Chin
|
||||
(-225.0, 170.0, -135.0), # Left eye left corner
|
||||
(225.0, 170.0, -135.0), # Right eye right corner
|
||||
(-150.0, -150.0, -125.0), # Left Mouth corner
|
||||
(150.0, -150.0, -125.0), # Right mouth corner
|
||||
]
|
||||
)
|
||||
|
||||
# MediaPipe 对应的关键点索引
|
||||
idx_list = [1, 152, 33, 263, 61, 291]
|
||||
|
||||
|
||||
image_points = []
|
||||
for idx in idx_list:
|
||||
p = landmarks[idx]
|
||||
image_points.append((p.x * width, p.y * height))
|
||||
|
||||
|
||||
image_points = np.array(image_points, dtype="double")
|
||||
|
||||
focal_length = width
|
||||
center = (width / 2, height / 2)
|
||||
camera_matrix = np.array(
|
||||
[[focal_length, 0, center[0]],
|
||||
[0, focal_length, center[1]],
|
||||
[0, 0, 1]], dtype="double"
|
||||
[[focal_length, 0, center[0]], [0, focal_length, center[1]], [0, 0, 1]],
|
||||
dtype="double",
|
||||
)
|
||||
dist_coeffs = np.zeros((4, 1))
|
||||
dist_coeffs = np.zeros((4, 1))
|
||||
|
||||
# 求解PnP
|
||||
success, rotation_vector, translation_vector = cv2.solvePnP(
|
||||
@@ -80,15 +124,21 @@ def estimate_head_pose(landmarks, width, height):
|
||||
angles, mtxR, mtxQ, Qx, Qy, Qz = cv2.RQDecomp3x3(rmat)
|
||||
|
||||
pitch = angles[0]
|
||||
yaw = angles[1]
|
||||
roll = angles[2]
|
||||
yaw = angles[1]
|
||||
roll = angles[2]
|
||||
|
||||
if pitch < -180: pitch += 360
|
||||
if pitch > 180: pitch -= 360
|
||||
if pitch < -180:
|
||||
pitch += 360
|
||||
if pitch > 180:
|
||||
pitch -= 360
|
||||
pitch = 180 - pitch if pitch > 0 else -pitch - 180
|
||||
if yaw < -180: yaw += 360
|
||||
if yaw > 180: yaw -= 360
|
||||
if roll < -180: roll += 360
|
||||
if roll > 180: roll -= 360
|
||||
if yaw < -180:
|
||||
yaw += 360
|
||||
if yaw > 180:
|
||||
yaw -= 360
|
||||
if roll < -180:
|
||||
roll += 360
|
||||
if roll > 180:
|
||||
roll -= 360
|
||||
|
||||
return pitch, yaw, roll
|
||||
return pitch, yaw, roll
|
||||
|
||||
Reference in New Issue
Block a user