Author:qyan.li
Date:2022.4.17
Topic:LightWeightOpenpose框架简介及使用踩坑记录
一、前言
? 忙碌将近两周,课程设计在磕磕绊绊中终于也算是完成,这其中真的是遇到无数的坑,当时就许诺,完成之后,一定要写篇博文记录一下。
? 课程设计是有关姿态识别方向的,因此需要借助于一定的框架实现人体关键点的检测和骨架图的绘制;开始时本来想借助于OpenPose 框架,但奈何中途遇到问题太多,跑三天愣是没跑出来,无奈只好转战其他轻量级或者易于调用的框架,隔壁队友选择MediaPipe ,而自己则是选择LightWeightOpenPose
? 本博文提及的思路和解决办法,仅限参考(按照我的方法代码决对是可以跑通的,但不一定是最简单的方法),同时本博文不涉及任何原理性的讲解,实际上我也看不太懂,后面部分会有一些自己对于代码的小更改,可以参考。
二、LightWeightOpenPose 框架简介
? 自己对于LightWeightOpenPose 的了解并不是特别深刻,选择它的原因也更多在于第一个看见的就是它;下面的简介来自于知乎的一篇文章,可以参考一下:
? LightWeightOpenPose 是Intel 在OpenPose 的基础上,提出一种轻量版本,相对于2阶的OpenPose ,其参数量只有15% ,但是性能缺相差无几(精度降低1%)。最主要的是,其模型可以在CPU 上达到26fps
参考文献:轻量级OpenPose, Lightweight OpenPose - 知乎 (zhihu.com)
小Tips :
- 框架在自己电脑上运行时,速度远没有达到
26fps ,据我的观察,输入视频,大概也就是每秒几帧。 - 相比于隔壁的
MediaPipe ,性能差距个人感觉还是挺大的,至少从我的观察看,性能和效果都比不上隔壁的MediaPipe - 相比于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:
- 上述代码需保证你已经进入相应的文件路径中
--images xx.jpg xx.jpg 为识别图片的执行命令,识别视频更改为--video xx.mp4 - 多张图片识别时直接在命令后添加即可,中间使用空格分隔
? 看到这里,可能会有一部分同学理解不了为什么这么执行,其实这些东西在github 上写的比较清楚,写在这里只是防止某些同学看不懂或者没心思看。
? 参数设置问题可以在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.py 中cv2.imshow('Lightweight Human Pose Estimation Python Demo', img) ,并自行添加保存处理后图片的imwrite 函数,具体原因,可参见另一篇博文:(13条消息) 免费的GPU及Colab使用踩坑教程_隔壁李学长的博客-CSDN博客 -
图像或视频保存问题: ? 原始代码中并不存在将处理后图片保存的代码,因此需要自己添加,很简单,一句代码: cv2.imwrite('./saveTest.PNG',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")
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
exit
Ok ,又有一个解决问题的思路达成,剩下又到python 的主场:
'''FileNameLst为一万张图片的文件名列表'''
print(len(FileNameLst))
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')
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框架原始输出为在原图上的绘制人体骨骼图并进行输出,我们期待是能不能对输出图像进行一些改变:
- 删除背景,输出纯骨骼图
- 加粗原图上的人体骨骼线条,提高后续模型的分类效果
? 由于关键点检测和骨骼图绘制部分的代码集中分布在demo.py的run_demo函数中,因此我们后续主要针对run_demo中的代码进行调整
-
LightWeightOpenPose输出纯骨骼图
for pose in current_poses:
pose.draw(img)
上述代码完成在原图上绘制骨骼图的操作
pose 中主要保存人体的关键点信息以及连接方式- 借助于
pose 类中的自定义draw 方法实现绘图,img 表示原始图片
? 结合上述的代码实现,可以得到输出纯骨骼图的方法:手动生成一张纯色的与原始图像大小一致的图像,将其作为draw 函数传入的内部参数即可完成纯骨骼图像的输出
t = list(img.shape)
tempImage = np.zeros((int(t[0]),int(t[1]),3))
for pose in current_poses:
pose.draw(tempImage)
-
LightWeightOpenPose输出图线条调整 ? 假设仔细观察run_demo函数,会发现其中存在这样一个函数:addWeighted 函数,从函数的名称可以看出,此函数可以用于调整某两个事物之间的相对比例,可以尝试着修改下述代码中的参数: img = cv2.addWeighted(orig_img, 0.1, img, 0.9, 0)
? 改动之后发现,参数改动的结果会带来图像上线条粗细的改变,经查阅相关资料得知:addWeighted函数可以将两张图片按照一定的比例进行融合,这也就不难解释为什么可以通过参数调整改变线条粗细 -
总结: ? 上述的调整有哪些作用嘛?其实,我做上述调整的初衷是想要增强我后续分类器的分类效果,我们后期自己搭建分类器,希望通过做上面的调整改善数据集,增强分类效果。理论上讲,应该是可以增强的,只是迫于时间原因,没有进行尝试。
|