06.25 世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下

選自Medium

作者:Vladimir Goncharov

機器之心編譯

參與:Huiyuan Zhuo、思源、劉曉坤

作者 Vladimir Goncharov 平常主要關注與研究兩個主題:PHP 和 Server Administration(服務器管理)。在過去的半年中,作者利用空閒時間探索 PHP 與 OpenCV 的結合,並藉此調用與訓練優秀的機器學習模型。本文從實踐的角度介紹瞭如何使用 PHP 與 OpenCV 構建人臉檢測、人臉識別、超分辨率與目標檢測等系統,因此 PHP 的各位擁躉們,可以盡情使用 OpenCV 探索計算機視覺了。本文首發於公眾號機器之心(ID:almosthuman2014),部分代碼格式有誤可參閱原文章。

世界上最好的語言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與計算機視覺已在我掌控之下

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

結果:

世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下

從這個示例中可以看出,即使在殭屍妝容的照片上也可以找到一張人臉。特徵點不會干擾人臉的定位。

人臉識別

對於人臉識別,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

數據集:

世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下

世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下

結果:

世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下

當我開始調用 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

結果:

世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下

從這個示例中可以看出,殭屍妝容使得找到人臉上的特徵點變得更難。特徵點也會干擾人臉的定位。光照也有影響,在這個實例中,嘴裡的異物(草莓、香菸等)可能不會有干擾。

在我第一次拉拽請求之後,我受到了啟發同時開始瞭解 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

結果:

世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下

從這個示例中可以看出,神經網絡「在額頭上「使用時並不總是產生良好的結果。沒有找到第四張臉,但是如果將第四張照片單獨拿出來並導入神經網絡,人臉就會被找到。

使用神經網絡提升圖像質量

很久之前,我聽說過 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

世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下

不使用神經網絡放大圖片

圖像分類

在 ImageNet 上訓練的 MobileNet 神經網絡可以分類圖像。總的來說,它可以區分 1000 個類別,這對我來說還不夠。

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

世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下

結果: 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

世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下

原圖

世界上最好的語言PHP:OpenCV與計算機視覺已在我掌控之下

結果

語法高亮和代碼補全

我還添加了 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


分享到:


相關文章: