Huiyuan Zhuo 思源 刘晓坤编译

世界上最好的语言PHP:我也可以用OpenCV搞计算机视觉

作者 Vladimir Goncharov 平常主要关注与研究两个主题:PHP 和 Server Administration(服务器管理)。在过去的半年中,作者利用空闲时间探索 PHP 与 OpenCV 的结合,并借此调用与训练优秀的机器学习模型。本文从实践的角度介绍了如何使用 PHP 与 OpenCV 构建人脸检测、人脸识别、超分辨率与目标检测等系统,因此 PHP 的各位拥趸们,可以尽情使用 OpenCV 探索计算机视觉了。


就像许多开发人员一样,我也经常使用别人的工作成果(Medium 上的文章、GitHub 上的代码等),因此也很乐意与社区分享我的成果。写文章不仅是对社区的一种回报,还可以让你找到志趣相投的人,在一个狭小的领域内得到专业人员的指教,并进一步加深你对研究领域的理解。

事实上,本篇文章正是有关这些时刻之一。在本篇文章中,除了那些我看电视节目和玩游戏的时间,我叙述了在过去六个月的几乎所有空闲时间里所做的探索。

现今,「机器学习」发展迅速,并有大量相关的文章,包括那些 Medium 上的博客,同时几乎每位开发人员都开始在工作任务和本地项目中使用机器学习,但是从何处开始以及使用什么方法总是令人困惑的。大多针对初学者的文章提供了一堆文献,在阅读中发现这些文章脱离生活,或提供一些「价」的课程等。

通常在新发表的文章中描述了解决特定问题的新方法,你可以在 GitHub 上找到文章中方法的实现。由于更普遍使用的编程语言是:C / C ++、Python 2/3、Lua 和 Matlab,以及框架:Caffe、TensorFlow、Torch。因此在编程语言和框架上的大量细分选择使得找到你所需要的,并集成到项目中的过程变得更加复杂。

OpenCV 中添加的一个 DNN 模块以某种方式减少了这些混乱,它使得你可以直接使用一个在基本框架中训练过的模型。我会向你展示如何在 PHP 中使用这个模块。

DNN 模块:https://github.com/opencv/opencv/wiki/ChangeLog#version341

Jeremy Howard(免费的实践课程「machine learning for coders」的创建者)认为现如今在学习机器学习和实际应用之间存在一个很大的界限。

Howard 认为开始学习机器学习一年的编程经验就足够了。我完全同意他的观点,并且我希望我的文章可以帮助那些对机器学习不熟悉,以及还不清楚是否愿意从事机器学习的 PHP 开发人员降低 OpenCV 的使用门槛,同时我会尽力阐述我花了大量时间得到的观点,所以你们甚至都不需要很长的时间就可以了解它。

php-opencv 项目的 logo

我曾考虑使用 SWIG 写一个 php-opencv 模块,并花费了大量时间在上面,但是并没有取得任何成果。一切都因为我不懂 C / C++ 并且没有为 PHP 7 编写过扩展文件而变得复杂。不幸的是,网上大多数材料都是基于 PHP 5 而写的 PHP 扩展,因此我不得不一点点收集信息并自己解决问题。

然后,我在 GitHub 上找到了 php-opencv 库,它是一个用于调用 OpenCV 方法的 PHP 7 模块。我花了几个晚上来编译、安装和运行示例。我开始尝试这个模块的不同功能,但这个库还缺少一些方法,因此我就自己添加了它们并创建了一个 pull request,且该库的作者接受了它们。之后,我添加了更多的功能。

php-opencv:https://github.com/hihozhou/php-opencv

这是图像加载的方法:

$image = cv\imread(“images/faces.jpg”);

相比之下,在 python 下图像加载是这样的:

image = cv2.imread(“images/faces.jpg”)

当在 PHP(以及在 C++中)中读取一张图像时,信息就存储在 Mat 对象(矩阵)中。在 PHP 中,类似的是一个多维数组,但又与多维数组有所不同,该对象可以进行多种快速操作,例如,所有元素同时除以一个数。在 Python 中,当加载图像时,会返回「NumPy」对象。

小心原有的默认操作!它会发生这样的情况,imread(在 php、c ++ 和 python 中)不是以 RGB 格式加载图像,而是 BGR 格式。因此,在 OpenCV 的示例中,你经常可以看到转换 BGR 到 RGB 的过程,反之亦然。

人脸检测

我第一次尝试的是这个功能。为此,在 OpenCV 中有一个「CascadeClassifier」类,它可以加载 xml 格式的预训练模型。在找到人脸之前,该类建议将图像转换为黑白格式。

