学习 MediaPipe 手部检测和手势识别(2)

前文链接:学习 MediaPipe 手部检测和手势识别(1)
所有图片均来自互联网。

3 手势识别

3.0 识别内容讨论

个人认为,在手势识别中,应该对:手掌朝向、手指弯曲、指间距离,这 3 个方面进行识别。

3.1 手掌朝向

通过计算向量 0 —> 5 和 0 —> 17 的叉乘,确定手掌朝向。规定代表手掌朝向的向量由手背指向掌心。所以,识别的左手(label == Left)为 v0_5 × v0_17,识别的右手(label == Right)为 v0_17 × v0_5。将叉乘结果归一化,并将向量在xoy平面的映射向量画出来。用绿点表示朝外,红色表示朝内。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

计算手掌朝向向量部分的代码:

    @staticmethod
    def _calculate_cross_product(vector_a, vector_b, normalized:bool=True):
        if vector_a.shape == (3,) and vector_b.shape == (3,):
            cross_product = np.cross(vector_a, vector_b)
            if normalized:
                norm = np.linalg.norm(cross_product)
                if norm != 0:
                    cross_product = cross_product / norm
            return cross_product
        return None

    def _get_palm_facing(self):
        for result in self.results_list:
            # 绘制手掌方向
            if "palm_facing" not in result.keys():
                label = result["label"]
                coord_w = result["coord_w"]
                p0 = coord_w[:, 0]
                v0_5 = coord_w[:, 5] - p0
                v0_17 = coord_w[:, 17] - p0
                if label == "Left":
                    result["plam_facing"] = self._calculate_cross_product(
                        v0_5, v0_17)
                else:
                    result["plam_facing"] = self._calculate_cross_product(
                        v0_17, v0_5)

绘制手掌朝向向量部分的代码:

    def check(self, image):
        img_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        results = self.hands_model.process(img_rgb)

        # 解析结果
        self._analysis_results(results, image.shape)

        # 绘制手部关节和骨骼
        if results.multi_hand_landmarks:
            for handLms in results.multi_hand_landmarks:
                draw_landmarks(image, handLms, HAND_CONNECTIONS)

        self.draw(image)


    def draw(self, image):
        if len(self.results_list) == 0:
            return

        for result in self.results_list:
            # 手掌朝向
            self._get_palm_facing()
            vector_palm = result["plam_facing"]
            p0 = result["coord"][:, 0]
            pv = (p0 + vector_palm * 100)
            cv2.line(image, tuple(map(int, p0[:2])), tuple(map(int, pv[:2])),
                     (0, 0, 255), 2)
            if vector_palm[2] < 0:
                cv2.circle(image, tuple(map(int, p0[:2])), 3, (0, 255, 0), -1)

3.2 手指弯曲程度

这里笼统的称作手指弯曲程度,其实应该包含:手指弯曲程度、手指根部弯曲程度

考虑手指弯曲程度,有3种方案:
1.手指夹角;
2.指尖-指根距离占比;
3.指尖-中间关节距离与手腕到指根距离的大小比(优快云 徐岸轩: Mediapipe实现手势识别教程

手指根部弯曲程度使用角度计算比较靠谱。

3.2.1 手指夹角

可以通过 v1_2 和 v3_4(大拇指的也可以改为v2_3 和 v3_4),v5_6 和 v7_8(以此类推)的夹角评估手指弯曲程度。通过 v0_1 和 v1_2,v0_5 和 v5_6(以此类推)的夹角评估手指根部的弯曲程度。

这里可以使用 三维世界坐标 计算手指方向向量。从图片可以看出,计算出来的角度与目测的手指弯曲程度并不完全匹配。计算出来的角度可以一定程度上反应手指弯曲程度,但是也有笔直的手指计算出来的角度是50°。
在这里插入图片描述在这里插入图片描述在这里插入图片描述

使用计算手指弯曲程度的代码:

    # 手指弯曲程度
    @staticmethod
    def _angle_between_vectors(vector1, vector2):
        # 计算向量的模长
        magnitude1 = np.linalg.norm(vector1)
        magnitude2 = np.linalg.norm(vector2)

        # 计算向量的点积
        dot_product = np.dot(vector1, vector2)

        # 计算夹角
        theta = np.arccos(dot_product / (magnitude1 * magnitude2))

        return np.degrees(theta)

    def _get_finger_bending_degree(self):
        """ 使用世界三维坐标计算手指夹角,评价手指弯曲程度。 """
        for result in self.results_list:
            # 计算手指弯曲程度
            if "finger_bending" not in result.keys():
                coord_w = result["coord_w"]
                list_bending = []
                p0 = coord_w[:, 0]

                for i in range(5):
                    p1 = coord_w[:, 1+4*i]
                    p2 = coord_w[:, 2+4*i]
                    p3 = coord_w[:, 3+4*i]
                    p4 = coord_w[:, 4+4*i]

                    # 手指弯曲
                    list_bending.append(int(self._angle_between_vectors(p1-p0, p2-p1)))

                    # 手指的弯曲程度
                    list_bending.append(int(self._angle_between_vectors(p4-p3, p2-p1)))
                
                result["finger_bending"] = list_bending

显示手指弯曲程度的部分代码:

        for result in self.results_list:
            # 手指弯曲程度
            self._get_finger_bending_degree()
            finger_bending = result["finger_bending"]
            coord = result["coord"]
            list_key = [1, 4, 5, 8, 9, 12, 13, 16, 17, 20]
            for index, value in enumerate(list_key):
                cx, cy, cz = coord[:, value]
                text = f'{finger_bending[index]}'

                self._put_text_within_image(image, text, (cx, cy), color=(0, 0, 255),
                                            font_scale=0.25, thickness=1)

明天开始黑猴,今天学一下。

========== 2024/08/19 学习中 ==========

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猎猫骑巨兽

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

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

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

打赏作者

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

抵扣说明:

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

余额充值