个人博客:http://www.chenjianqu.com/
原文链接:http://www.chenjianqu.com/show-104.html
最近某些项目需要用到人体姿势识别。当前有很多著名的人体识别的开源项目,比如CMU的OpenPose,上交的AlphaPose,效果都很好。我昨天试着安装一下AlphaPose,配环境配了一天,终于可以运行Demo的时候,显存溢出。。。因此我换个思路,我的项目不要求实时性,使用API也是可以接受的。发现百度的人体识别这方面的API还不错,QPS限制为2,也能用,于是便有了这篇文章。
本文通过调用百度开放的人体关键点检测API,实现关键点检测并将其在Python的轻量级GUI库Tkinter绘制出来。当然最直接的方法是直接用opencv显示出来,但是我们更多的时候需要进行交互,因此使用Tkinter。
一、百度API获取
若想使用百度的API,需要在百度AI的官网上注册帐号,然后在人体识别功能下创建一个应用,即可得到
APP_ID、API_KEY、SECRET_KEY,这样就可以调用它的API了。调用方式可以参考其API文档https://ai.baidu.com/ai-doc/BODY/0k3cpyxme
若想使用Python调用,首先要安装百度的API接口模块:
pip install baidu-aip
二、人体关键点获取和绘制
可以通过下面的类调用百度API
-
class BaiduAIP(object):
-
def __init__(self):
-
self.client = AipBodyAnalysis(cfg.APP_ID, cfg.API_KEY, cfg.SECRET_KEY)
-
-
def bodyAnalysis(self,img_jpg):
-
etval, buffer = cv2.imencode(
'.jpg', img_jpg)
-
result =
self.client.bodyAnalysis(buffer)
#内部把buffer转换为base64了
-
return result
然后是绘制关键点和连线和方框的函数:
-
def draw_line(img,dic,text):
-
color=(
0,
255,
0)
-
thickness=
2
-
if(text==
'warn'):
-
color=(
0,
0,
255)
-
-
#nose ---> neck
-
cv2.line(img, (
int(dic[
'nose'][
'x']),
int(dic[
'nose'][
'y'])),(
int(dic[
'neck'][
'x']),
int(dic[
'neck'][
'y'])), color, thickness)
-
#neck --> left_shoulder
-
cv2.line(img, (
int(dic[
'neck'][
'x']),
int(dic[
'neck'][
'y'])),(
int(dic[
'left_shoulder'][
'x']),
int(dic[
'left_shoulder'][
'y'])), color, thickness)
-
#neck --> right_shoulder
-
cv2.line(img, (
int(dic[
'neck'][
'x']),
int(dic[
'neck'][
'y'])),(
int(dic[
'right_shoulder'][
'x']),
int(dic[
'right_shoulder'][
'y'])), color, thickness)
-
#left_shoulder --> left_elbow
-
cv2.line(img, (
int(dic[
'left_shoulder'][
'x']),
int(dic[
'left_shoulder'][
'y'])),(
int(dic[
'left_elbow'][
'x']),
int(dic[
'left_elbow'][
'y'])), color, thickness)
-
#left_elbow --> left_wrist
-
cv2.line(img, (
int(dic[
'left_elbow'][
'x']),
int(dic[
'left_elbow'][
'y'])),(
int(dic[
'left_wrist'][
'x']),
int(dic[
'left_wrist'][
'y'])), color, thickness)
-
#right_shoulder --> right_elbow
-
cv2.line(img, (
int(dic[
'right_shoulder'][
'x']),
int(dic[
'right_shoulder'][
'y'])),(
int(dic[
'right_elbow'][
'x']),
int(dic[
'right_elbow'][
'y'])), color, thickness)
-
#right_elbow --> right_wrist
-
cv2.line(img, (
int(dic[
'right_elbow'][
'x']),
int(dic[
'right_elbow'][
'y'])),(
int(dic[
'right_wrist'][
'x']),
int(dic[
'right_wrist'][
'y'])), color, thickness)
-
#neck --> left_hip
-
cv2.line(img, (
int(dic[
'neck'][
'x']),
int(dic[
'neck'][
'y'])),(
int(dic[
'left_hip'][
'x']),
int(dic[
'left_hip'][
'y'])), color, thickness)
-
#neck --> right_hip
-
cv2.line(img, (
int(dic[
'neck'][
'x']),
int(dic[
'neck'][
'y'])),(
int(dic[
'right_hip'][
'x']),
int(dic[
'right_hip'][
'y'])), color, thickness)
-
#left_hip --> left_knee
-
cv2.line(img, (
int(dic[
'left_hip'][
'x']),
int(dic[
'left_hip'][
'y'])),(
int(dic[
'left_knee'][
'x']),
int(dic[
'left_knee'][
'y'])), color, thickness)
-
#right_hip --> right_knee
-
cv2.line(img, (
int(dic[
'right_hip'][
'x']),
int(dic[
'right_hip'][
'y'])),(
int(dic[
'right_knee'][
'x']),
int(dic[
'right_knee'][
'y'])), color, thickness)
-
#left_knee --> left_ankle
-
cv2.line(img, (
int(dic[
'left_knee'][
'x']),
int(dic[
'left_knee'][
'y'])),(
int(dic[
'left_ankle'][
'x']),
int(dic[
'left_ankle'][
'y'])), color, thickness)
-
#right_knee --> right_ankle
-
cv2.line(img, (
int(dic[
'right_knee'][
'x']),
int(dic[
'right_knee'][
'y'])),(
int(dic[
'right_ankle'][
'x']),
int(dic[
'right_ankle'][
'y'])), color, thickness)
-
-
def draw_point(img,dic,text):
-
color=(
0,
255,
0)
-
thickness=
2
-
if(text==
'warn'):
-
color=(
0,
0,
255)
-
for i
in dic:
-
cv2.circle(img,(
int(dic[i][
'x']),
int(dic[i][
'y'])),
5,color,thickness)
-
-
def draw_box(img,dic,text):
-
color=(
255,
0,
0)
-
if(text==
'warn'):
-
color=(
0,
0,
255)
-
-
left_top=(
int(dic[
'left']),
int(dic[
'top']))
-
left_bottom=(
int(dic[
'left']),
int(dic[
'top']+dic[
'height']))
-
right_bottom=(
int(dic[
'left']+dic[
'width']),
int(dic[
'top']+dic[
'height']))
-
right_top=(
int(dic[
'left']+dic[
'width']),
int(dic[
'top']))
-
cv2.line(img, left_top,left_bottom, color,
2)
-
cv2.line(img, left_top,right_top, color,
2)
-
cv2.line(img, right_bottom,left_bottom,color,
2)
-
cv2.line(img, right_bottom,right_top,color,
2)
-
-
cv2.putText(img, text, (
int(dic[
'left']),
int(dic[
'top'])+
20), cv2.FONT_HERSHEY_COMPLEX,
1,color,
1)
测试一下:
-
if __name__ ==
'__main__':
-
baiduapi = BaiduAIP()
-
img = cv2.imread(
'/media/chen/chen/Photo/others/littleCookies.jpg')
-
d=baiduapi.bodyAnalysis(img)
-
print(
"api time= "+str(t2-t1))
-
print(d[
"person_num"])
-
print(d[
"log_id"])
-
persion=d[
"person_info"]
-
for p
in persion:
-
draw_line(img,p[
'body_parts'],
'ok')
-
draw_point(img,p[
'body_parts'],
'ok')
-
draw_box(img,p[
'location'],
'beauty')
-
-
cv2.imwrite(
"image1.jpg",img)
结果:
三、tkinter界面开发
先定义窗口和组件:
-
IMG_HEIGT=
352*
2
-
IMG_WIDTH=
640*
2
-
#根窗口
-
window = tk.Tk()
-
-
#显示变量
-
num_people = StringVar()
-
num_people.
set(
'当前人数:0')
-
num_desertion = StringVar()
-
num_desertion.
set(
'开小差:0')
-
-
#UI绘制
-
window.title(
"智能课堂管理")
-
sw = window.winfo_screenwidth()
#获取屏幕宽
-
sh = window.winfo_screenheight()
#获取屏幕高
-
-
#设置窗口大小和位置
-
wx = IMG_WIDTH+
100
-
wh = IMG_HEIGT+
100
-
window.geometry(
"%dx%d+%d+%d" %(wx,wh,(sw-wx)/
2,(sh-wh)/
2
-100))
#窗口至指定位置
-
-
#顶部是信息栏
-
fm1 = Frame(window)
-
Label(fm1,
text=
"总人数:20").pack(side=
'left')
-
label_num = Label(fm1, textvariable=num_people).pack(side=
'left')
-
label_num = Label(fm1, textvariable=num_desertion).pack(side=
'left')
-
fm1.pack(side=
'top', padx=10)
-
-
#左侧是操作栏
-
fm2 = Frame(window)
-
fm2.pack(side=
'left', padx=10)
-
-
canvas1 = tk.Canvas(window,bg=
"#c4c2c2",height=IMG_HEIGT,width=IMG_WIDTH)
#绘制画布
-
canvas1.pack(side=
"left")
-
-
job=ImgProcThread()
-
-
bt_start = tk.Button(fm2,
text=
"启动",height=
2,width=
15,command=job.start).pack(side=
"top")
-
bt_pause = tk.Button(fm2,
text=
"暂停",height=
2,width=
15,command=job.pause).pack(side=
"top")
-
bt_resume = tk.Button(fm2,
text=
"恢复",height=
2,width=
15,command=job.
resume).pack(side=
"top")
-
bt_stop = tk.Button(fm2,
text=
"结束线程",height=
2,width=
15,command=job.
stop).pack(side=
"top")
-
bt_quit = tk.Button(fm2,
text=
"退出程序",height=
2,width=
15,command=window.quit).pack(side=
"top")
-
-
window.mainloop()
ImgProcThread是什么?由于tkiner中没有提供直接播放视频的组件,因此我们使用cavas显示图片,并将关键点检测和图片检测放到另一个线程。但是由于Python自带的线程类功能功能不足,因此需要自己实现线程阻塞、结束等功能,如下:
-
class ImgProcThread(threading.Thread):
-
def __init__(self, *args, **kwargs):
-
super(ImgProcThread,
self).__init_
_(*args, **kwargs)
-
self.__flag = threading.Event()
# 用于暂停线程的标识
-
self.__flag.set()
# 设置为True
-
self.__running = threading.Event()
# 用于停止线程的标识
-
self.__running.set()
# 将running设置为True
-
-
def run(self):
-
capture = cv2.VideoCapture(
'/media/chen/chen/Photo/others/1.mp4')
-
while
self.__running.isSet()
and capture.isOpened():
-
self.__flag.wait()
# 为True时立即返回, 为False时阻塞直到内部的标识位为True后返回
-
pass
-
-
def pause(self):
-
self.__flag.clear()
# 设置为False, 让线程阻塞
-
-
def resume(self):
-
self.__flag.set()
# 设置为True, 让线程停止阻塞
-
-
def stop(self):
-
self.__flag.set()
# 将线程从暂停状态恢复, 如何已经暂停的话
-
self.__running.clear()
# 设置为Fals
四、总程序
本程序是根据我自己的需求来写的,因此会有一些在别人看起来蜜汁操作的代码,大家可以略过。
config.py
-
APP_ID=
"***"
-
API_KEY=
"***"
-
SECRET_KEY=
"***"
aip_bodyanalysis.py
-
from aip import AipBodyAnalysis
-
import sys
-
if(
'/opt/ros/kinetic/lib/python2.7/dist-packages' in sys.path):
-
sys.path.remove(
'/opt/ros/kinetic/lib/python2.7/dist-packages')
-
import cv2
-
import os
-
import config as cfg
-
import base64
-
import
time
-
-
def pose_analyse(img,dic):
-
nose=(
int(dic[
'nose'][
'x']),
int(dic[
'nose'][
'y']))
-
neck=(
int(dic[
'neck'][
'x']),
int(dic[
'neck'][
'y']))
-
if(neck[
1]<=nose[
1]):
-
return
'warn'
-
return
'ok'
-
-
def draw_line(img,dic,text):
-
color=(
0,
255,
0)
-
thickness=
2
-
if(text==
'warn'):
-
color=(
0,
0,
255)
-
-
#nose ---> neck
-
cv2.line(img, (
int(dic[
'nose'][
'x']),
int(dic[
'nose'][
'y'])),(
int(dic[
'neck'][
'x']),
int(dic[
'neck'][
'y'])), color, thickness)
-
#neck --> left_shoulder
-
cv2.line(img, (
int(dic[
'neck'][
'x']),
int(dic[
'neck'][
'y'])),(
int(dic[
'left_shoulder'][
'x']),
int(dic[
'left_shoulder'][
'y'])), color, thickness)
-
#neck --> right_shoulder
-
cv2.line(img, (
int(dic[
'neck'][
'x']),
int(dic[
'neck'][
'y'])),(
int(dic[
'right_shoulder'][
'x']),
int(dic[
'right_shoulder'][
'y'])), color, thickness)
-
#left_shoulder --> left_elbow
-
cv2.line(img, (
int(dic[
'left_shoulder'][
'x']),
int(dic[
'left_shoulder'][
'y'])),(
int(dic[
'left_elbow'][
'x']),
int(dic[
'left_elbow'][
'y'])), color, thickness)
-
#left_elbow --> left_wrist
-
cv2.line(img, (
int(dic[
'left_elbow'][
'x']),
int(dic[
'left_elbow'][
'y'])),(
int(dic[
'left_wrist'][
'x']),
int(dic[
'left_wrist'][
'y'])), color, thickness)
-
#right_shoulder --> right_elbow
-
cv2.line(img, (
int(dic[
'right_shoulder'][
'x']),
int(dic[
'right_shoulder'][
'y'])),(
int(dic[
'right_elbow'][
'x']),
int(dic[
'right_elbow'][
'y'])), color, thickness)
-
#right_elbow --> right_wrist
-
cv2.line(img, (
int(dic[
'right_elbow'][
'x']),
int(dic[
'right_elbow'][
'y'])),(
int(dic[
'right_wrist'][
'x']),
int(dic[
'right_wrist'][
'y'])), color, thickness)
-
#neck --> left_hip
-
cv2.line(img, (
int(dic[
'neck'][
'x']),
int(dic[
'neck'][
'y'])),(
int(dic[
'left_hip'][
'x']),
int(dic[
'left_hip'][
'y'])), color, thickness)
-
#neck --> right_hip
-
cv2.line(img, (
int(dic[
'neck'][
'x']),
int(dic[
'neck'][
'y'])),(
int(dic[
'right_hip'][
'x']),
int(dic[
'right_hip'][
'y'])), color, thickness)
-
#left_hip --> left_knee
-
cv2.line(img, (
int(dic[
'left_hip'][
'x']),
int(dic[
'left_hip'][
'y'])),(
int(dic[
'left_knee'][
'x']),
int(dic[
'left_knee'][
'y'])), color, thickness)
-
#right_hip --> right_knee
-
cv2.line(img, (
int(dic[
'right_hip'][
'x']),
int(dic[
'right_hip'][
'y'])),(
int(dic[
'right_knee'][
'x']),
int(dic[
'right_knee'][
'y'])), color, thickness)
-
#left_knee --> left_ankle
-
cv2.line(img, (
int(dic[
'left_knee'][
'x']),
int(dic[
'left_knee'][
'y'])),(
int(dic[
'left_ankle'][
'x']),
int(dic[
'left_ankle'][
'y'])), color, thickness)
-
#right_knee --> right_ankle
-
cv2.line(img, (
int(dic[
'right_knee'][
'x']),
int(dic[
'right_knee'][
'y'])),(
int(dic[
'right_ankle'][
'x']),
int(dic[
'right_ankle'][
'y'])), color, thickness)
-
-
def draw_point(img,dic,text):
-
color=(
0,
255,
0)
-
thickness=
2
-
if(text==
'warn'):
-
color=(
0,
0,
255)
-
for i in dic:
-
cv2.circle(img,(
int(dic[i][
'x']),
int(dic[i][
'y'])),
5,color,thickness)
-
-
def draw_box(img,dic,text):
-
color=(
255,
0,
0)
-
if(text==
'warn'):
-
color=(
0,
0,
255)
-
-
left_top=(
int(dic[
'left']),
int(dic[
'top']))
-
left_bottom=(
int(dic[
'left']),
int(dic[
'top']+dic[
'height']))
-
right_bottom=(
int(dic[
'left']+dic[
'width']),
int(dic[
'top']+dic[
'height']))
-
right_top=(
int(dic[
'left']+dic[
'width']),
int(dic[
'top']))
-
cv2.line(img, left_top,left_bottom, color,
2)
-
cv2.line(img, left_top,right_top, color,
2)
-
cv2.line(img, right_bottom,left_bottom,color,
2)
-
cv2.line(img, right_bottom,right_top,color,
2)
-
-
cv2.putText(img, text, (
int(dic[
'left']),
int(dic[
'top'])+
20), cv2.FONT_HERSHEY_COMPLEX,
1,color,
1)
-
-
-
class BaiduAIP(object):
-
def __init_
_(self):
-
self.client = AipBodyAnalysis(cfg.APP_ID, cfg.API_KEY, cfg.SECRET_KEY)
-
-
def bodyAnalysis(self,img_jpg):
-
etval, buffer = cv2.imencode(
'.jpg', img_jpg)
-
result = self.client.bodyAnalysis(buffer)
#内部把buffer转换为base64了
-
return result
-
-
-
if __name_
_ ==
'__main__':
-
baiduapi = BaiduAIP()
-
img = cv2.imread(
'/media/chen/chen/Photo/others/littleCookies.jpg')
-
t1=time.time()
-
d=baiduapi.bodyAnalysis(img)
-
t2=time.time()
-
print(
"api time= "+str(t2-t1))
-
print(d[
"person_num"])
-
print(d[
"log_id"])
-
persion=d[
"person_info"]
-
for p in persion:
-
draw_line(img,p[
'body_parts'],
'ok')
-
draw_point(img,p[
'body_parts'],
'ok')
-
draw_box(img,p[
'location'],
'beauty')
-
t3=time.time()
-
print(
"draw time= "+str(t3-t2))
-
cv2.imwrite(
"image1.jpg",img)
tkinter_body.py
-
import sys
-
if(
'/opt/ros/kinetic/lib/python2.7/dist-packages'
in sys.path):
-
sys.path.remove(
'/opt/ros/kinetic/lib/python2.7/dist-packages')
-
import cv2
-
import tkinter as tk
-
from tkinter import *
#文件控件
-
from PIL import Image, ImageTk
#图像控件
-
import threading
#多线程
-
from aip_bodyanalysis import *
-
import time
-
-
-
IMG_HEIGT=
352*
2
-
IMG_WIDTH=
640*
2
-
-
aip=BaiduAIP()
-
is_run=True
-
-
#申请线程锁
-
lock=threading.Lock()
-
-
#根窗口
-
window = tk.Tk()
-
-
#显示变量
-
num_people = StringVar()
-
num_people.set(
'当前人数:0')
-
num_desertion = StringVar()
-
num_desertion.set(
'不正常:0')
-
-
#UI绘制
-
window.title(
"测试")
-
sw = window.winfo_screenwidth()
#获取屏幕宽
-
sh = window.winfo_screenheight()
#获取屏幕高
-
-
#设置窗口大小和位置
-
wx = IMG_WIDTH+
100
-
wh = IMG_HEIGT+
100
-
window.geometry(
"%dx%d+%d+%d"
%(wx,wh,(sw-wx)/
2,(sh-wh)/
2-
100))
#窗口至指定位置
-
-
#顶部是信息栏
-
fm1 = Frame(window)
-
Label(fm1, text=
"总人数:20").pack(side=
'left')
-
label_num = Label(fm1, textvariable=num_people).pack(side=
'left')
-
label_num = Label(fm1, textvariable=num_desertion).pack(side=
'left')
-
fm1.pack(side=
'top', padx=
10)
-
-
#左侧是操作栏
-
fm2 = Frame(window)
-
fm2.pack(side=
'left', padx=
10)
-
canvas1 = tk.Canvas(window,bg=
"#c4c2c2",height=IMG_HEIGT,width=IMG_WIDTH)
#绘制画布
-
canvas1.pack(side=
"left")
-
-
-
def getResult(img):
-
d=aip.bodyAnalysis(img)
-
if(
"person_info"
not
in d):
-
return
-
persion=d[
"person_info"]
-
#获取人数
-
num=len(persion)
-
num_people.set(
"当前人数:"+str(num))
-
#绘制人体姿势
-
for p
in
persion:
-
status=pose_analyse(img,p[
'body_parts'])
-
draw_line(img,p[
'body_parts'],status)
-
draw_point(img,p[
'body_parts'],status)
-
draw_box(img,p[
'location'],status)
-
-
def cc():
-
capture = cv2.VideoCapture(
'/media/chen/chen/Photo/others/1.mp4')
-
#capture = cv2.VideoCapture(0)
-
while capture.isOpened():
-
t
0=time.time()
-
_, frame = capture.read()
-
#清除缓存
-
for i
in range(
15):
-
_, frame = capture.read()
-
getResult(frame)
-
frame = cv2.flip(frame,
1)
#翻转 0:上下颠倒 大于0水平颠倒
-
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
-
img = Image.fromarray(cv2image)
-
image_file=ImageTk.PhotoImage(img)
-
canvas1.create_image(
0,
0,anchor=
"nw",image=image_file)
-
print(time.time()-t
0)
-
print(
"ending")
-
-
-
class ImgProcThread(threading.Thread):
-
def __init__(self, *args, **kwargs):
-
super(ImgProcThread,
self).__init_
_(*args, **kwargs)
-
self.__flag = threading.Event()
# 用于暂停线程的标识
-
self.__flag.set()
# 设置为True
-
self.__running = threading.Event()
# 用于停止线程的标识
-
self.__running.set()
# 将running设置为True
-
-
def run(self):
-
capture = cv2.VideoCapture(
'/media/chen/chen/Photo/others/1.mp4')
-
while
self.__running.isSet()
and capture.isOpened():
-
self.__flag.wait()
# 为True时立即返回, 为False时阻塞直到内部的标识位为True后返回
-
t
0=time.time()
-
_, frame = capture.read()
-
for i
in range(
15):
-
_, frame = capture.read()
-
frame = cv2.resize(frame,(IMG_WIDTH,IMG_HEIGT), interpolation=cv2.INTER_NEAREST)
-
getResult(frame)
-
#frame = cv2.flip(frame, 1)#翻转 0:上下颠倒 大于0水平颠倒
-
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
-
img = Image.fromarray(cv2image)
-
image_file=ImageTk.PhotoImage(img)
-
canvas1.create_image(
0,
0,anchor=
"nw",image=image_file)
-
print(time.time()-t
0)
-
-
def pause(self):
-
self.__flag.clear()
# 设置为False, 让线程阻塞
-
-
def resume(self):
-
self.__flag.set()
# 设置为True, 让线程停止阻塞
-
-
def stop(self):
-
self.__flag.set()
# 将线程从暂停状态恢复, 如何已经暂停的话
-
self.__running.clear()
# 设置为Fals
-
-
def video_demo():
-
t=threading.Thread(target=cc)
-
t.start()
-
-
job=ImgProcThread()
-
-
bt_start = tk.Button(fm2,text=
"启动",height=
2,width=
15,command=job.start).pack(side=
"top")
-
bt_pause = tk.Button(fm2,text=
"暂停",height=
2,width=
15,command=job.pause).pack(side=
"top")
-
bt_resume = tk.Button(fm2,text=
"恢复",height=
2,width=
15,command=job.resume).pack(side=
"top")
-
bt_stop = tk.Button(fm2,text=
"结束线程",height=
2,width=
15,command=job.stop).pack(side=
"top")
-
bt_quit = tk.Button(fm2,text=
"退出程序",height=
2,width=
15,command=window.quit).pack(side=
"top")
-
-
window.mainloop()
代码执行结果:
转载:https://blog.csdn.net/qq_37394634/article/details/105494672