基于深度学习的表情识别服务搭建(一)

文章目录

  • 基于深度学习的表情识别服务搭建(一)
    • 背景
    • 识别服务设计
    • 实现方式的选择
    • dlib性能验证
    • 功能实现
    • 小结

背景

之前我完成了终端和服务端之间交流的全部内容,接下来我需要完成的是服务端识别线程的功能。完成之后,我们的系统应该就剩下前端和优化工作了

识别服务设计

我们称之为服务,是因为它并不能很好的和我们现有的系统无缝拼接,因为这个模块主要由Python实现,但是我们服务端最多只能是PHP+Java,我们的设计是这个样子的:

  • 后台的Java+PHP负责从终端和web端接受音频图片或上传的视频,而识别服务仅仅完成对已有图片的处理
  • 后台和识别服务通过网络方式通信,后台将从用户处接受到的文件名发送给识别服务,识别服务打开这些文件,完成识别,并将识别结果以json的格式发送给后台

有了具体框架,我们还需要定义识别服务的功能:

  • 找出图片中每一张脸的位置
  • 对图片中人脸的朝向进行分析
  • 对人脸的表情进行分析

那么我们就清楚了我们如何去实现这一系列功能

实现方式的选择

经过前期组内的研究,人脸识别应该使用caffe,darknet中虽然有相关的网络,但是darknet的输入只能是彩色图片,而人脸表情特征实际上使用黑白图片就能表述清楚,由于对框架了解不够深入,不知如何做出调整,所以我们先使用功能完善,组内熟悉的Caffe完成表情分类

在寻找脸部方位检测方法的时候我们找到了dlib,这个东西其实是挺不错的,有许多功能,包括人脸检测,头部位置检测,鉴于我们不得不使用它,并考虑到组内其他同学调试方便(如果直接使用darknet的话问题会比较有趣,因为组内所有同学中只有我的电脑有独立显卡,而darknet由于不成熟的原因并不支持传统的CPU加速方法,完成一张图片的检测,GPU只需几十毫秒,而CPU需要80秒),我们决定先完全使用dlib+caffe完成一套系统,darknet过一段时间再考虑

总结一下我们的选择:

  • 人脸检测:darknet(dlib)
  • 头部位置识别:dlib
  • 表情分类:caffe

dlib性能验证

经过我们的研究,dlib中人脸检测使用的是图形学方法,这在光线不均匀的情况下效果极差,表现为一下状态:

这样可以很明显的看出问题,但是基于深度学习方法的darknet似乎没有这样的烦恼

原本darknet的人脸检测框的没有这么方正,我对框选算法进行了一下修改,具体改动将在后续接入darknet的文章中详细介绍

功能实现

类定义:

class HPD():# 3D facial model coordinateslandmarks_3d_list = [np.array([[0.000, 0.000, 0.000],  # Nose tip[0.000, -8.250, -1.625],  # Chin[-5.625, 4.250, -3.375],  # Left eye left corner[5.625, 4.250, -3.375],  # Right eye right corner[-3.750, -3.750, -3.125],  # Left Mouth corner[3.750, -3.750, -3.125]  # Right mouth corner], dtype=np.double),np.array([[0.000000, 0.000000, 6.763430],  # 52 nose bottom edge[6.825897, 6.760612, 4.402142],  # 33 left brow left corner[1.330353, 7.122144, 6.903745],  # 29 left brow right corner[-1.330353, 7.122144, 6.903745],  # 34 right brow left corner[-6.825897, 6.760612, 4.402142],  # 38 right brow right corner[5.311432, 5.485328, 3.987654],  # 13 left eye left corner[1.789930, 5.393625, 4.413414],  # 17 left eye right corner[-1.789930, 5.393625, 4.413414],  # 25 right eye left corner[-5.311432, 5.485328, 3.987654],  # 21 right eye right corner[2.005628, 1.409845, 6.165652],  # 55 nose left corner[-2.005628, 1.409845, 6.165652],  # 49 nose right corner[2.774015, -2.080775, 5.048531],  # 43 mouth left corner[-2.774015, -2.080775, 5.048531],  # 39 mouth right corner[0.000000, -3.116408, 6.097667],  # 45 mouth central bottom corner[0.000000, -7.415691, 4.070434]  # 6 chin corner], dtype=np.double),np.array([[0.000000, 0.000000, 6.763430],  # 52 nose bottom edge[5.311432, 5.485328, 3.987654],  # 13 left eye left corner[1.789930, 5.393625, 4.413414],  # 17 left eye right corner[-1.789930, 5.393625, 4.413414],  # 25 right eye left corner[-5.311432, 5.485328, 3.987654]  # 21 right eye right corner], dtype=np.double)]# 2d facial landmark listlm_2d_index_list = [[30, 8, 36, 45, 48, 54],[33, 17, 21, 22, 26, 36, 39, 42, 45, 31, 35, 48, 54, 57, 8],  # 14 points[33, 36, 39, 42, 45]  # 5 points]# caffe 模型的各种路径caffe_model = 'face/myfacialnet_iter_59000.caffemodel'caffe_lable = 'face/labels.txt'caffe_deploy = 'face/deploy.prototxt'caffe_mean = 'face/mean.binaryproto'def __init__(self, lm_type=1, predictor="model/shape_predictor_68_face_landmarks.dat", verbose=True):self.bbox_detector = dlib.get_frontal_face_detector()self.landmark_predictor = dlib.shape_predictor(predictor)self.lm_2d_index = self.lm_2d_index_list[lm_type]self.landmarks_3d = self.landmarks_3d_list[lm_type]self.v = verbose# caffe 模型初始化一下self.net = caffe.Net(self.caffe_deploy, self.caffe_model, caffe.TEST)# 图片预处理设置self.transformer = caffe.io.Transformer({'data': self.net.blobs['data'].data.shape})  # 设定图片的shape格式(1,1,42,42)self.transformer.set_transpose('data', (2, 0, 1))  # 改变维度的顺序,由原始图片(42,42,1)变为(1,42,42)self.transformer.set_raw_scale('data', 255)  # 缩放到【0,255】之间#加载均值文件proto_data = open(self.caffe_mean, "rb").read()a = caffe.io.caffe_pb2.BlobProto.FromString(proto_data)m = caffe.io.blobproto_to_array(a)[0]self.transformer.set_mean('data', m.mean(1).mean(1))    #减去均值,前面训练模型时没有减均值,这儿就不用self.net.blobs['data'].reshape(1, 1, 42, 42)caffe.set_mode_cpu()def class2np(self, landmarks):coords = []for i in self.lm_2d_index:coords += [[landmarks.part(i).x, landmarks.part(i).y]]return np.array(coords).astype(np.int)def getLandmark(self, im):# Detect bounding boxes of facesif im is not None:rects = self.bbox_detector(im, 1)else:rects = []# make this an larger arraylandmarks_2ds = []if len(rects) <= 0:return None, Nonefor rect in rects:# Detect landmark of first facelandmarks_2d = self.landmark_predictor(im, rect)  # 这里获得特征点的集合,我们需要这个结果来切脸# Choose specific landmarks corresponding to 3D facial model## and i decided to move it to another place!! --Tecelectalandmarks_2ds.append(landmarks_2d)return landmarks_2ds, rectsdef getHeadpose(self, im, landmarks_2d, verbose=False):h, w, c = im.shapef = w  # column size = x axis length (focal length)u0, v0 = w / 2, h / 2  # center of image planecamera_matrix = np.array([[f, 0, u0],[0, f, v0],[0, 0, 1]], dtype=np.double)# Assuming no lens distortiondist_coeffs = np.zeros((4, 1))# Find rotation, translation(success, rotation_vector, translation_vector) = cv2.solvePnP(self.landmarks_3d, landmarks_2d, camera_matrix,dist_coeffs)if verbose:print("Camera Matrix:\n {0}".format(camera_matrix))print("Distortion Coefficients:\n {0}".format(dist_coeffs))print("Rotation Vector:\n {0}".format(rotation_vector))print("Translation Vector:\n {0}".format(translation_vector))return rotation_vector, translation_vector, camera_matrix, dist_coeffs# rotation vector to euler anglesdef getAngles(self, rvec, tvec):rmat = cv2.Rodrigues(rvec)[0]P = np.hstack((rmat, tvec))  # projection matrix [R | t]degrees = -cv2.decomposeProjectionMatrix(P)[6]rx, ry, rz = degrees[:, 0]return [rx, ry, rz]# return image and anglesdef processImage(self, im, draw=True):faces_dets = dlib.full_object_detections()prob_list = []draws = []all_angles = []# landmark Detectionim_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)landmarks_2ds, bboxs = self.getLandmark(im_gray)  # 这里直接使用这个数组# if no face deteced, return original imageif bboxs is None:return im, Nonefor i in range(len(bboxs)):# Headpose Detectionlandmarks_2d = landmarks_2ds[i]bbox = bboxs[i]faces_dets.append(landmarks_2d)landmarks_2d = self.class2np(landmarks_2d)landmarks_2d = landmarks_2d.astype(np.double)rvec, tvec, cm, dc = self.getHeadpose(im, landmarks_2d)angles = self.getAngles(rvec, tvec)all_angles.append(angles)if draw:draws.append(Draw(im, angles, bbox, landmarks_2d, rvec, tvec, cm, dc, b=10.0))images = dlib.get_face_chips(im, faces_dets, size=320)# 接入caffe完成分类for face in images:net_input = cv2ski(face)net_input = self.transformer.preprocess("data",net_input)self.net.blobs['data'].data[...] = net_inputself.net.forward()prob_list.append(self.net.blobs['prob'].data[0].flatten())for d in draws:im = d.drawAll()return im, bboxs, all_angles, prob_listdef processBatch(self, fileNames, in_dir, out_dir):if in_dir[-1] != '/': in_dir += '/'if out_dir[-1] != '/': out_dir += '/'batch_json = []for fn in fileNames:im = cv2.imread(in_dir + fn)im, bboxes, angles, probs = self.processImage(im)cv2.imwrite(out_dir + fn, im)pic_json = []for i in range(len(bboxes)):pic_json.append({"x" : (bboxes[i].left() + bboxes[i].right()) / 2,"y" : (bboxes[i].top() + bboxes[i].bottom()) / 2,"rx" : angles[i][0].astype("float"),"ry" : angles[i][1].astype("float"),"rz" : angles[i][2].astype("float"),"angry" : probs[i][0].astype("float"),"digust" : probs[i][1].astype("float"),"fear" : probs[i][2].astype("float"),"happy" : probs[i][3].astype("float"),"sad" : probs[i][4].astype("float")})batch_json.append(pic_json)return batch_json

