如何实现工业级手部骨架姿态识别

【oneAPI DevSummit & OpenVINODevCon联合黑客松】

随着制造与工业领域的发展,对于更高效、智能、安全的生产方式需求的日益增长。同这次大赛,带给我们的新的灵感和技术支持。于是我们基于英特尔oneAPI基础工具套件。开发了一款基于手部骨架姿态识别的现实交互系统,利用oneAPI中的并行计算框架,Intel Threading Building Blocks (TBB) 以及Data Parallel C++ (DPC++),对算法进行并行化优化,充分利用多核处理器的计算资源,提高算法的运行速度和吞吐量。准确识别操作者手部姿态,实现人机交互,为制造与工业场景带来了巨大的应用创新。

基于手部骨架姿态识别的现实交互系统主要由硬件设备和软件算法两部分组成。硬件设备包括深度摄像头、传感器、计算单元等,用于捕捉和处理操作者手部骨架姿态数据。软件算法则负责对手部姿态数据进行分析、识别和解码,并转化为具体的操作指令。

方案详细描述

数据预处理:对从深度摄像头和传感器获取的原始数据进行噪声去除、滤波和校准,以提高数据质量。

手部关键点检测:使用深度学习模型或机器学习算法,在图像中检测手部关键点(如手指关节、掌心等),以获取手部的三维姿态信息。

姿态解码与分析:对手部姿态数据进行解码和分析,识别不同的手势动作和手部状态(如张开手掌、握拳、点击等)。

操作指令生成:根据识别出的手势动作和手部状态,生成相应的操作指令,用于与现实交互系统进行通信。

