LightWeightOpenPose框架简介及踩坑过程


Author:qyan.li

Date:2022.4.17

Topic:LightWeightOpenpose框架简介及使用踩坑记录

一、前言

​ 忙碌将近两周,课程设计在磕磕绊绊中终于也算是完成,这其中真的是遇到无数的坑,当时就许诺,完成之后,一定要写篇博文记录一下。

​ 课程设计是有关姿态识别方向的,因此需要借助于一定的框架实现人体关键点的检测骨架图的绘制;开始时本来想借助于OpenPose框架,但奈何中途遇到问题太多,跑三天愣是没跑出来,无奈只好转战其他轻量级或者易于调用的框架,隔壁队友选择MediaPipe,而自己则是选择LightWeightOpenPose

​ 本博文提及的思路和解决办法,仅限参考(按照我的方法代码决对是可以跑通的,但不一定是最简单的方法),同时本博文不涉及任何原理性的讲解,实际上我也看不太懂,后面部分会有一些自己对于代码的小更改,可以参考。

二、LightWeightOpenPose框架简介

​ 自己对于LightWeightOpenPose的了解并不是特别深刻,选择它的原因也更多在于第一个看见的就是它;下面的简介来自于知乎的一篇文章,可以参考一下:

LightWeightOpenPoseIntelOpenPose的基础上,提出一种轻量版本,相对于2阶的OpenPose,其参数量只有15% ,但是性能缺相差无几(精度降低1%)。最主要的是,其模型可以在CPU上达到26fps

参考文献:轻量级OpenPose, Lightweight OpenPose - 知乎 (zhihu.com)

扫描二维码关注公众号,回复: 14806816 查看本文章

Tips

  1. 框架在自己电脑上运行时,速度远没有达到26fps,据我的观察,输入视频,大概也就是每秒几帧。
  2. 相比于隔壁的MediaPipe,性能差距个人感觉还是挺大的,至少从我的观察看,性能和效果都比不上隔壁的MediaPipe
  3. 相比于MediaPipe,LightWeight的优点在于它可以进行多人的关键点检测,同时可以将人物用矩形框框选出来,这对于某些情形还是非常重要的

三、LightWeightOpenPose框架运行:

github地址链接:LightWeightOpenpose框架

划重点:

​ 假设你和我一样,不想了解其内部算法的具体原理以及训练推理的具体过程,只需要以下几步即可以运行代码:

  • 配置好框架所需的具体环境:见GitHub

  • pull下框架代码,下载预训练模型:checkpoint_iter_370000.pth文件

    下载地址:https://download.01.org/opencv/openvino_training_extensions/models/human_pose_estimation/checkpoint_iter_370000.pth

  • 命令行执行cmd命令:python demo.py --checkpoint-pth checkpoint_iter_370000.pth --images xx.jpg xx.jpg

    Tips:

    1. 上述代码需保证你已经进入相应的文件路径中
    2. --images xx.jpg xx.jpg 为识别图片的执行命令,识别视频更改为--video xx.mp4
    3. 多张图片识别时直接在命令后添加即可,中间使用空格分隔

​ 看到这里,可能会有一部分同学理解不了为什么这么执行,其实这些东西在github上写的比较清楚,写在这里只是防止某些同学看不懂或者没心思看。

​ 参数设置问题可以在demo.py文件中查看:

### demo.py
parser = argparse.ArgumentParser(
        description='''Lightweight human pose estimation python demo.
                       This is just for quick results preview.
                       Please, consider c++ demo for the best performance.''')
    parser.add_argument('--checkpoint-path', type=str, required=True, help='path to the checkpoint')
    parser.add_argument('--height-size', type=int, default=256, help='network input layer height size')
    parser.add_argument('--video', type=str, default='', help='path to video file or camera id')
    parser.add_argument('--images', nargs='+', default='', help='path to input image(s)')
    parser.add_argument('--cpu', action='store_true', help='run network inference on cpu')
    parser.add_argument('--track', type=int, default=1, help='track pose id in video')
    parser.add_argument('--smooth', type=int, default=1, help='smooth pose keypoints')
    args = parser.parse_args()

​ 根源上讲,上述所有参数都可以进行设置,我们在此只是提及两个如果你想跑通代码必须设置的参数:checkpoint-path 和images或video

