解放你的双手:借助深度学习用眼睛控制电脑

不知道你有没有这种经历:在电脑屏幕前看电影时吃得不亦乐乎,实在腾不出手来调整电影音量、屏幕亮度啥的。或者更哲学一点,有时面对屏幕实在太忙,无法双手打字或者快进到视频亮点部分,总之你懂得。

国外有位叫Julien Despois的程序员小哥对此也甚为苦恼,不过是因为前面这个问题,还是后面这个问题我们就不猜了。最终小哥通过深度学习技术成功解决了这个麻烦。今天就跟着他学习一下怎样借助人工智能,通过眼球运动来控制你的电脑,解放你的双手!

项目简介

我们想要啥?

这个项目的目标就是用我们的眼睛来触发电脑上的操作。而这个是个很宽泛的问题,所以我们需要具体指明我们想达到什么效果。

比如,我们可以检测眼睛什么时候直视屏幕的某个角落,然后以此着手研究。然而这种方法很有局限性,也不灵活,还需要各种硬编码操作。因此我们抛弃这种方式,而是改用循环神经网络学习识别完整的眼球运动。

数据

我们不会使用外部数据库,而是自己制作。这是有好处的,能让训练模型和做出预测使用相同的数据源。

毫无疑问,从我们的眼睛中提取信息的最有效方法就是使用专用特写摄像头。利用这样的硬件,我们能够直接追踪瞳孔的中心。但是我没有使用这样的外接摄像头,而是决定用我笔记本上自带的720 p摄像头。

工作流程

在开讲技术知识前,先看看工作流的各个步骤:

从摄像头中获取图像,找到眼睛

预处理图像,提取重要特征

根据历史数据预测当前眼球运动

图:工作流程

下面我们一步步看具体操作。

获取眼睛图像

找到眼睛

直接从摄像头开始,我们首先对图像进行降采样,将其转换为灰度图(颜色通道太过冗余)。这样能让后面的步骤更快些,帮我们实时运行模型。

对于检测部分,我们会使用OpenCV的HAAR Cascades,因为它们真的运行很快。经过些许调试,我们就能获取很好的结果,但直接尝试检测眼睛会导致出现假阳性结果。为了避免这种情况,我们不去在图像中寻找眼睛,而是发现图像中的人脸,然后再从人脸上找出眼睛。

等获取双眼的边界框后,我们就可以从最初的全尺寸摄像头快照中提取图像,这样我们就不会丢失任何信息。

预处理数据

等我们获取双眼图像后,就需要将它们处理成我们的数据集。我们可以简单将图像重新调整为固定尺寸——正方形,24 px——再使用直方图正态化来去除阴影。

插入原文此处GIF

图:提取眼睛的步骤

然后我们就可以将正态化后的图像直接用作输入,但这里还可以再多做一点工作,对我们的帮助会更大些。我们不使用眼睛图像,而是计算当前帧中眼睛和前面帧中眼睛的差异。这是对眼部动作进行编码的非常高效的方式,反正我们最后也需要这么做。

注意,除了下面GIF外的所有图形中,我们都会用眼睛图像来表示眼睛差异,因为眼睛差异在屏幕上看着太瘆人了。

插入原文此处GIF

等处理完眼睛图像后,我们会有两种选择,一个是将两个眼睛的图像分别当成同一类别的两个表示,另一个是将它们在一起使用,当成一张图像。我选择了后者,因为虽然两眼理应具有一致的动作,但有两个输入会让模型更稳定些。

图:将左右眼放在一起

创建数据集

录像

我为两种动作录下来50个数据样本,一个是眼球做出类似字母γ的动作,另一个是做出类似字母z的动作。我还对样本的姿势、幅度和速度做了一些变化,帮助模型有更好的泛化效果。此外我还添加了另外50个眼睛样本,包含了眼睛胡乱打转的动作以及静止帧。

图:眼球动作示例——γ状和z状动作

不幸的是,150个样本对这样的任务来说还是太少了,所以我们需要用新的样本来增强数据集。

数据增强

我们首先可以做的就是将任意序列长度固定为100帧。然后短于100帧的样本,将它们放慢速度,长于100帧,则加快速度。这是可行的,因为眼球运动速度并不能定义眼球的运动动作。

而且因为短于100帧的序列应该在100帧窗口中随时都能被检测到,我们可以添加填充后的样本。

图:对短于100帧的样本进行滑动窗口填充

通过这些数据增强方法,我们可以将数据集扩展至1000-2000个左右的样本。

最终数据集