这个类的实现过程中我们参考了已经有的项目,对它进行了解读和完善,最终得到这个类,其中核心的方法是processImage,它首先完成了人脸的检测,然后使用自己支持的深度学习算法完成了landmark的标定,之后将画出的人脸交给caffe模型完成表情分类

辅助函数定义:

def cv2ski(cv_mat):'''将脸从cv2转换成ski的格式:param cv_mat::return:'''# rgbgr & normalizeski_mat = np.zeros((48,48,1), dtype="float32")cv_mat = cv2.cvtColor(cv_mat, cv2.COLOR_BGR2GRAY, dstCn=1)cv_mat = cv2.resize(cv_mat, (48, 48), interpolation=cv2.INTER_LINEAR) / 255.ski_mat = cv_mat[:,:,np.newaxis]#cv2.imwrite("middle.jpg", cv_mat)# cv2.imshow("middle", ski_mat)#cv2.waitKey(0)return ski_mat.astype("float32")

正如注释描述的一样,这个函数完成的是格式转换功能,因为caffe输入需要的格式是skimage.io获得的,而我们整体的处理过程使用opencv完成的,这就要求我们完成格式转换另外,从原图上截下的脸是彩色的,我们还需要完成色域转换

执行文件:

# -- encoding:utf-8 --
#import landmarkPredict as headpose
import socket
import hpd
import jsonLOCALHOST = "127.0.0.1"
sock = Nonedef sockInit(port):'''接收文件名,一次一个batch:param port: 接收使用的port,字符串格式!:return:'''global socksock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.bind((LOCALHOST, port))sock.listen(1)returndef recvFileName(conn):'''利用已经建立的连接完成一批文件的接收:param conn::return: 一堆文件名'''data = conn.recv(4096)file_name = data.split('\n')return file_name[:len(file_name) - 1]def sendJson(conn, jList):'''将识别返回的结果发回请求端:param conn: 之前与请求端建立的连接:param jList: 返回的一批图片的识别结果:return: shi'''for pic_json in jList:conn.send(json.dumps(pic_json))print("---finish sending res of 1 pic---")print(pic_json)if __name__ == "__main__":sockInit(10101)headpose = hpd.HPD()while 1:conn, addr = sock.accept()print("Accpeting conn from {}".format(addr))batch_name = recvFileName(conn)res = headpose.processBatch(batch_name, "/srv/ftp/pic", "/srv/ftp/res")#这里指定正确的路径就可以完成任务sendJson(conn,res)

依旧是首先定义基本的网络传输函数,接受文件名,并完成从服务端接收所有json对象,并转换成字符串发送的功能

小结

这一部分还会被进一步完善,上面我们提到,dlib的性能存在问题,接下来我们会将darknet接入,着手解决这些问题