四、LightWeightOpenpose框架踩坑

  • GPU问题:

    ​ 假设你是拥有实验室资源的研究生或者拥GPU自重的本科生,这条可以跳过;但是假设你什么都没有,还想跑通这个框架:

    ​ 请删除demo.py中:

    if not cpu:
    	net = net.cuda()
    

    ​ 但假设白嫖Colab的学生党人士,请删除demo.pycv2.imshow('Lightweight Human Pose Estimation Python Demo', img),并自行添加保存处理后图片的imwrite函数,具体原因,可参见另一篇博文:(13条消息) 免费的GPU及Colab使用踩坑教程_隔壁李学长的博客-CSDN博客

  • 图像或视频保存问题:

    ​ 原始代码中并不存在将处理后图片保存的代码,因此需要自己添加,很简单,一句代码:

    cv2.imwrite('./saveTest.PNG',img) ## img为imshow函数传入的img
    

    针对于视频的保存问题:

    ​ 目前还没有想到好的保存方式,但在后面的博文中我应该会更新如何将若干帧图像合成视频

  • 超量图像检测问题:

    ​ 这个问题其实是由代码执行的方式所导致的问题,上述提及:借助于LightWeightOpenPose框架进行图像标注的代码执行方式为python demo.py --checkpoint-path checkpoint_iter_370000.pth --images xx.jpg xx.jpg

    ​ 借助于这种方式执行代码,图片量小可以,你可以手动输入,比如两张三张,但是假设图片量非常大呢?项目中我们数据集图片处理过后有超过一万张,这个时候手动输入肯定不现实,那我们就必须考虑借助于某种手段实现命令行命令。

    ​ 我们干不了这个工作,主要由于工作量太大,但是程序喜欢这种情形,尤其是python,特别喜欢干这种工作,这是它的强项,也是他出圈的一个重要方面,有思路,剩下就是写代码:

    ### 获取文件名列表
    import os
    FileNameLst = os.listdir("Your File Path")
    
    ### command命令生成
    with open('./Test.txt','a',encoding = 'utf-8') as f:
        '''Test.txt文件为最终存放cmd命令的文件'''
        f.write('python demo.py --checkpoint-path checkpoint_iter_370000.pth --images ')
        '''FileNameLst为一万张图片文件名列表,借助于os的listdir命令获取'''
        for item in FileNameLst:
            '''images为存放图片的文件夹'''
            f.write("./images/" + str(item) + " ")
    

    这里算是一个分界线,下面的问题有些同学可能并不会遇到(由于数据集数量的问题)

  • 命令行字符限制问题:

    ​ 借助于上述程序实现了command的命令生成,我们高高兴兴的拿着命令行命令去执行,但是又出现另外的问题:

    ​ 将上述程序生成的命令粘贴至命令行时,会发现命令总是被截断的,一开始我以为是复制粘贴不完整,但后续发现每次都是这样,经查阅资料,发现问题:命令行是存在字符限制的,具体限制的多少大家可自行查阅资料:

    ​ 那么出现问题就要解决,怎么办呢?既然命令太长,那就缩短嘛,分批次进行,不要一次性全部处理,那么一次识别多少图片呢?这个size怎么是确定呢?

    ​ 就以我的字符文件数量和文件命令方式,当我执行上述不完整的cmd命令时,发现每次执行结果为352张图片,OK,那就说明一次性执行350张图片,将我一万多张图像分为33个批次执行。

  • cmd命令执行复杂问题:

    ​ 到这里,所有阻碍我们程序运行的拦路虎都已经解决,下面讲一个简化代码执行复杂度的问题;上述生成33条命令,假设我们每次都复制粘贴至命令行手动执行,这效率有点太低,那我们在想有没有什么可以简化cmd命令执行的方法,经查阅,生成Bat文件方法。

    Bat文件网上讲解资料很多,大家可自行查阅,此处仅给出撰写格式:

    @echo off
    C:
    cd C:/Users\腻味\Desktop\PoseData\mpii_human_pose_v1  ## 目标路径
    start python demo.py --checkpoint-path xx.pth --images xx.jpg ## cmd命令
    exit
    

    Ok,又有一个解决问题的思路达成,剩下又到python的主场:

    ### 生成前32个Bat文件
    
    '''FileNameLst为一万张图片的文件名列表'''
    print(len(FileNameLst)) # 11715/350 = 33.47
    
    for i in range(33):
        with open('./Test' + str(i) + '.bat','a',encoding = 'utf-8') as f:
            f.write('@echo off' + '\n')
            f.write('C:' + '\n')
            f.write('cd C:/Users\腻味\Desktop\PoseData\mpii_human_pose_v1')
            f.write('\n')
            f.write('start ')
            f.write('python demo.py --checkpoint-path checkpoint_iter_370000.pth --images ')
            for j in range(i*350,(i+1)*350):
                f.write("./images/" + str(FileNameLst[j]) + " ")
            f.write('\n')
            f.write('exit')
            
    ### 生成最后一个Bat文件
    with open('./Test33.bat','a',encoding = 'utf-8') as f:
        f.write('@echo off' + '\n')
        f.write('C:' + '\n')
        f.write('cd C:/Users\腻味\Desktop\PoseData\mpii_human_pose_v1' + '\n')
        f.write('start ')
        f.write('python demo.py --checkpoint-path checkpoint_iter_370000.pth --images ')
        for m in range(33*350,len(FileNameLst)):
            f.write("./images/" + str(FileNameLst[m]) + " ")
        f.write('\n')
        f.write('exit')
    
  • 并行处理33个Bat文件问题:

    ​ 在搜索Bat文件时,一般都会看到关键词:批量执行,看到这,我在想:一个一个点Bat文件确实有难度,那我干脆多生成一个Bat文件,把33个小Bat文件全都放在其中,最终执行时,点一下大的Bat文件不就全解决啦。

    ​ 当然我也将想法变成现实,无一例外,同样借助于程序,但是最终执行时发现,当所有命令命令行退出执行完毕,仅仅只生成2000张图片,简化的探索失败。

    ​ 自己考虑问题在电脑应该不支持同时处理这么多的程序,但是这给我们提供一个思路,33个文件执行时,可以同时多执行几个(我当时是4个),不必线性执行,提高效率

    ​ 当然,Bat文件在编写时,应该支持线性或者并行执行,或者同时执行几个,执行完在执行其他,只是自己没有深入探索,感兴趣者可自行查阅

    总结:写到这里,写不动啦,问题总会解决的,有时候走到死胡同,可以换个思路!!!