$src = imread(“images/faces.jpg”); 
$gray = cvtColor($src, COLOR_BGR2GRAY); 
$faceClassifier = new CascadeClassifier(); 
$faceClassifier->load(‘models/lbpcascades/lbpcascade_frontalface.xml’); 
$faceClassifier->detectMultiScale($gray, $faces);

完整测示例代码:https://github.com/php-opencv/php-opencv-examples/blob/master/detect_face_by_cascade_classifier.php

结果:

从这个示例中可以看出,即使在僵尸妆容的照片上也可以找到一张人脸。特征点不会干扰人脸的定位。

人脸识别

对于人脸识别OpenCV 拥有「LBPHFaceRecognizer」类和「train / predict」方法。

如果我们想要知道照片中是谁,首先我们需要使用 train 方法训练模型,它需要两个参数:对于这些图像的一个人脸图像的数组和一个数值标签的数组。然后你可以在测试图像(人脸)上调用 predict 方法并获得相匹配的数值标签。

$faceRecognizer = LBPHFaceRecognizer :: create ();
$faceRecognizer-> train ($myFaces, $myLabels = [1,1,1,1]); // 4 my faces
$faceRecognizer-> update ($angelinaFaces, $angelinaLabels = [2,2,2,2]); // 4 faces of Angelina
$label = $faceRecognizer-> predict ($faceImage, $confidence);
// get label (1 or 2) and confidence

完整的示例代码:https://github.com/php-opencv/php-opencv-examples/blob/master/recognize_face_by_lbph.php

数据集:

结果:

当我开始调用 LBPHFaceRecognizer 类时,它无法保存/加载/更新训练好的模型。事实上,我的第一个 pull request 添加了这些方法:写入/读取/更新。

人脸标记/特征点

当我开始熟悉 OpenCV 时,我经常看到一些人的照片,这些照片上的点标记着眼睛、鼻子、嘴唇等。我想自己重复这个实验,但在 OpenCV 的 Python 版本中并没有实现。我花了一个晚上为 PHP 添加了 FacematkLBF 支持并返回一个对象。一切都是简单易行的,我们加载预训练的模型,输入关于人脸的一个数组,然后得到关于每个人的特征点的一个数组。

$facemark = FacemarkLBF::create(); 
$facemark->loadModel(‘models/opencv-facemark-lbf/lbfmodel.yaml’); 
$facemark->fit($image, $faces, $landmarks);

完整的示例代码:https://github.com/php-opencv/php-opencv-examples/blob/master/detect_facemarks_by_lbf.php

结果:

从这个示例中可以看出,僵尸妆容使得找到人脸上的特征点变得更难。特征点也会干扰人脸的定位。光照也有影响,在这个实例中,嘴里的异物(草莓、香烟等)可能不会有干扰。

在我第一次拉拽请求之后,我受到了启发同时开始了解 opencv 可以做些什么,偶然发现了一篇文章《Deep Learning,now in OpenCV》(OpenCV 中的深度学习)。我立刻决定在 php-opencv 中使用预训练模型,这些模型在互联网上有很多。尽管后来我花了很多时间学习如何使用多维矩阵并在不使用 OpenCV 的情况下使用 Caffe / Torch / TensorFlow 模型,但事实证明加载 Caffe 模型并不困难。

Deep Learning,now in OpenCV:https://github.com/opencv/opencv/wiki/ChangeLog#version341

使用 DNN 模型进行人脸检测

因此,OpenCV 允许你使用 readNetFromCaffe 函数在 Caffe 中加载预训练模型。它需要两个参数:指向 .prototxt 和 .caffemodel 文件的路径。prototxt 文件中有模型的描述,而在 caffemodel 中有模型训练期间计算的权重

以下是一个 prototxt 文件开头的示例:

input:“data”
input_shape { 
 dim: 1 
 dim: 3 
 dim: 300 
 dim: 300 
}

这段文件描述了输入一个 1x3x300x300 的 4 维矩阵。在对模型的描述中,通常会说明以这种格式输入的意义是什么,但在大多数情况下,这意味着将输入尺寸为 300x300 的 RGB 图像(3 通道)。

通过使用 imread 函数加载一张 300x300 的 RGB 图像,我们得到一个 300x300x3 的矩阵。

OpenCV 中有一个 blobFromImage 函数能将 300x300x3 的矩阵转换为 1x3x300x300 的格式。

之后,我们可以仅通过使用 setInput 方法将 blob 应用于网络输入并调用 forward 方法,其可以返回最终的结果给我们。

