参考视频来自哔哩哔哩up:小黑黑讲AI
测试视频取自哔哩哔哩up:小阿森bb
动因:上篇文章说想分析自己不喜欢的演员的面部表情,然后我就开始做,但是没有用那个演员来做测试,而是用了我自己和一个很喜欢的baby做测试,世界应该多一些美好!阿彼真的很可爱!现在,就开始说一下过程吧!
一、deepface库的配置
参考视频可以看到up使用了deepface库,在下载deepface后opencv也跟随着下载完毕了。
在pycharm的terminal直接使用下面代码会出现下载一半就报错的问题。一开始是版本问题,更新后继续下载还是一直报错,显示time out。应该是直接在外网下载太慢了。
pip install deepface
所以使用镜像下载
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple deepface
参考文章:https://zhuanlan.zhihu.com/p/641831098
如果完全按照视频里面的conda我会报错,没有这个配置,如果报错的也可以像我一样直接安装,反正是可以运行的。
二、编写代码
我不太会使用github,所以没有办法像视频评论区的网友说的那样去找源代码,我就直接把视频里面的代码自己打上去了(因为代码不长)
这边附上我对着视频搞上去的代码,代码来源我就标视频吧,因为我也不知道up的代码来自哪里。打上去的时候有一部分出入:
#分析视频中人物表情
import cv2
from deepface import DeepFace
#简单测试,图片版本
# result = DeepFace.analyze(img_path="E:\\study\\test.jpg",
# actions=["emotion"])
# print(result)
input_video = "E:\\study\\Pythonstudy\\test\\abi.mp4"#设置待分析视频的路径
output_video ="E:\\study\\Pythonstudy\\test\\emotion.mp4"#分析结果视频
#使用opencv的videocapture接口,打开视频文件
#cap用于从视频文件或者摄像头中捕获视频帧
cap = cv2.VideoCapture(input_video)
fps = int(cap.get(cv2.CAP_PROP_FPS))#获取视频帧的速率
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))#获取视频帧的宽度
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))#获取视频帧的高度
#定义视频编解码器,指定视频文件的编码格式
#XVID是一个流行的MPEG-4的视频编解码器
fourcc = cv2.VideoWriter.fourcc('X', 'V', 'I', 'D')#老版本是_fourcc,所以一直报错
out = cv2.VideoWriter(output_video,#输出视频地址
fourcc,#视频编码器
fps, (frame_width, frame_height))#视频帧参数
#通过返回的out,可以将处理后的视频帧保存到输出文件中
frame_cnt=0#当前正在处理的帧
emodict=dict()#识别出情绪的结果
while cap.isOpened():#进入视频分析循环
ret, frame = cap.read()#读取当前视频帧
#如果返回值ret不是真,说明视频结束了
if not ret:
break#跳出循环
# 对当前帧分析,识别出当前视频帧中人物的表情
# 需要将enforce_detection设置为False
# 也就是不强制识别出人脸,否则如果没有识别到人脸就会抛出异常
result = DeepFace.analyze(img_path=frame,
actions=['emotion'],
enforce_detection=False)
#获取结果中强度最高的情感,保存在emotion中
emotion = result[0]['dominant_emotion']
#面部区域的位置和宽高保存在x,y,w和h中
x, y, w, h = result[0]['region'].values()
frame_cnt+=1#记录已经计算的帧数
#打印该帧是视频的第几帧,和这一帧对应的表情结果
print("frame %d : %s" % (frame_cnt, emotion))
#使用字典emodict,记录每种表情的数量
if emotion not in emodict:
emodict[emotion] = 0
emodict[emotion] += 1
#调用cv2.rectangle,将帧frame中的面部区域使用矩形进行框选
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 5)
#调用cv2.putText,将表情结果标记在矩形框的旁边
cv2.putText(frame, emotion, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 0), 5)
#调用out.write,将标记好的帧frame保存在输出视频中
out.write(frame)
#打印保存表情统计结果的字典emodict
print("")
print("emotion statistics:")
print("total frame : %d"%(frame_cnt))
for emotion in emodict:
ratio = emodict[emotion]*1.0/frame_cnt*100
print(f"{emotion:<10}frame num: emodict[emotion]:<5 ratio={ratio}")
cap.release()
out.release()
可以看到,代码中第一段被我注释掉了,那一段感兴趣的可以取消注释试着运行,用于图片人物表情分析。主要是做测试的。这里就不展示了。
代码我也看不太懂,反正就是调用各种库然后分析视频帧人物的表情,最后保存起来,感兴趣的可以搜一下各个库的使用,我太懒了就懒得搜了。唯一需要注意的点就是,如果出现了:“AttributeError: 'NoneType' object has no attribute 'shape'”这样的报错,大概率是你的视频路径里面包含了中文,参考解决方案:http://t.csdnimg.cn/5kycu
三、运行结果
对话框会返回每一帧的分析结果。
最后还会返回结果字典:
其实按我的分析,那段视频99%都是开心,可能因为人物的面部侧过去了,所以分析的有点出入。我自己录制的视频里全部是正脸分析就比较准确,至于那段报错,我也不知道为啥,我试着改了一下,把fourcc改成了mp4v后还是会继续报错,如果有人能帮我解答这个问题就好了。
原视频好像没法插入,那就只能展示一下截图了。
原视频截图:
运行后生成视频截图:
超级可爱的好吗?
我感觉opencv是不是对baby的表情分析有问题,它好像有时候把笑脸识别成了sad,不清楚为什么。
四、问题与反思
因为不想深究opencv的运行逻辑,所以直接使用后的结果让我有点摸不着头脑,不过我也只是想测试一下。
在家里实在是太无聊了,所以昨天就直接买票回了学校,就是说在动车上搞的代码真的存在很多问题,昨天晚上一直报错差点让我心态炸裂,主要的问题就是缩进,一开始因为缩进有问题,就是代码里面break后的一大串都是在循环内的,今早才发现我把它们弄到循环外了,所以一直运行错误。成功的那一刻真的很开心!