我们先回过头一小会儿,理解一下我们的数据。我们记录了一些样本,并有对应的标签。每个样本都是一系列两张24 px宽的正方形图像。

注意,我们对每个眼睛都有一个数据集。

图:数据集的张量表示

模型

有了数据集后,我们就可以搭建一个合适的模型来从数据中学习。对模型的要求如下:

我们的模型应该能从每个时步中的两张眼睛图像中提取信息,将这些特征进行组合来预测眼睛所做的动作。

这样的一个复杂系统需要用到强大的人工智能模型——神经网络。我们看看怎样可以搭建一个符合我们需求的模型。神经网络层就像乐高玩具一样,我们要选择正确的模块,将它们放在正确的位置。

视觉特征——卷积神经网络

为了能从图像中提取信息,我们需要用到卷积神经网络。这种神经网络特别擅长处理图像,从中提取视觉特征。

我们需要分别处理每个眼睛,然后通过一个全连接层融合这些特征。最终得到的卷积神经网络会学习从每对眼睛中提取相关信息。

图:CNN网络——两个并行卷积层提取视觉特征后再融合

时序特征——循环神经网络

我们已经获取了图像的简单表示,那么需要能够按照顺序处理它们的模型。这里就要用到循环神经网络——LSTM网络。它会利用从当前时步提取的特征和其之前的状态来更新其当前状态。

最后等我们处理完图像的全部序列后,LSTM的状态就会被输入到一个softmax分类器中,以预测每个眼球动作的概率。

完整模型

我们最终得到的神经网络模型会将一系列的成对图像作为输入,输出每个眼球动作的概率。这里有关键一点,我们是在单一部分上搭建的模型,因此可以通过反向传播对模型进行端到端训练。

图:CNN网络从输入中提取视觉特征,LSTM网络在每一步处理特征

没有池化

我选择使用一个卷积层,没有池化,因为图像都非常小(宽24 px)。这样能保证我们保留尽可能多的信息。

另外,我本来也可以设置每个眼睛的权重共享,但最终没这么做,因为眼睛的姿势和形状还是存在轻微的差异。我没花时间测试这样做是否有用,但分享权重会让模型更轻量些。

做出预测&多线程

从摄像头/眼睛检测上以单独的线程运行分类器是绝对必需的,因为模型做出预测要花较长的时间(几十/几百微秒,但关系重大),而与此同时我们可能会错失一些信息[a]。由于眼球运动速度很快(约为1秒),所以我们想尽可能以最高帧率来捕捉它们,然后在可用的最新帧上做出预测[b]。

想象你在尽力记下某人说的话,但每写完一个词你就得把笔和纸交给另外一人去翻译你写的词,所以这样你无法记下完整的句子。

这一次你尽可能多地写下你听到的话,另外一人站在你旁边时不时瞄你的笔记,翻译你最新记下的词。

这两种方法都会遗漏信息,但相比根据不完整不连贯的数据做出很多预测,根据完整数据做出很少的预测更合理。

做出预测

项目的目标是评估借助深度学习和笔记本摄像头评估眼球运动识别的可行性。因此我没有花太多时间在最后一步,即利用预测来触发电脑命令。不过,我还是想让模型尽可能的好。

因此我必须解决模型预测的不准确性问题,当每秒钟要做出3个预测时,会出现很多假阳性结果。解决方法就是将模型在每个眼球动作期间所做的预测进行平均处理。

这就涉及到很棘手的帧率计算(见我代码库中的classifier.py文件),目的是匹配用于平均计算的窗口,以及一个动作在帧历史上停留的时间。背后的原理是如果模型在序列的开始、中间和结尾分别对动作做出预测,那么预测准确率会更高。

结果

经过训练后模型在测试集上达到了85+%的准确率,这相当不错了,因为我们的训练集是很小的(在数据增强之前)。如果再多点时间,再多投入点资源,对每个类别我本来可以记录至少100-200个样本,3-4个眼球动作。这样肯定会进一步提高模型的性能。

结语

本文我们讲解了如何借助深度学习技术通过眼球运动来操控电脑。我们可以使用OpenCV的HAAR Cascades从图像中发现眼睛,然后对数据清洗制作成数据集,将卷积神经网络和LSTM网络组成最终模型,从数据中学习后预测眼球的动作。

希望大家喜欢这个项目,也可以自己试着做一做,让AI解放你的双手,只需一个眼神就能操作电脑。

附本项目代码:

https://github.com/despoisj/DeepEyeControl

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180911G101C700?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券