[实训题目EmoProfo]基于深度学习的表情识别服务搭建(一)相关推荐

  1. 基于深度学习的表情识别系统(中英文版本)

    文件大小:19M 代码行数:201行(主程序) 开发环境:Python3.8.OpenCV4.5 点击下载:点击下载 简要概述:基于深度学习的表情识别系统 1.中英文显示均可 2.可实时检测 图片视频 ...

  2. 山东大学项目实训小组一——基于深度学习的AI视频剪辑器“易剪”

    技术要点:图像处理 计算机视觉 深度学习 多媒体前端 一.项目研究背景: 随着短视频热潮的兴起,越来越多的人投入精力到了视频剪辑视频制作之中.然而利用现有的视频剪辑工具,剪辑一段视频是非常麻烦的,尤其 ...

  3. 表单识别(五)——票据识别-论文研读:基于深度学习的票据识别系统设计与实现,卞飞飞(上)

    (论文研读:基于深度学习的票据识别系统设计与实现,卞飞飞:) 引言: 传统的文本检测与识别算法主要指利用数字图像处理等非深度学习技术进行文本检测的方法,依赖于数字图像特征(如颜色.纹理.轮廓.形状等特 ...

  4. 基于深度学习的口罩识别与检测PyTorch实现

    基于深度学习的口罩识别与检测PyTorch实现 1. 设计思路 1.1 两阶段检测器:先检测人脸,然后将人脸进行分类,戴口罩与不戴口罩. 1.2 一阶段检测器:直接训练口罩检测器,训练样本为人脸的标注 ...

  5. 基于深度学习的脑电图识别 综述篇(三)模型分析

    作者|Memory逆光 本文由作者授权分享 导读 脑电图(EEG)是一个复杂的信号,一个医生可能需要几年的训练并利用先进的信号处理和特征提取方法,才能正确解释其含义.而如今机器学习和深度学习的发展,大 ...

  6. 基于深度学习的脑电图识别 综述篇(二)数据采样及处理

    作者|Memory逆光 本文由作者授权分享 导读 脑电图(EEG)是一个复杂的信号,一个医生可能需要几年的训练并利用先进的信号处理和特征提取方法,才能正确解释其含义.而如今机器学习和深度学习的发展,大 ...

  7. python dlib caffe人脸相似度_基于深度学习的人脸识别系统(Caffe+OpenCV+Dlib)【一】如何配置caffe属性表...

    前言 基于深度学习的人脸识别系统,一共用到了5个开源库:OpenCV(计算机视觉库).Caffe(深度学习库).Dlib(机器学习库).libfacedetection(人脸检测库).cudnn(gp ...

  8. 开发基于深度学习的人脸识别【考勤/签到】系统

    开发基于深度学习的人脸识别[考勤/签到]系统 人脸识别介绍 平台环境需求 技术点 系统流程 细节设计 人脸检测 人脸关键点定位 人脸特征提取 模型的训练 模型的部署 MySQL数据库的使用 MFC工程 ...

  9. 基于深度学习的人脸识别系统(Caffe+OpenCV+Dlib)【三】VGG网络进行特征提取

    前言 基于深度学习的人脸识别系统,一共用到了5个开源库:OpenCV(计算机视觉库).Caffe(深度学习库).Dlib(机器学习库).libfacedetection(人脸检测库).cudnn(gp ...

最新文章

  1. 测试openstack neutron的网络连通性
  2. Jupyter notebook与Spyder,以及Jupyter notebook与Spyder集成插件
  3. rapter求n的阶乘流程图_RAPTOR程序设计例题参考答案
  4. jzoj4229-学习神技【逆元,费马小定理】
  5. DAS 2020 诚征论文及赞助!
  6. Attribute in C#
  7. git常用命令常用场景
  8. vim介绍与一些使用方式
  9. 花4个月时间整理出《Spring揭秘》的文字版的感受
  10. 计算机毕业设计asp.net的旅游网站(源码+系统+mysql数据库+Lw文档)
  11. 虚拟主机需要备案吗?
  12. CentOS 6.4 电信ADSL拨号上网网络配置
  13. 2018年,硅谷的P2P公司们为啥没跑路?
  14. PLC与运动控制器有什么不同?为什么运动控制器比PLC好?
  15. T-test数据分析
  16. Ilog、Drools、Jess规则引擎的Rule Language 对比
  17. ESP01S CH340 一键下载电路设计
  18. Unicode、UTF-8、ASCII等编码方式浅述
  19. android logo:内核、android开机动画【转】
  20. SPSS相关分析(实例操作版)

热门文章

  1. springboot集成cas3.5.2
  2. 【图像分类】2022-CMT CVPR
  3. 杭州计算机中级职称评级流程,浙江省杭州评中级工程师职称流程是怎么样,2021年杭州中级工程师职称怎么准备...
  4. python 交易日_使用python和tushare股票交易日历数据,判断节假日周末休市
  5. OpenGL如何画球体?
  6. IT项目启示录——来自泰坦尼克号的教训(第十篇)(转)
  7. 批归一化作用_批归一化(Batch Normalization)
  8. eps罗马柱头制作方法_元阳好的eps罗马柱批发价格,罗马柱子制作
  9. MongoDB——MongoDB的日常维护
  10. java小游戏大鱼吃小鱼入门(15min写一个小游戏)