$src = imread(“images/faces.jpg”); 
$net = \CV\DNN\readNetFromCaffe(‘models/ssd/res10_300x300_ssd_deploy.prototxt’, ‘models/ssd/res10_300x300_ssd_iter_140000.caffemodel’); 
$blob = \CV\DNN\blobFromImage($src, $scalefactor = 1.0, $size = new Size(300, 300), $mean = new Scalar(104, 177, 123), $swapRB = true, $crop = false); 
$net->setInput($blob,“”); 
$result = $net->forward();

在这个实例中,结果是一个 1×1×200×7 的矩阵,即每张图像有 7 个元素的 200 个数组。在一张有 4 张脸的照片中,网络寻找到 200 个候选对象。其中每一个对象的形式为 [,, $confidence, $startX, $startY, $endX, $endY]。元素 $confidence 代表「置信度」,即预测概率有多好,比如 0.75 是好的。之后的元素代表人脸矩形框的坐标。在这个示例中,只有 3 张人脸以超过 50% 的置信度被找到,而剩下的 197 个候选对象的置信度小于 15%。

完整的示例代码:https://github.com/php-opencv/php-opencv-examples/blob/master/detect_face_by_dnn_ssd.php

结果:

从这个示例中可以看出,神经网络「在额头上「使用时并不总是产生良好的结果。没有找到第四张脸,但是如果将第四张照片单独拿出来并导入神经网络,人脸就会被找到。

使用神经网络提升图像质量

很久之前,我听说过 waifu2x 库,它可以消除噪声并增加图标/照片的大小。该库使用 lua 编写,在底层使用几种 Torch 中训练好的模型(为了增加图标大小,消除照片噪声等)。该库的作者将这些模型导出为 Caffe 并帮助我在 OpenCV 中使用它们。因此,一个示例就是在 PHP 中编写的用于增加图标的分辨率。

  • waifu2x 库:https://github.com/nagadomi/waifu2x

  • 示例的完整代码:https://github.com/php-opencv/php-opencv-examples/blob/master/upscale_image_x2_by_dnn_waifu2x.php

不使用神经网络放大图片

图像分类

在 ImageNet 上训练的 MobileNet 神经网络可以分类图像。总的来说,它可以区分 1000 个类别,这对我来说还不够。

示例的完整代码:https://github.com/php-opencv/php-opencv-examples/blob/master/classify_image_by_dnn_mobilenet.php

结果: 87% - 埃及猫,4% - 斑猫,2% - 虎猫

Tensorflow 目标检测 API

在 COCO 数据集上使用 Tensorflow 训练的 MobileNet SSD(Single Shot MultiBox Detector)网络不仅可以对图像进行分类,还可以返回目标区域,尽管只能检测 182 个类别。

示例的完整代码:https://github.com/php-opencv/php-opencv-examples/blob/master/detect_objects_by_dnn_mobilenet.php

原图

结果

语法高亮和代码补全

我还添加了 phpdoc.php 文件到版本库中并作为示例。多亏了它,Phpstorm 突出了函数的语法、类和它们的方法,并且还可以用于代码补全。这个文件不需要包含在你的代码中(否则会出现错误),将其放到你的项目中就足够了。就个人而言,它使得我的编程更轻松。这个文件描述了 OpenCV 中的大多数函数,但不是所有,因此欢迎发送拉拽请求。

phpdoc.php:https://github.com/php-opencv/php-opencv-examples/blob/master/phpdoc.php

安装

「dnn」模块仅在 OpenCV 3.4 中出现(对于之前的版本它是在 contrib 中)。

Ubuntu 18.04 最新的 OpenCV 版本是 3.2。从源码搭建 OpenCV 大约需要 半个小时,所以我在 Ubuntu 18.04 下编译了这个包(也适用于 17.10 版本,大小 25 MB),同时为 PHP 7.2(Ubuntu 18.04)和 PHP 7.1(Ubuntu 17.10)(大小 100 KB)编译了 php-opencv 包。注册 ppa:php-opencv,但还没上传完,同时没有发现比在 GitHub 上传包更好的。我还创建了一个在 pecl 中申请一个账户的请求,但几个月都没得到回复。

在 GitHub 上传包:https://github.com/php-opencv/php-opencv-packages

因此现在在 Ubuntu 18.04 下的安装看起来是这样的:

apt update && apt install -y wget && \
wget https://raw.githubusercontent.com/php-opencv/php-opencv-packages/master/opencv_3.4_amd64.deb && dpkg -i opencv_3.4_amd64.deb && rm opencv_3.4_amd64.deb && \
wget https://raw.githubusercontent.com/php-opencv/php-opencv-packages/master/php-opencv_7.2-3.4_amd64.deb && dpkg -i php-opencv_7.2–3.4_amd64.deb && rm php-opencv_7.2–3.4_amd64.deb && \
echo“extension=opencv.so”> /etc/php/7.2/cli/conf.d/opencv.ini