class Tracker:
    """
    检测跟踪模块。
    对深度学习模型的结果进行初步处理。
    1. 增强鲁棒性
    2. 识别手势
    3. IOU跟踪
    """

    def __init__(self, pose_cfg, pose_thres=0.2, no_data_limit=5):
        # 设置
        with open(pose_cfg, 'r') as f:
            self.pose_list = json.load(f)  # 手势规则 list[dict{name:int,angle:list[float*5]}*n个手势]
        self.pose_thres = pose_thres
        self.no_data_limit = no_data_limit  # 容忍检测丢失的帧数

        # 缓存
        self.last_box = np.array([[0, 0, 0, 0],
                                  [0, 0, 0, 0]])  # 上一帧的box位置
        self.no_data_now = 0  # 当前检测丢失累计
        self.active_click = np.array([[0, 0, 0],
                                      [0, 0, 0]])  # 点击响应位置+静态手势
        self.plot_cache = [[None, None],
                           [None, None]]

        # 多线程
        self.piano_data = []
        _thread.start_new_thread(play_piano, (self.piano_data,))
        self.circle_list = []

    def update(self, det, key_point_list):
        # 返回格式
        for n, (*xyxy, conf, cls) in enumerate(det):  # 对可能有两只手进行遍历,六个参数
            x1, y1, x2, y2 = xyxy[0].item(), xyxy[1].item(), xyxy[2].item(), xyxy[3].item()
            iou: np.ndarray = self.__compute_iou(x1, y1, x2, y2)  # 获得一个二维数组,分别对应id0/1的手势的iou
            track_id, iou_val = iou.argmax(), iou.max()  # 获得iou最大对应的手 初步追踪
            pose: int = self.__compute_pose(key_point_list[n])

            # piano
            piano = False
            if piano:
                self.circle_list = piano_judge(key_point_list[n], self.piano_data)

            # 更新内部追踪
            if iou_val == 0:  # 当前手对于之前的两只手都匹配不上
                if self.last_box[track_id].max() != 0:  # 当前追踪的一只手有记录
                    if self.last_box[1 - track_id].max() != 0:  # 记录的另一只手也有记录
                        self.update_nodata([0, 1])  # ①移动过快:全部重置
                        return
                    else:  # 记录的另一只手没有记录
                        track_id = 1 - track_id  # ②画面中加入新手,修正id

            self.no_data_now = 0  # 重置检测丢失计数

            # ③成功追踪到正在移动的手
            self.last_box[track_id] = np.array([x1, y1, x2, y2])
            self.active_click[track_id][0] = key_point_list[n][8][0]
            self.active_click[track_id][1] = key_point_list[n][8][1]
            self.active_click[track_id][2] = pose

            self.plot_cache[track_id][0] = np.array([x1, y1, x2, y2, conf, track_id, iou_val, pose], dtype=np.float32)
            self.plot_cache[track_id][1] = key_point_list[n]

            if len(det) == 1:
                self.update_nodata(1 - track_id, now=True)

    def plot(self, im0):
        for track_id in range(0, 2):
            if self.plot_cache[track_id][0] is not None:
                draw(im0, self.plot_cache[track_id][0], self.plot_cache[track_id][1])
        for i, point in enumerate(self.circle_list):
            x = int(point[0])
            y = int(point[1])
            c = int(255 * (i + 1) / 6)
            cv2.circle(im0, (x, y), 25, (c, 255 - c, abs(122 - c)), 5)

    def get_order(self):
        """
        获得响应位置及手势
        """
        return self.active_click

    def update_nodata(self, idx, now=False):
        """
        清空记录数据
        """
        if now or self.no_data_now == self.no_data_limit:
            self.last_box[idx] = np.array([0, 0, 0, 0])
            self.active_click[idx] = np.array([0, 0, 0])
            if idx == 1 or idx == 0:
                self.plot_cache[idx][0] = None
                self.plot_cache[idx][1] = None
            else:
                self.plot_cache = [[None, None],
                                   [None, None]]
            self.no_data_now = 0
        else:
            self.no_data_now += 1

    def __compute_iou(self, x1, y1, x2, y2):
        """
        计算当前预测框,与记录的两个预测框的IOU值
        """
        box1 = np.array([x1, y1, x2, y2])
        iou_list = []
        for box2 in self.last_box:
            h = max(0, min(box1[2], box2[2]) - max(box1[0], box2[0]))
            w = max(0, min(box1[3], box2[3]) - max(box1[1], box2[1]))
            area_box1 = ((box1[2] - box1[0]) * (box1[3] - box1[1]))
            area_box2 = ((box2[2] - box2[0]) * (box2[3] - box2[1]))
            inter = w * h
            union = area_box1 + area_box2 - inter
            iou = inter / union
            iou_list.append(iou)

        iou_list = np.array(iou_list)
        return iou_list

    # 识别不出手势就是0
    def __compute_pose(self, key_point):
        """
        读取设置文件,匹配手势
        """
        angles = pose_to_angles(key_point)  # [    0.99953    -0.91983    -0.95382    -0.98989    -0.99999]
        for pose in self.pose_list:
            max = (np.array(pose['angle']) + self.pose_thres >= angles).sum()
            min = (np.array(pose['angle']) - self.pose_thres <= angles).sum()
            if max == min == 6:
                return int(pose['name'])
        return 0


if __name__ == '__main__':
    t = Tracker()
    # t.last_time[0] = 90
    # t.last_time[1] = 90
    # t.get_order()
    # t.update_nodata([0, 1])
    # t.__compute_iou(1, 1, 5, 5)
    # t.last_box += 1
    # print(t.last_box)
    # print(t.last_time[0])
    # print(n)
    # print(type(n))

​

系统设计理念

通过借用oneDNN Graph Compiler对手部骨架姿态识别任务进行优化,以解决传统编译器在深度学习领域的不足,从而提高识别任务的效率和速度。oneDNN是Intel开发的一套优化库,旨在为深度学习提供高效的并行计算支持,它基于Intel的oneAPI编程模型,利用Intel的硬件特性,如多核CPU、GPU和Xeon Phi,来提高深度学习的性能。通过将oneDNN与TensorFlow集成,可以显著提高模型的训练速度和效率。

