基于OpenCV对神经网络预处理人脸图像的快速指南

做者|Jaime Duránx
编译|Flin
来源|mediumhtml

目前我正在研究一个涉及面部分类的计算机视觉问题。这一般意味着应用深度学习,所以在将图像注入到咱们的神经网络以前须要一个特殊的预处理阶段。java

为了提升咱们的模型精度,这是一项很是重要的任务,经过如下几个简单的步骤能够很容易地完成。对于本文,咱们可使用OpenCV:一个高度优化的计算机视觉开源库,在C++、java和Python中可用。python

这是一篇简短的文章,包含了一些基本的指导原则、示例和代码,你可能须要将它们应用到每一个面部分类或识别问题上。git

注意:本文中使用的全部静态图像都来自 https://imgflip.com/memetemplatesgithub

图片载入

咱们将使用imread()函数加载图像,指定文件的路径和mode。第二个参数对于动态运行基本通道和深度转换很是重要。数组

img = cv2.imread('path/image.jpg', cv2.IMREAD_COLOR)

要查看图像,咱们有imshow()函数:网络

cv2.imshow(img)

若是你写的是类型(img),你会看到尺寸是(height, weight, channels)。机器学习

咱们的彩色图像有3个通道:蓝色,绿色和红色(在OpenCV中按这个顺序)。ide

咱们能够轻松查看单个通道:函数

# Example for green channel
img[:, :, 0]; img[:, :, 2]; cv2.imshow(img)

灰度版本

为了不在面部图像分类中分心,使用黑白图片是个好主意(也可能不是!)你能够二者都试试)。为了获得灰度版本,咱们只须要在图像加载函数中指定,将适当的值做为第二个参数传递:

img = cv2.imread('path/image.jpg', cv2.IMREAD_GRAYSCALE)

如今咱们的图像有了一个单独的通道!

人脸和眼睛检测

当处理面部分类问题时,咱们可能想要作面部检测,以验证(是否有脸?),裁剪和拉直咱们的图像。咱们将使用OpenCV中包含的基于Haar特性的级联分类器进行对象检测。(https://docs.opencv.org/2.4/modules/objdetect/doc/cascade_classification.html)

首先,咱们选择预先训练的人脸和眼睛检测分类器。有一个可用的XML文件列表,咱们可使用此列表:

1)对于人脸检测,OpenCV提供如下(从最宽松的先验到最严格的先验):

  • haarcascade_frontalface_default.xml

  • haarcascade_frontalface_alt.xml

  • haarcascade_frontalface_alt2.xml

  • haarcascade_frontalface_alt_tree.xml

2) 对于眼睛检测,咱们能够选择两种方法:

  • haarcascade_eye.xml

  • haarcascade_eye_tree_eyegasses.xml

咱们以这种方式加载预先训练的分类器:

face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + ‘haarcascade_frontalface_default.xml’)
eyes_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + ‘haarcascade_eye.xml’)

你能够测试几种组合。记住,在全部状况下,它们中没有一个是最优的(若是第一个分类器失败,你能够尝试第二个分类器,或者甚至尝试全部分类器)。

对于人脸检测,咱们使用如下代码:

faces_detected = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors=5)

结果是一个数组,包含全部检测到的人脸。咱们能够很容易地画出矩形:

(x, y, w, h) = faces_detected[0]
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 1);
cv2.imshow(img)

对于眼睛,咱们以相似的方式进行搜索,但将搜索范围缩小到面部矩形:

eyes = eyes_cascade.detectMultiScale(img[y:y+h, x:x+w])
for (ex, ey, ew, eh) in eyes:
    cv2.rectangle(img, (x+ex, y+ey), (x+ex+ew, y+ey+eh), 
                  (255, 255, 255), 1)

成啦!

虽然这是预期的结果,但咱们会遇到不少其余方面的问题。不少时候,咱们没有正面和清晰的人的脸,甚至……

没有眼睛:

眼睛是被白色包围的黑色污点:

此处有4只眼,仅检测到3只眼:

拉直脸部

经过计算两只眼睛之间的角度,咱们能够拉直脸部图像(这很容易)。计算后,咱们仅需两个步骤便可旋转图像:

rows, cols = img.shape[:2]
M = cv2.getRotationMatrix2D((cols/2, rows/2), <angle>, 1)
img_rotated = cv2.warpAffine(face_orig, M, (cols,rows))

裁剪脸部

为了帮助咱们的神经网络完成面部分类任务,最好去掉背景、衣服或配饰等外部干扰信息。在这种状况下,裁剪脸部是一个很好的选择。

咱们须要作的第一件事是从拉直的图像中再次获得人脸矩形。而后咱们须要作一个决定:咱们能够按原样裁剪矩形区域,或者添加一个额外的填充,这样咱们能够得到更多的空间。

这取决于要解决的具体问题(按年龄、性别、种族等分类);也许你想要更多的头发;也许不须要。

最后,裁剪(p表示填充):

cv2.imwrite('crop.jpg', img_rotated[y-p+1:y+h+p, x-p+1:x+w+p])

看!这张脸是孤立的,几乎能够进行深度学习了

图像缩放

神经网络须要全部的输入图像具备相同的形状和大小,由于GPU在同一时间对一批图像应用相同的指令,以达到超级快的速度。咱们能够动态地调整它们的大小,但这可能不是一个好主意,由于在训练期间将对每一个文件执行多个转换。

所以,若是咱们的数据集有不少图像,咱们应该考虑在训练阶段以前实现批量调整大小的过程。

在OpenCV中,咱们可使用resize()函数执行向下缩放和向上缩放,有几种可用的插值方法。指定最终尺寸的例子:

cv2.resize(img, (<width>, <height>), interpolation=cv2.INTER_LINEAR)

为了缩小图像,OpenCV建议使用INTER_AREA插值,而要放大图像,可使用INTER_CUBIC(慢)或INTER_LINEAR(快,效果仍然不错)。

最后是质量和时间之间的权衡。

我作了一个快速的升级比较:

前两幅图像的质量彷佛更高(但你能够观察到一些压缩伪像)。

线性方法的结果明显更平滑而且噪点更少。

最后一个是像素化的。

归一化

咱们可使用normalize()函数应用视觉归一化,以修复很是暗/亮的图片(甚至能够修复低对比度)。

该归一化类型(https://docs.opencv.org/3.4/d2/de8/group__core__array.html#gad12cefbcb5291cf958a85b4b67b6149f) 在函数参数中指定:

norm_img = np.zeros((300, 300))
norm_img = cv2.normalize(img, norm_img, 0, 255, cv2.NORM_MINMAX)

例子:

当使用图像做为深度卷积神经网络的输入时,不须要应用这种归一化。

在实践中,咱们将对每一个通道进行适当的归一化,好比减去平均值,而后除以像素级的标准差(所以咱们获得平均值0和误差1)。若是咱们使用迁移学习,最好的方法老是使用预先训练的模型统计数据。

结论

在处理人脸分类/识别问题时,若是输入的图像不是护照图片,则检测和分离出人脸是一项常见的任务。

OpenCV是一个很好的图像预处理库,但不只仅如此。它也是一个强大的工具,为许多计算机视觉应用…

来看文档吧!

但愿你喜欢这篇文章!

原文连接:https://medium.com/yottabytes/a-quick-guide-on-preprocessing-facial-images-for-neural-networks-using-opencv-in-python-47ee3438abd4

欢迎关注磐创AI博客站:
http://panchuang.net/

sklearn机器学习中文官方文档:
http://sklearn123.com/

欢迎关注磐创博客资源汇总站:
http://docs.panchuang.net/