人脸检测:正脸侧脸、是否仰头低头判断

该文章介绍了使用人脸关键点(如眼睛、鼻子、嘴角)进行正脸、侧脸以及抬头低头判断的算法,通过计算向量的交点和比例关系来确定人脸姿态。算法涉及向量正交、线性方程和阈值设置,用于判断是否处于标准角度范围。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 鼻子与两眼的向量正交时,设交点为a,理想情况下两眼到a距离的比值应该为1。
    • 考虑到大脸和小脸,我们设m为两眼中心点,则判断是否是正脸侧脸式子为: d i s = ( a − m ) 2 ( x 1 − x 2 ) 2 dis = \frac{(a-m)^2}{(x1-x2)^2} dis=(x1x2)2(am)2
  • 设b为鼻子到嘴角向量正交时的交点,则鼻子到a的距离和到b的距离在不仰头低头情况下存在一定比例关系,并且应该在[1,2]之间。
    • 数值越大,头越低。
    • 新更新: 如果有遮挡,关键点会歪,所以不取垂点,取交点
    • 新做法:鼻子到a的距离与b到a的距离存在比例关系,b是ax3沿线与x4x5的交点,则满足: ( x 3 − a ) = α ( b − a ) (x_3 -a) = \alpha(b - a) (x3a)=α(ba),这个 α \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)

        # 抬头低头判断 取交点,满足:$(x_3 -a) = \alpha(b - a)$
        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)  # x3a与x4x5的交点
        dis_bow = np.linalg.norm(keypoints[2] - a) / np.maximum(np.linalg.norm(b - a), 1e-10)
        orientation.append(dis_bow)

        # >0.5算侧脸、[0.2, 0.7]之外算仰头低头
        if (dis_front > 0.5) or (not 0.2 < dis_bow < 0.7):
            isfront = False

        # print("[DEBUG] orientation: ", orientation)

        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]
        # conb, lambdax = np.linalg.solve(np.array([[1, x1], [1, x2]]), np.array([y1, y2]))
        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])
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WGS.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值