在手部骨架姿态识别任务中,我们通常使用卷积神经网络(CNN)或循环神经网络(RNN)等模型来识别手部骨架姿态数据。这些模型的训练和推理过程需要大量的计算资源和时间。通过使用oneDNN,我们可以充分利用硬件加速器(如GPU或ASIC)的计算能力,提高骨架姿态数据识别任务的效率和速度。

功能描述

  • 专业化操作支持:在机械维修领域,特定行业或工艺的专业操作是非常重要的。现实交互系统应该具备支持特定行业或工艺的专业化操作的能力。例如,在维修机械中,系统需要能够识别手势精确地识别机械零部件的拆卸和安装动作,以帮助人们正确使用操作技能。
  • 实时反馈与评估:现实交互系统应该能够提供实时反馈和评估功能,以帮助学员纠正错误并改善操作技巧。通过识别学员的手势,系统可以及时给出反馈,指导学员调整姿势或改正操作,从而提升学习效果,提高安全保障。
  • 数据记录与分析:为了支持学习效果评估和操作改进,现实交互系统应具备数据记录和分析功能。通过记录工作人员的手势数据和学习过程,系统可以生成学习报告和统计分析,帮助教师评估工作人员的进展情况并针对性地调整教学策略
  • 虚拟仿真环境:为了提供更丰富的教学体验,现实交互系统可以与虚拟仿真环境结合使用。通过将学员的手势与虚拟场景进行交互,系统能够创造出逼真的场景,使学员能够在虚拟环境中进行实际操作和练习,提高操作技能。
  • 多用户支持:在教育和培训场景中,通常需要同时支持多人同时进行操作和学习。现实交互系统应具备多用户支持的能力,能够同时识别和跟踪多个工作人员的手势,并为每个人提供个性化的反馈和指导
def main(choice):
    if choice == 1:
        # 构建模型----------------------------------------------------------------
        model_ = resnet50(num_classes=42, img_size=ops.img_size[0])

        device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
        model_.to(device).eval()  # 设置为前向推断模式

        chkpt = torch.load(ops.model_path, map_location=device)
        model_.load_state_dict(chkpt)
        print('load test model : {}'.format(ops.model_path))

        # ---------------------------------------------------------------- 预测图片
        with torch.no_grad():
            idx = 0
            data_list = []
            for file in os.listdir(ops.test_path):
                if '.jpg' not in file:
                    continue
                idx += 1
                print('{}) image : {}'.format(idx, file))
                # img = cv2.imread(ops.test_path + file)  # img = None
                img = cv2.imdecode(np.fromfile(os.path.join(ops.test_path, file), dtype=np.uint8), -1)
                img_width = img.shape[1]
                img_height = img.shape[0]
                # 输入图片预处理
                img_ = cv2.resize(img, (ops.img_size[1], ops.img_size[0]), interpolation=cv2.INTER_CUBIC)
                img_ = img_.astype(np.float32)
                img_ = (img_ - 128.) / 256.
                img_ = img_.transpose(2, 0, 1)
                img_ = torch.from_numpy(img_).unsqueeze_(0)
                if torch.cuda.is_available():
                    img_ = img_.cuda()  # (bs, 3, h, w)

                # 模型推理
                pre_ = model_(img_.float())
                output = pre_.cpu().detach().numpy()
                output = np.squeeze(output)

                pts_hand = []  # 构建关键点结构
                for i in range(int(output.shape[0] / 2)):
                    x = (output[i * 2 + 0] * float(img_width))
                    y = (output[i * 2 + 1] * float(img_height))
                    pts_hand.append([x, y])

                if ops.vis:
                    draw_hand_line(img, pts_hand)
                    draw_hand_point(img, pts_hand)
                    cv2.imshow(file, img)
                    cv2.waitKey(0)

                # 计算角度
                angle = pose_to_angles(pts_hand)
                name = file.replace('.jpg', '', 1)
                data = {
                    'name': name,
                    'angle': angle.tolist()
                }
                data_list.append(data)
                print(data)
                # 保存图片
                cv2.imwrite("inference/output/"+name+".jpg", img)


            if ops.cfg_write:
                with open(ops.cfg_pose, 'w') as f:
                    json.dump(data_list, f)

        cv2.destroyAllWindows()

        print('well done ')
    elif choice == 2:
        if os.path.isfile(ops.cfg_pose):
            with open(ops.cfg_pose, 'r') as f:
                data = json.load(f)
            print(data)
            print(type(data))
        else:
            Exception('no data')


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=' Hand Pose Setting')
    # 模型路径
    parser.add_argument('--model_path', type=str, default='inference/weights/pose_weight/resnet50_2021-418.pth', help='model_path')
    # GPU选择
    parser.add_argument('--GPUS', type=str, default='0', help='GPUS')
    # 设置手势图片路径  hand_key/image
    parser.add_argument('--test_path', type=str, default='inference/input/pose_setting', help='test_path')
    # 输入模型图片尺寸
    parser.add_argument('--img_size', type=tuple, default=(256, 256), help='img_size')
    # 是否可视化图片
    parser.add_argument('--vis', type=bool, default=True, help='vis')
    # 手势字典文件路径
    parser.add_argument('--cfg_pose', type=str, default='inference/weights/cfg_pose.json')
    # 重写设置文件
    parser.add_argument('--cfg_write', type=bool, default=True)

    ops = parser.parse_args()

    choice = input("1.设定手势\n2.查看手势\n")
    main(int(choice))

