用Python获取摄像头并实时控制人脸的实现示例


Posted in Python onJuly 11, 2019

实现流程

从摄像头获取视频流,并转换为一帧一帧的图像,然后将图像信息传递给opencv这个工具库处理,返回灰度图像(就像你使用本地静态图片一样)

程序启动后,根据监听器信息,使用一个while循环,不断的加载视频图像,然后返回给opencv工具呈现图像信息。

创建一个键盘事件监听,按下"d"键,则开始执行面部匹配,并进行面具加载(这个过程是动态的,你可以随时移动)。

面部匹配使用Dlib中的人脸检测算法来查看是否有人脸存在。如果有,它将为每个人脸创建一个结束位置,眼镜和烟卷会移动到那里结束。

然后我们需要缩放和旋转我们的眼镜以适合每个人的脸。我们将使用从Dlib的68点模型返回的点集来找到眼睛和嘴巴的中心,并为它们之间的空间旋转。

在我们实时获取眼镜和烟卷的最终位置后,眼镜和烟卷从屏幕顶部进入,开始匹配你的眼镜和嘴巴。

假如没有人脸,程序会直接返回你的视频信息,不会有面具移动的效果。

默认一个周期是4秒钟。然后你可以通过"d"键再次检测。

程序退出使用"q"键。

这里我将这个功能抽象成一个面具加载服务,请跟随我的代码一窥究竟吧。

1.导入对应的工具包

from time import sleep

import cv2
import numpy as np
from PIL import Image
from imutils import face_utils, resize

try:
  from dlib import get_frontal_face_detector, shape_predictor
except ImportError:
  raise

创建面具加载服务类DynamicStreamMaskService及其对应的初始化属性: 

class DynamicStreamMaskService(object):
  """
  动态黏贴面具服务
  """

  def __init__(self, saved=False):
    self.saved = saved # 是否保存图片
    self.listener = True # 启动参数
    self.video_capture = cv2.VideoCapture(0) # 调用本地摄像头
    self.doing = False # 是否进行面部面具
    self.speed = 0.1 # 面具移动速度
    self.detector = get_frontal_face_detector() # 面部识别器
    self.predictor = shape_predictor("shape_predictor_68_face_landmarks.dat") # 面部分析器
    self.fps = 4 # 面具存在时间基础时间
    self.animation_time = 0 # 动画周期初始值
    self.duration = self.fps * 4 # 动画周期最大值
    self.fixed_time = 4 # 画图之后,停留时间
    self.max_width = 500 # 图像大小
    self.deal, self.text, self.cigarette = None, None, None # 面具对象

按照上面介绍,我们先实现读取视频流转换图片的函数: 

def read_data(self):
  """
  从摄像头获取视频流,并转换为一帧一帧的图像
  :return: 返回一帧一帧的图像信息
  """
  _, data = self.video_capture.read()
  return data

接下来我们实现人脸定位函数,及眼镜和烟卷的定位: 

def get_glasses_info(self, face_shape, face_width):
  """
  获取当前面部的眼镜信息
  :param face_shape:
  :param face_width:
  :return:
  """
  left_eye = face_shape[36:42]
  right_eye = face_shape[42:48]

  left_eye_center = left_eye.mean(axis=0).astype("int")
  right_eye_center = right_eye.mean(axis=0).astype("int")

  y = left_eye_center[1] - right_eye_center[1]
  x = left_eye_center[0] - right_eye_center[0]
  eye_angle = np.rad2deg(np.arctan2(y, x))

  deal = self.deal.resize(
    (face_width, int(face_width * self.deal.size[1] / self.deal.size[0])),
    resample=Image.LANCZOS)

  deal = deal.rotate(eye_angle, expand=True)
  deal = deal.transpose(Image.FLIP_TOP_BOTTOM)

  left_eye_x = left_eye[0, 0] - face_width // 4
  left_eye_y = left_eye[0, 1] - face_width // 6

  return {"image": deal, "pos": (left_eye_x, left_eye_y)}

