基于openpose的K聚类算法手势识别

【写在前面的话】

记录信息

  • 记录时间:2022.1.10
  • 记录地点:实验室
  • 记录背景:年终总结的时候,想到的有个综合设计的课题没有整理。

说明

当初学校那边考虑到大部分同学在考研没有时间做,所以给的要求比较低,一组六个人实际上就我一个人做。所以我给自己的目标就是找个简单的方法,同时尽量学明白。找了很多教程之后,选择了最简单的算法进行分类。

【正文内容】

1. 数据集采集

基本前提是利用openpose提取特征点,并基于特征点进行识别(区分),所以我是更具自己的目标进行动作设计的。这个目标就是自己所使用的【K聚类分类算法】,因为动作之间的区分明显,算法很好区分。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jraTK7sO-1641805254276)(C:\Users\LittleDC\Pictures\12_LI (2)].jpg)

2. 数据集预处理

  • 拍摄照片大小统一处理成64*64像素大小进行归一化。

  • 每张图以3个数据作为邻居样本:分别是左右手腕位置、手腕与肩部距离的左右差值。

  • 计算待分类的图像的这三个数据,与已有10组图像样本的数据进行对比,每组差别最小的获取它的手势标签作为识别结果。

  • 一共得到10组结果,取众数作为识别结果。

  • 说明:(标签定义为:左侧–0,右侧–1,中间–2)

    # 处理一张图片
    
    def train_pose(image_path, out_dir, inWidth=368, inHeight=368, threshhold=0.2):
    
      # net = cv.dnn.readNetFromTensorflow(model_path) # 调用预训练好的tf模型
    
      frame = cv.imread(image_path) # 一帧图片
    
      frame = cv.resize(frame, (480, 480)) # 修改图像尺寸
    
      frameWidth = frame.shape[1]
    
      frameHeight = frame.shape[0]  # 图片的大小
    
     
    
      net = cv.dnn.readNetFromCaffe(protoFile, weightsFile)
    
      inp = cv.dnn.blobFromImage(frame, 1.0 / 255, (inWidth, inHeight), (0, 0, 0), swapRB=False, crop=False)
    
      net.setInput(inp)
    
      out = net.forward()
    
      out = out[:, :9, :, :] # MobileNet output [1, 57, -1, -1], we only need the first 19 elements
    

## 3. 基于openpose的身体姿态提取

- 标定需要用到的点:上肢姿态点,左三个右三个。肩膀、手肘、手腕

# 图像的左上角为坐标原点
# 字典变量
BODY_PARTS = {"Nose": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4,
              "LShoulder": 5, "LElbow": 6, "LWrist": 7, "RHip": 8, "RKnee": 9,
              "RAnkle": 10, "LHip": 11, "LKnee": 12, "LAnkle": 13, "REye": 14,
              "LEye": 15, "REar": 16, "LEar": 17, "Background": 18}

# 颈部1;左右肩25;左右手肘36;左右手腕47

# 列表变量
POSE_PAIRS = [["Neck", "RShoulder"], ["Neck", "LShoulder"], ["RShoulder", "RElbow"],
              ["RElbow", "RWrist"], ["LShoulder", "LElbow"], ["LElbow", "LWrist"],
              ["Neck", "RHip"], ["RHip", "RKnee"], ["RKnee", "RAnkle"], ["Neck", "LHip"],
              ["LHip", "LKnee"], ["LKnee", "LAnkle"], ["Neck", "Nose"], ["Nose", "REye"],
              ["REye", "REar"], ["Nose", "LEye"], ["LEye", "LEar"]]
# 前六个为手势相关的六条连接

protoFile = "pose_deploy_linevec.prototxt"
weightsFile = "pose_iter_440000.caffemodel"
model_path = "graph_opt.pb"


# 调用预训练好的tf模型
def train_pose(image_path, out_dir, inWidth=368, inHeight=368, threshhold=0.2):
    # net = cv.dnn.readNetFromTensorflow(model_path)  


    net = cv.dnn.readNetFromCaffe(protoFile, weightsFile)
    inp = cv.dnn.blobFromImage(frame, 1.0 / 255, (inWidth, inHeight), (0, 0, 0), swapRB=False, crop=False)

