如何追踪视频中的人脸 | RaPoSpectre 的个人博客

1,896 阅读5分钟

公司最近的项目需要将一段视频中的人头抠掉然后换上手机摄像头中的数据,因为视频中的人是来回走动的,所以人头也在随时变化位置,为了解决这个问题,首先博主想到了用人脸识别 + 视频追踪的方式应该能解决问题。于是博主开始探索这块的解决方案。

基于 opencv 的视频处理

python 中没有特别顺手的视频处理库 ( 其实之前找到一个还行的,记不住名字了,捂脸 ) 于是直接上 opencv ,opencv ( Open Source Computer Vision Library ) 是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。

首先是安装,博主 Mac 系统直接采用 linux 系统的源码编译安装方式:

  1. 下载最新版 opencv
  2. 解压缩进入 opencv 目录
  3. 新建目录 build ,进入 build 目录
  4. 安装 numpy : pip install numpy
  5. 用 cmake 编译 opencv 源码: cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..
  6. 安装 opencv : make install
  7. 添加 python 支持: 在 build/lib/ 目录下找到 cv2.so 文件,拷贝到你的 python 包目录 , 一般是 /usr/local/lib/pythonx.x/site-packages
  8. 打开 python shell 尝试 import cv2,无报错则安装成功

然后经过一阵面向 google 编程 , 视频追踪识别.py 交易 get :

# coding: utf-8
import cv2
import numpy as np
cv2.namedWindow("test")
cap=cv2.VideoCapture(0)
success, frame = cap.read()
color = (0,0,0)
classfier=cv2.CascadeClassifier("haarcascades/haarcascade_frontalface_alt.xml")
while success:
    success, frame = cap.read()
    size=frame.shape[:2]
    image=np.zeros(size,dtype=np.float16)
    image = cv2.cvtColor(frame, cv2.cv.CV_BGR2GRAY)
    cv2.equalizeHist(image, image)
    divisor=8
    h, w = size
    minSize=(w/divisor, h/divisor)
    faceRects = classfier.detectMultiScale(image, 1.2, 2, cv2.CASCADE_SCALE_IMAGE,minSize)
    if len(faceRects)>0:
        for faceRect in faceRects:
                x, y, w, h = faceRect
                cv2.rectangle(frame, (x, y), (x+w, y+h), color)
    cv2.imshow("test", frame)
    key=cv2.waitKey(10)
    c = chr(key & 255)
    if c in ['q', 'Q', chr(27)]:
        break
cv2.destroyWindow("test")

运行代码,可以看到程序打开了摄像头并且一个黑框正在实时追踪手机里乔布斯的脸,哇塞,这么快就解决了问题~

查看图片

嗯,其实我什么也没做,一切识别算法都是 opencv 提供好的, 特征数据全在 haarcascade_frontalface_alt.xml 里,这里要注意,这个文件需要自己拷贝到代码运行目录下,一般是在 /opencv/data/harcascades/ 目录下。

然后我们把视频数据由摄像头数据流改为本地视频文件, 只需要修改这一句:

  1. cap=cv2.VideoCapture(0)

改为:

  1. cap=cv2.VideoCapture("test.mp4")

然后运行:
查看图片

WTF ?! 你追踪的人脸呐? 后来想想,视频文件的视频质量参差不齐,远不及高清摄像头数据,同时视频中一般干扰因素会有很多,比如上面这个,大晚上的一个黑人穿着黑衣服跳舞,人眼也不好识别好吧,哈哈哈,再加上项目需要人工编辑定位到的人脸位置,所以博主经过一阵折腾后放弃了机器识别这条路。

不用机器识别,而且需要可以编辑视频定位的点,那么就需要在网站上编辑视频了,于是 Github js 视频处理库,嗯,果然一个都没有,毕竟 js 所处的位置天生就不适合做视频编辑这块,那该怎么破? 思前想后,博主决定干回老本行了! 写个 pc 端应用处理视频,然后传给服务器。

TVideoGrabber 处理编辑视频

TVideoGrabber 是 .net 框架下一个编辑处理视频的控件 ( opencv 也有 .net 的 api )这里博主觉得 .net 下视频处理有比 opencv 更好的解决方案,所以选择了 TVideoGrabber 。

帮助文档: www.datastead.com/_releases/v…

大概看了看文档,发现 TVideoGrabber 功能很强大。 我们来梳理下需求: 人工编辑定位视频流中人脸的位置。 嗯,再看看文档,发现一个功能符合我们的需求:

Graphic and text overlays

用一个图层当 overlay 覆盖视频中的人脸,然后根据视频时间推进随着人脸移动而移动,最后记录所有人脸改变位置的关键帧传回给服务器。 OK 问题不大 。

做的过程中遇到一点小问题,TVideoGrabber 控件本身的 OnMouseMove 事件中没有鼠标状态( 默认控件的 MouseMove 事件会返回鼠标状态 ),这样就没有办法得知鼠标是否点击,做鼠标拖动。 之后博主尝试继承 TVideoGrabber 的 MouseMove 类给它加上鼠标状态,悲剧的是太久不用 .net 了,和以前不一样了啊!!最后灵机一动,采用了曲线救国的方式: 在 OnMouseDown 事件中绑定移动事件, 在 OnMouseUp 事件中解绑移动事件。

之后做的过程没有太大的问题,虽然很久没写过了但底子还在嘛,这里我也就不放代码了,说一下需要注意的地方:

上传视频和关键帧时需要耗费比较长的时间,不能用主线程去做,不然整个程序就假死了,这里可以用多线程去做,也可以用代理 delegate ( 其实还是线程 )做。 同时,非 UI 控件生成线程 ( 主线程 ) 不能直接操作 UI 控件,需要代理委托给主线程操作。

  1. 为网络请求写代理方法:
// 先申明代理
public delegate string UploadHandler(string filename, string filepath, Dictionarykeymap, bool upload, Dictionary extraData);
// 编写代理方法
public string upload(string filename, string filepath, Dictionary keymap, bool upload, Dictionary extraData)
            {
                ...
                return string
            }
// 采用异步回调方式调用代理方法
 IAsyncResult result = handle.BeginInvoke(FILENAME, FILEPATH, Record, upload, extra, new AsyncCallback(call_back), "AsycState:OK");
 // 编写回调函数
 private void call_back(IAsyncResult result)
        {
            UploadHandler handler = (UploadHandler)((AsyncResult)result).AsyncDelegate;
            this.Invoke(new MethodInvoker(delegate {
                button5.Enabled = true;
                button5.Text = "上传";
                uploadKey = handler.EndInvoke(result);
                MessageBox.Show(uploadKey);
                upload = true;
            }));
        }
  1. UI 操作代理委托:
this.Invoke(new MethodInvoker(delegate {
                button5.Enabled = true;
                button5.Text = "上传";
                uploadKey = handler.EndInvoke(result);
                MessageBox.Show(uploadKey);
                upload = true;
            }));

直接在非主线程运行方法中使用 this.Invoke(new MethodInvoker(delegate { xxx })); 即可

最后测试编辑视频,大功告成:
查看图片

没想到吧,这篇文章最后竟然是写 C# 的,毕竟博主一开始也没想到最后会用 pc 端端方式解决 0.0