def get_cigarette_info(self, face_shape, face_width):
  """
  获取当前面部的烟卷信息
  :param face_shape:
  :param face_width:
  :return:
  """
  mouth = face_shape[49:68]
  mouth_center = mouth.mean(axis=0).astype("int")
  cigarette = self.cigarette.resize(
    (face_width, int(face_width * self.cigarette.size[1] / self.cigarette.size[0])),
    resample=Image.LANCZOS)
  x = mouth[0, 0] - face_width + int(16 * face_width / self.cigarette.size[0])
  y = mouth_center[1]
  return {"image": cigarette, "pos": (x, y)}

def orientation(self, rects, img_gray):
  """
  人脸定位
  :return:
  """
  faces = []
  for rect in rects:
    face = {}
    face_shades_width = rect.right() - rect.left()
    predictor_shape = self.predictor(img_gray, rect)
    face_shape = face_utils.shape_to_np(predictor_shape)
    face['cigarette'] = self.get_cigarette_info(face_shape, face_shades_width)
    face['glasses'] = self.get_glasses_info(face_shape, face_shades_width)

    faces.append(face)

  return faces

刚才我们提到了键盘监听事件,这里我们实现一下这个函数: 

def listener_keys(self):
  """
  设置键盘监听事件
  :return:
  """
  key = cv2.waitKey(1) & 0xFF
  if key == ord("q"):
    self.listener = False
    self.console("程序退出")
    sleep(1)
    self.exit()

  if key == ord("d"):
    self.doing = not self.doing

接下来我们来实现加载面具信息的函数: 

def init_mask(self):
  """
  加载面具
  :return:
  """
  self.console("加载面具...")
  self.deal, self.text, self.cigarette = (
    Image.open(x) for x in ["images/deals.png", "images/text.png", "images/cigarette.png"]
  )

上面基本的功能都实现了,我们该实现画图函数了,这个函数原理和之前我写的那篇用AI人脸识别技术实现抖音特效实现是一样的,这里我就不赘述了,可以去github或Python中文社区微信公众号查看。