4. 基于openpose的身体姿态提取

  • 实现一个分类器的基础是一个预先给定的类别集合C={C1,C2,…,Cm}和一批已知类别的样本数据集D={d1,d2,…,dn}。不同分类算法的区别一般体现在所形成的分类规则上。

  • 在分类问题中,一个核心的概念是两个数据点之间的距离。所谓判断一个数据点该属于哪个类,本质上就是看它离哪个类的已知数据点更近。而“距离”在不同的应用背景下可能有不同的定义。以二维数据空间为例,给出三种常见的距离定义,如图2所示。

  • 设(x1,y1)和(x2,y2)为两个数据点p1和p2的坐标,欧式距离、曼哈顿距离和余弦相似度(即平面上两个向量夹角的余弦值)分别为:
    在这里插入图片描述

  • 针对样本数据D,KNN算法计算待分类数据x与样本数据集D中所有数据的距离,然后取其中最小的K个(也就是“KNN”中的K,而NN表示“最近的邻点”),看它们分别属于哪一个类,判定x应该属于K中出现较多的那个类。针对样本数据D,KNN算法计算待分类数据x与样本数据集D中所有数据的距离,然后取其中最小的K个(也就是“KNN”中的K,而NN表示“最近的邻点”),看它们分别属于哪一个类,判定x应该属于K中出现较多的那个类。

  • 利用openpose识别身体姿态点,标定需要用到的六个上肢姿态点,分别为左半身和右半身的三个肩膀点、手肘点、手腕点。确定3种手势,在不同环境下由不同人拍摄制作采集成10组数据,作为数据集。图片统一处理成64*64像素大小,进行归一化处理。每张图片采用欧式距离定义以3个数据作为样本:分别是左右手腕位置、手腕与肩部距离的左右差值。通过计算待分类的图像的这三个数据,与已有10组图像样本的数据进行对比,计算得到每组中与待分类图像差值最小的手势,获取它的手势标签,作为识别结果。其中第一种手势即左手抬起的标签定义为–0,第二种手势即右手抬起,左手放下的标签定义为–1,第三种手势即双手均放下的标签定义为–2。共得到10组识别结果,取众数作为识别结果。

 # 读取待识别图像
    path = 'detect.jpg'
    result_path = r'C:\Users\viola\PycharmProjects\hand_re\openpose_dynamic\detect_result'
    dfe = get_pose(path, result_path, inWidth=368, inHeight=368, threshhold=0.2)

    # 计算与30个样本数据的距离
    d = np.zeros((30, 5))
    for w in range(30):
        d[w] = np.sum((dfe - train_features[w, :]) ** 2)

    d = d.tolist()  # 转变为列表数据结构
    ma = max(d)
    result_index = []
    k = 10  # 非3的倍数,可以出结果
    for i in range(k):
        a = d.index(min(d))
        result_index.append(a)
        d[a] = ma  # 避免被反复查找

    result_labels = [int(i / 10) for i in result_index]
    r = np.zeros(3)
    for n in result_labels:
        r[int(n)] += 1
    result_pose = hand_pose[int(np.argmax(r))]  # 返回最大值的索引
    print('the pose maybe is' + result_pose)

5. 利用pickle保存模型

  • 在机器学习中,当确定好一个模型后,我们需要将它保存下来,这样当新数据出现时,我们能够调出这个模型来对新数据进行预测。同时这些新数据将被作为历史数据保存起来,经过一段周期后,使用更新的历史数据再次训练,得到更好的模型。

  • 如果模型的流转都在python内部,那么可以使用内置的pickle库来完成模型的存储和调取。

  • pickle是负责将python对象序列化(serialization)和反序列化(de- serialization)的模块。pickle模块可以读入任何python对象,然后将它们转换成字符串,我们再使用dump函数将其储存到文件中,这个过程叫做pickling;反之从文件中提取原始python对象的过程交错unpickling。

# 保存训练数据
    pickle_file = 'parameter.pickle'
    if not os.path.isfile(pickle_file):  # 判断是否存在此文件,若无则存储
        print('Saving data to pickle file...')
        try:
            with open('parameter.pickle', 'wb') as pfile:
                pickle.dump({'train_dataset': fes, 'train_labels': train_labels}, pfile, pickle.HIGHEST_PROTOCOL)
        except Exception as e:
            print('Unable to save data to', pickle_file, ':', e)
            raise
    print('Data cached in pickle file.')

# 读取训练数据
    pickle_file = 'parameter.pickle'
    with open(pickle_file, 'rb') as f:
        pickle_data = pickle.load(f)  # 反序列化,与pickle.dump相反
        train_features = pickle_data['train_dataset']
        train_labels = pickle_data['train_labels']
        del pickle_data  # 释放内存
    print('Data and modules loaded.')

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值