- 鼻子与两眼的向量正交时,设交点为a,理想情况下两眼到a距离的比值应该为1。
- 考虑到大脸和小脸,我们设m为两眼中心点,则判断是否是正脸侧脸式子为:
d
i
s
=
(
a
−
m
)
2
(
x
1
−
x
2
)
2
dis = \frac{(a-m)^2}{(x1-x2)^2}
dis=(x1−x2)2(a−m)2
- 设b为鼻子到嘴角向量正交时的交点,则鼻子到a的距离和到b的距离在不仰头低头情况下存在一定比例关系,并且应该在[1,2]之间。
- 数值越大,头越低。
- 新更新: 如果有遮挡,关键点会歪,所以不取垂点,取交点
- 新做法:鼻子到a的距离与b到a的距离存在比例关系,b是ax3沿线与x4x5的交点,则满足:
(
x
3
−
a
)
=
α
(
b
−
a
)
(x_3 -a) = \alpha(b - a)
(x3−a)=α(b−a),这个
α
\alpha
α应该在[0, 1],
- 所以阈值为: 满足:[0.2, 0.7],不算低头仰头
def is_front_face(self, keypoints: np.ndarray) -> Tuple[List[float], bool, List[float]]:
'''
侧脸判断
抬头仰头判断
Args:
keypoints: 人脸关键点(左眼、右眼、鼻子、左嘴角、右嘴角)
Returns: orientation, isfront
'''
isfront = True
orientation = []
a = self.orthogonal_point(keypoints[0], keypoints[1], keypoints[2])
m = (keypoints[0] + keypoints[1]) / 2
dis_front = np.linalg.norm(a - m) / np.maximum(np.linalg.norm(keypoints[0] - keypoints[1]), 1e-10)
orientation.append(dis_front)
k1, c1 = self.linear_equation(keypoints[2][0], keypoints[2][1], a[0], a[1])
k2, c2 = self.linear_equation(keypoints[3][0], keypoints[3][1], keypoints[4][0], keypoints[4][1])
b = self.linear_equation(k1, c1, k2, c2, beg_k=False)
dis_bow = np.linalg.norm(keypoints[2] - a) / np.maximum(np.linalg.norm(b - a), 1e-10)
orientation.append(dis_bow)
if (dis_front > 0.5) or (not 0.2 < dis_bow < 0.7):
isfront = False
return orientation, isfront, [a, b]
def orthogonal_point(self, x1, x2, x3):
'''
已知:(x1,y1)、(x2,y2)、(x3,y3)
求:a = (ax, ay)
∵ 向量x1x2 = (x2-x1,y2-y1) 正交于 向量ax3 = (x3-ax, y3-ay)
∴ (x2 - x1)(x3 - ax) + (y2-y1)(y3-ay) = 0
∵ a在向量x1x2上, y = λx + b
∴ (x2 - x1)(x3 - ax) + (y2-y1)(y3- λax + b) = 0
∴ ax = (x3*((x2 - x1)) + y3*(y2-y1) - (y2-y1)*b) / (((x2 - x1)) + (y2-y1)*λ)
∴ ay = λ ax + b
'''
x1, y1 = x1[0], x1[1]
x2, y2 = x2[0], x2[1]
x3, y3 = x3[0], x3[1]
k, b = self.linear_equation(x1, y1, x2, y2, beg_k=True)
ax = (x3 * (x2 - x1) + y3 * (y2 - y1) - (y2 - y1) * b) / np.maximum((x2 - x1) + (y2 - y1) * k, 1e-10)
ay = k * ax + b
return np.array([ax, ay])
def linear_equation(self, x1, y1, x2, y2, beg_k=True):
'''
+ beg_k=True:两点确定一条直线
+ beg_k=False: 求两条直线的交点
a, x3, x4, x5 -> b
直线1:y = k1 * x + c1
直线2:y = k2 * x + c2
x = (c1 - c2) / (k2 - k1)
y = k1 * x + c1
Args:
beg_k: true 为解方程求截取和斜率,传入为x1, y1, x2, y2。false 为求x和y,传入为两个方程的截距和斜率
Returns: (k, b) or (x, y)
'''
if beg_k:
k = (y2 - y1) / np.maximum((x2 - x1), 1e-10)
b = y1 - k * x1
return k, b
else:
k1, c1, k2, c2 = x1, y1, x2, y2
x = (c1 - c2) / np.maximum((k2 - k1), 1e-10)
y = k1 * x + c1
return np.array([x, y])