def drawing(self, draw_img, faces):
  """
  画图
  :param draw_img:
  :param faces:
  :return:
  """
  for face in faces:
    if self.animation_time < self.duration - self.fixed_time:
      current_x = int(face["glasses"]["pos"][0])
      current_y = int(face["glasses"]["pos"][1] * self.animation_time / (self.duration - self.fixed_time))
      draw_img.paste(face["glasses"]["image"], (current_x, current_y), face["glasses"]["image"])

      cigarette_x = int(face["cigarette"]["pos"][0])
      cigarette_y = int(face["cigarette"]["pos"][1] * self.animation_time / (self.duration - self.fixed_time))
      draw_img.paste(face["cigarette"]["image"], (cigarette_x, cigarette_y),
              face["cigarette"]["image"])
    else:
      draw_img.paste(face["glasses"]["image"], face["glasses"]["pos"], face["glasses"]["image"])
      draw_img.paste(face["cigarette"]["image"], face["cigarette"]["pos"], face["cigarette"]["image"])
      draw_img.paste(self.text, (75, draw_img.height // 2 + 128), self.text)

既然是一个服务类,那该有启动与退出函数吧,最后我们来写一下吧。

简单介绍一下这个start()函数, 启动后根据初始化监听信息,不断监听视频流,并将流信息通过opencv转换成图像展示出来。
并且调用按键监听函数,不断的监听你是否按下"d"键进行面具加载,如果监听成功,则进行图像人脸检测,并移动面具,
并持续一个周期的时间结束,面具此时会根据你的面部移动而移动。最终呈现文章顶部图片的效果.

def start(self):
  """
  启动程序
  :return:
  """
  self.console("程序启动成功.")
  self.init_mask()
  while self.listener:
    frame = self.read_data()
    frame = resize(frame, width=self.max_width)
    img_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    rects = self.detector(img_gray, 0)
    faces = self.orientation(rects, img_gray)
    draw_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
    if self.doing:
      self.drawing(draw_img, faces)
      self.animation_time += self.speed
      self.save_data(draw_img)
      if self.animation_time > self.duration:
        self.doing = False
        self.animation_time = 0
      else:
        frame = cv2.cvtColor(np.asarray(draw_img), cv2.COLOR_RGB2BGR)
    cv2.imshow("hello mask", frame)
    self.listener_keys()

def exit(self):
  """
  程序退出
  :return:
  """
  self.video_capture.release()
  cv2.destroyAllWindows()

最后,让我们试试:

if __name__ == '__main__':
  ms = DynamicStreamMaskService()
  ms.start()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
详尽讲述用Python的Django框架测试驱动开发的教程
Apr 22 Python
python回调函数用法实例分析
May 09 Python
python引用DLL文件的方法
May 11 Python
在Python的列表中利用remove()方法删除元素的教程
May 21 Python
Python的多态性实例分析
Jul 07 Python
Python下rrdtool模块的基本使用方法
Nov 13 Python
一个基于flask的web应用诞生 记录用户账户登录状态(6)
Apr 11 Python
Python爬虫——爬取豆瓣电影Top250代码实例
Apr 17 Python
python 叠加等边三角形的绘制的实现
Aug 14 Python
django-rest-swagger的优化使用方法
Aug 29 Python
python 公共方法汇总解析
Sep 16 Python
Python实现Excel自动分组合并单元格
Feb 22 Python
python实现LBP方法提取图像纹理特征实现分类的步骤
Jul 11 #Python
Python用字典构建多级菜单功能
Jul 11 #Python
Python + OpenCV 实现LBP特征提取的示例代码
Jul 11 #Python
python切片的步进、添加、连接简单操作示例
Jul 11 #Python
python 日期排序的实例代码
Jul 11 #Python
Python qqbot 实现qq机器人的示例代码
Jul 11 #Python
python的一些加密方法及python 加密模块
Jul 11 #Python
You might like
linux实现php定时执行cron任务详解
2013/12/24 PHP
php简单实现快速排序的方法
2015/04/04 PHP
Yii2超好用的日期和时间组件(值得收藏)
2016/05/05 PHP
浅析PHP数据导出知识点
2018/02/17 PHP
javascript 树形导航菜单实例代码
2013/08/13 Javascript
判断输入是否为空,获得输入类型的JS代码
2013/10/30 Javascript
jQuery移除元素自动解绑事件实现思路及代码
2014/05/31 Javascript
js读取json的两种常用方法示例介绍
2014/10/19 Javascript
JS获取时间的方法
2015/01/21 Javascript
jquery+css实现动感的图片切换效果
2015/11/25 Javascript
jQuery旋转木马式幻灯片轮播特效
2015/12/04 Javascript
javascript设置文本框光标的方法实例小结
2016/11/04 Javascript
JS实现一个简单的日历
2017/02/22 Javascript
Mongoose经常返回e11000 error的原因分析
2017/03/29 Javascript
vue中mint-ui环境搭建详细介绍
2017/04/06 Javascript
基于vue中keep-alive缓存问题的解决方法
2018/09/21 Javascript
微信小程序配置服务器提示验证token失败的解决方法
2019/04/03 Javascript
Layui弹出层 加载 做编辑页面的方法
2019/09/16 Javascript
js blob类型url的视频下载问题的解决
2019/11/29 Javascript
微信小程序实现滑动翻页效果(完整代码)
2019/12/06 Javascript
vue实现打地鼠小游戏
2020/08/21 Javascript
[02:56]DOTA2英雄基础教程 巨魔战将
2013/12/10 DOTA
python 计算数组中每个数字出现多少次--“Bucket”桶的思想
2017/12/19 Python
Python网络编程之TCP与UDP协议套接字用法示例
2018/02/02 Python
python实现简单的单变量线性回归方法
2018/11/08 Python
解决win7操作系统Python3.7.1安装后启动提示缺少.dll文件问题
2019/07/15 Python
python实现引用其他路径包里面的模块
2020/03/09 Python
Python 解决相对路径问题:&quot;No such file or directory&quot;
2020/06/05 Python
基于python计算滚动方差(标准差)talib和pd.rolling函数差异详解
2020/06/08 Python
python 实时调取摄像头的示例代码
2020/11/25 Python
求职信的七个关键技巧
2014/02/05 职场文书
爸爸的花儿落了教学反思
2014/02/20 职场文书
个人贷款收入证明
2014/10/26 职场文书
pytest配置文件pytest.ini的详细使用
2021/04/17 Python
golang如何去除多余空白字符(含制表符)
2021/04/25 Golang
详解Laravel框架的依赖注入功能
2021/05/27 PHP