价值

  • 基于手部骨架姿态识别的现实交互系统在制造与工业领域具有重要的社会价值。首先,它能够提升生产效率,减少人工操作的时间和工作量,降低劳动强度,进一步提高生产线的自动化程度。其次,它能够提高产品质量和安全性,通过精准的手势控制和操控,可以避免误操作和事故的发生,保障工人的安全。此外,还能够降低专业技能门槛,减少培训成本,提高员工的上岗速度和适应能力。
  • 人机交互进一步提升,手部骨架姿态识别系统可以实现对手部动作的实时跟踪和识别,为人与计算机之间的交互带来更加自然、直观的方式。用户可以通过手势控制设备或应用程序,实现更加智能、便捷的操作体验。
  • 增强虚拟现实和增强现实应用,手部骨架姿态识别系统可以无线化地将用户的手势动作与虚拟世界或现实场景进行交互。这为虚拟现实游戏、培训模拟、设计评估等领域带来了全新的交互方式,提升了用户参与感和沉浸感。
  • 应用领域广泛,手部骨架姿态识别系统可以应用于多个领域。在医疗领域,可以用于康复训练、手术辅助等;在教育领域,可以用于互动教学、学生评估等;在智能家居领域,可以用于智能设备控制、手势识别开关等;在工业领域,可以用于生产操作、机器人交互等。这些应用将提升效率、降低成本,并创造出更多可能性。
  • 创新驱动产业发展,手部骨架姿态识别系统的推出,将推动相关技术、设备和行业的发展。企业可以基于这一技术开发各种应用,带动相关产业链的创新。同时,这也有助于提升国家的科技实力和竞争力。

写在最后

基于英特尔oneAPI基础工具套件开发了一款基于手部骨架姿态识别的现实交互系统,利用并行计算框架和优化库提高算法的速度和效率。系统包括数据预处理、手部关键点检测、姿态解码与分析和操作指令生成等模块,并支持专业化操作、实时反馈与评估、数据记录与分析、虚拟仿真环境和多用户支持等功能。该系统在制造与工业领域具有广泛应用前景,同时技术和市场可行性良好。

该系统可以实现对手部动作的实时跟踪和识别,为人与计算机之间的交互带来更加自然、直观的方式。它应用领域广泛,在制造、工业、医疗、教育、智能家居等领域都有广泛的应用前景。此外,它还可以提升生产效率、降低成本、创造更多可能性,并推动相关技术、设备和行业的发展。该系统在技术和市场可行性方面具备潜力,但需要进行细致的调研和验证,确保系统的稳定性、用户体验和商业可行性。

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LCL.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值