​ 写不动啦!!!有时间再写,后续会更新些LightWeightOpenPose框架代码的小改动,以输出不同形式的图片 2022/04/17 周日


时间:2022/04/17 周日,也没剩多少东西,就不拖延啦!!!今天更完!!


五、LightWeightOpenPose输出样式微调:

​ 首先说一下,为什么要进行输出样式的微调,LightWeightOpenPose框架原始输出为在原图上的绘制人体骨骼图并进行输出,我们期待是能不能对输出图像进行一些改变:

  1. 删除背景,输出纯骨骼图
  2. 加粗原图上的人体骨骼线条,提高后续模型的分类效果

​ 由于关键点检测和骨骼图绘制部分的代码集中分布在demo.py的run_demo函数中,因此我们后续主要针对run_demo中的代码进行调整

  • LightWeightOpenPose输出纯骨骼图

    ## pose相当于原图+曲线点的集合,img相当于一张底色的图片
    for pose in current_poses:
    	pose.draw(img) # 在原有的图像上绘制pose的线条
    

    上述代码完成在原图上绘制骨骼图的操作

    1. pose中主要保存人体的关键点信息以及连接方式
    2. 借助于pose类中的自定义draw方法实现绘图,img表示原始图片

    ​ 结合上述的代码实现,可以得到输出纯骨骼图的方法:手动生成一张纯色的与原始图像大小一致的图像,将其作为draw函数传入的内部参数即可完成纯骨骼图像的输出

    ## LightWeightOpenPose输出纯骨骼图像
    t = list(img.shape)
    	tempImage = np.zeros((int(t[0]),int(t[1]),3)) # 其中必须传入tuple
        for pose in current_poses:
        	pose.draw(tempImage) # 在新生成的自定义图像上绘制pose的线条
    
  • LightWeightOpenPose输出图线条调整

    ​ 假设仔细观察run_demo函数,会发现其中存在这样一个函数:addWeighted函数,从函数的名称可以看出,此函数可以用于调整某两个事物之间的相对比例,可以尝试着修改下述代码中的参数:

    img = cv2.addWeighted(orig_img, 0.1, img, 0.9, 0)
    

    ​ 改动之后发现,参数改动的结果会带来图像上线条粗细的改变,经查阅相关资料得知:addWeighted函数可以将两张图片按照一定的比例进行融合,这也就不难解释为什么可以通过参数调整改变线条粗细

  • 总结:

    ​ 上述的调整有哪些作用嘛?其实,我做上述调整的初衷是想要增强我后续分类器的分类效果,我们后期自己搭建分类器,希望通过做上面的调整改善数据集,增强分类效果。理论上讲,应该是可以增强的,只是迫于时间原因,没有进行尝试。

猜你喜欢

转载自blog.csdn.net/DALEONE/article/details/124231940