安装这个选项大约需要 1 分钟,所有安装选项在 Ubuntu 上进行:https://github.com/php-opencv/php-opencv-examples/wiki/Installation-on-ubuntu

我同时编译了 168 MB 的 docker 映像。

使用示例

下载:

git clone https://github.com/php-opencv/php-opencv-examples.git && cd php-opencv-examples

运行:

php detect_face_by_dnn_ssd.php

原文链接:https://medium.com/@morozovsk/computer-vision-and-machine-learning-in-php-using-the-opencv-library-3131fe9df94b

工程PHPOpenCV计算机视觉
1
相关数据
神经网络技术
Neural Network

(人工)神经网络是一种起源于 20 世纪 50 年代的监督式机器学习模型,那时候研究者构想了「感知器(perceptron)」的想法。这一领域的研究者通常被称为「联结主义者(Connectionist)」,因为这种模型模拟了人脑的功能。神经网络模型通常是通过反向传播算法应用梯度下降训练的。目前神经网络有两大主要类型,它们都是前馈神经网络:卷积神经网络(CNN)和循环神经网络(RNN),其中 RNN 又包含长短期记忆(LSTM)、门控循环单元(GRU)等等。深度学习是一种主要应用于神经网络帮助其取得更好结果的技术。尽管神经网络主要用于监督学习,但也有一些为无监督学习设计的变体,比如自动编码器和生成对抗网络(GAN)。

人脸识别技术
Facial recognition

广义的人脸识别实际包括构建人脸识别系统的一系列相关技术,包括人脸图像采集、人脸定位、人脸识别预处理、身份确认以及身份查找等;而狭义的人脸识别特指通过人脸进行身份确认或者身份查找的技术或系统。 人脸识别是一项热门的计算机技术研究领域,它属于生物特征识别技术,是对生物体(一般特指人)本身的生物特征来区分生物体个体。

机器学习技术
Machine Learning

机器学习是人工智能的一个分支,是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、计算复杂性理论等多门学科。机器学习理论主要是设计和分析一些让计算机可以自动“学习”的算法。因为学习算法中涉及了大量的统计学理论,机器学习与推断统计学联系尤为密切,也被称为统计学习理论。算法设计方面,机器学习理论关注可以实现的,行之有效的学习算法。

OpenCV技术
OpenCV

OpenCV的全称是Open Source Computer Vision Library,是一个跨平台的计算机视觉库。OpenCV是由英特尔公司发起并参与开发,以BSD许可证授权发行,可以在商业和研究领域中免费使用。OpenCV可用于开发实时的图像处理、计算机视觉以及模式识别程序。

参数技术
parameter

在数学和统计学裡,参数(英语:parameter)是使用通用变量来建立函数和变量之间关系(当这种关系很难用方程来阐述时)的一个数量。

权重技术
Weight

线性模型中特征的系数,或深度网络中的边。训练线性模型的目标是确定每个特征的理想权重。如果权重为 0,则相应的特征对模型来说没有任何贡献。

深度学习技术
Deep learning

深度学习(deep learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。 深度学习是机器学习中一种基于对数据进行表征学习的算法,至今已有数种深度学习框架,如卷积神经网络和深度置信网络和递归神经网络等已被应用在计算机视觉、语音识别、自然语言处理、音频识别与生物信息学等领域并获取了极好的效果。

张量技术
Tensor

张量是一个可用来表示在一些矢量、标量和其他张量之间的线性关系的多线性函数,这些线性关系的基本例子有内积、外积、线性映射以及笛卡儿积。其坐标在 维空间内,有 个分量的一种量,其中每个分量都是坐标的函数,而在坐标变换时,这些分量也依照某些规则作线性变换。称为该张量的秩或阶(与矩阵的秩和阶均无关系)。 在数学里,张量是一种几何实体,或者说广义上的“数量”。张量概念包括标量、矢量和线性算子。张量可以用坐标系统来表达,记作标量的数组,但它是定义为“不依赖于参照系的选择的”。张量在物理和工程学中很重要。例如在扩散张量成像中,表达器官对于水的在各个方向的微分透性的张量可以用来产生大脑的扫描图。工程上最重要的例子可能就是应力张量和应变张量了,它们都是二阶张量,对于一般线性材料他们之间的关系由一个四阶弹性张量来决定。

TensorFlow技术
TensorFlow

TensorFlow是一个开源软件库,用于各种感知和语言理解任务的机器学习。目前被50个团队用于研究和生产许多Google商业产品,如语音识别、Gmail、Google 相册和搜索,其中许多产品曾使用过其前任软件DistBelief。

推荐文章