用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 selenium如何设置等待时间
Sep 15 Python
pyenv命令管理多个Python版本
Mar 26 Python
Django项目实战之用户头像上传与访问的示例
Apr 21 Python
python 数字类型和字符串类型的相互转换实例
Jul 17 Python
python梯度下降法的简单示例
Aug 31 Python
python最小生成树kruskal与prim算法详解
Jan 17 Python
python使用pipeline批量读写redis的方法
Feb 18 Python
pytorch 在sequential中使用view来reshape的例子
Aug 20 Python
Python装饰器使用你可能不知道的几种姿势
Oct 25 Python
IDLE下Python文件编辑和运行操作
Apr 25 Python
python3 os进行嵌套操作的实例讲解
Nov 19 Python
浅谈python数据类型及其操作
May 25 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
比较时间段一与时间段二是否有交集的php函数
2011/05/31 PHP
php中通过正则表达式下载内容中的远程图片的函数代码
2012/01/10 PHP
php中的注释、变量、数组、常量、函数应用介绍
2012/11/16 PHP
php图片水印添加、压缩、剪切的封装类实现
2020/04/18 PHP
学习ExtJS(二) Button常用方法
2009/10/07 Javascript
JS比较两个时间大小的简单示例代码
2013/12/20 Javascript
Javascript中的五种数据类型详解
2014/12/26 Javascript
angularJS 中$scope方法使用指南
2015/02/09 Javascript
jQuery实现的感应鼠标悬停图片色彩渐显效果
2015/03/03 Javascript
javascript中的previousSibling和nextSibling的正确用法
2015/09/16 Javascript
初识简单却不失优雅的Vue.js
2016/09/12 Javascript
js原生之焦点图转换加定时器实例
2016/12/12 Javascript
浅析上传头像示例及其注意事项
2016/12/14 Javascript
JavaScript设计模式之策略模式详解
2017/06/09 Javascript
JQuery选中select组件被选中的值方法
2018/03/08 jQuery
在Vue 中使用Typescript的示例代码
2018/09/10 Javascript
vue-cli 打包使用history模式的后端配置实例
2018/09/20 Javascript
vue单页面实现当前页面刷新或跳转时提示保存
2018/11/02 Javascript
Vue-CLI 3.X 部署项目至生产服务器的方法
2019/03/22 Javascript
javascript实现遮罩层动态效果实例
2019/05/14 Javascript
解决layui-open关闭自身窗口的问题
2019/09/10 Javascript
[01:00:22]DOTA2-DPC中国联赛定级赛 LBZS vs Magma BO3第三场 1月10日
2021/03/11 DOTA
Python中还原JavaScript的escape函数编码后字符串的方法
2014/08/22 Python
详解Python编程中time模块的使用
2015/11/20 Python
Python向Excel中插入图片的简单实现方法
2018/04/24 Python
tensorflow 加载部分变量的实例讲解
2018/07/27 Python
pandas通过字典生成dataframe的方法步骤
2019/07/23 Python
css3遮罩层镂空效果的多种实现方法
2020/05/11 HTML / CSS
HTML5 FormData 方法介绍以及实现文件上传示例
2017/09/12 HTML / CSS
HTML中使用SVG与SVG预定义形状元素介绍
2013/06/28 HTML / CSS
英国女性时尚精品店:THE DRESSING ROOM
2018/05/23 全球购物
CheapTickets香港机票预订网站:CheapTickets.hk
2019/06/26 全球购物
学生社团文化节开幕式主持词
2014/03/28 职场文书
培训学校2015年度工作总结
2015/07/20 职场文书
远程教育培训心得体会
2016/01/09 职场文书
pycharm代码删除恢复的方法
2021/06/26 Python