快速指南:使用OpenCV预处理神经网络中的面部图像的
重磅干货,第一时间送达
本期将介绍脸部检测、眼睛检测;图像拉直、裁剪、调整大小、归一化等内容
目前,涉及面部分类的计算机视觉问题,通常都需要使用深度学习。因此在将图像输入神经网络之前,需要经过一个预处理阶段,以便达到更好的分类效果。
图像预处理通常来说非常简单,只需执行几个简单的步骤即可轻松完成。但为了提高模型的准确性,这也是一项非常重要的任务。对于这些问题,我们可以使用OpenCV完成:一个针对(实时)计算机视觉应用程序的高度优化的开源库,包括C ++,Java和Python语言。
接下来我们将一起探索可能会应用在每个面部分类或识别问题上应用的基本原理,示例和代码。
注意:下面使用的所有图像均来自memes.。
图片载入
我们使用该imread()函数加载图像,并指定文件路径和图像模式。第二个参数对于运行基本通道和深度转换很重要。
img = cv2.imread('path/image.jpg', cv2.IMREAD_COLOR)
要查看图像可以使用imshow()功能:
cv2.imshow(img)
如果使用的type(img)话,将显示该图像的尺寸包括高度、重量、通道数。
彩色图像有3个通道:蓝色,绿色和红色(在OpenCV中按此顺序)。
我们可以很轻松查看单个通道:
# Example for green channel
img[:, :, 0]; img[:, :, 2]; cv2.imshow(img)
Grayscale version
灰度图像
为了避免在人脸图像分类过程中存在的干扰,通常选择黑白图像(当然也可以使用彩图!请小伙伴们自行尝试两者并比较结果)。要获得灰度图像,我们只需要在图像加载函数中通过将适当的值作为第二个参数传递来指定它:
img = cv2.imread('path/image.jpg', cv2.IMREAD_GRAYSCALE)
现在,我们的图像只有一个灰度通道了!
面部和眼睛检测
在处理人脸分类问题时,我们可能需要先对图形进行裁剪和拉直,再进行人脸检测以验证是否有人脸的存在。为此,我们将使用OpenCV中自带的的基于Haar特征的级联分类器进行对象检测。
首先,我们选择用于面部和眼睛检测的预训练分类器。以下时可用的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_eyeglasses.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)
尽管这是预期的结果,但是很多时候再提取的过程中我们会遇到一些难以解决的问题。比如我们没有正面清晰的人脸视图。
不能正确检测的案例
脸部旋转
通过计算两只眼睛之间的角度,我们就可以拉直面部图像(这很容易)。计算之后,我们仅需两个步骤即可旋转图像:
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()功能使视觉图像标准化,以修复非常暗/亮的图像(甚至可以修复低对比度)。该归一化类型是在函数参数指定:
norm_img = np.zeros((300, 300))
norm_img = cv2.normalize(img, norm_img, 0, 255, cv2.NORM_MINMAX)
例子:
当使用图像作为深度卷积神经网络的输入时,无需应用这种归一化(上面的结果对我们来说似乎不错,但是并不针对他们的眼睛)。在实践中,我们将对每个通道进行适当的归一化,例如减去均值并除以像素级别的标准差(这样我们得到均值0和偏差1)。如果我们使用转移学习,最好的方法总是使用预先训练的模型统计信息。
结论
当我们处理面部分类/识别问题时,如果输入的图像不是护照照片时,检测和分离面部是一项常见的任务。
OpenCV是一个很好的图像预处理任务库,不仅限于此。对于许多计算机视觉应用来说,它也是一个很好的工具……
https://www.youtube.com/watch?v=GebcshN4OdE
https://www.youtube.com/watch?v=z1Cvn3_4yGo
https://github.com/vjgpt/Face-and-Emotion-Recognition