選自Medium,做者:Vladimir Goncharov,機器之心編譯。php
做者 Vladimir Goncharov 日常主要關注與研究兩個主題:PHP 和 Server Administration(服務器管理)。在過去的半年中,做者利用空閒時間探索 PHP 與 OpenCV 的結合,並藉此調用與訓練優秀的機器學習模型。本文從實踐的角度介紹瞭如何使用 PHP 與 OpenCV 構建人臉檢測、人臉識別、超分辨率與目標檢測等系統,所以 PHP 的各位擁躉們,能夠盡情使用 OpenCV 探索計算機視覺了。
就像許多開發人員同樣,我也常用別人的工做成果(Medium 上的文章、GitHub 上的代碼等),所以也很樂意與社區分享個人成果。寫文章不只是對社區的一種回報,還可讓你找到志趣相投的人,在一個狹小的領域內獲得專業人員的指教,並進一步加深你對研究領域的理解。python
事實上,本篇文章正是有關這些時刻之一。在本篇文章中,除了那些我看電視節目和玩遊戲的時間,我敘述了在過去六個月的幾乎全部空閒時間裏所作的探索。git
現今,「機器學習」發展迅速,並有大量相關的文章,包括那些 Medium 上的博客,同時幾乎每位開發人員都開始在工做任務和本地項目中使用機器學習,可是從何處開始以及使用什麼方法老是使人困惑的。大多針對初學者的文章提供了一堆文獻,在閱讀中發現這些文章脫離生活,或提供一些「價」的課程等。github
一般在新發表的文章中描述瞭解決特定問題的新方法,你能夠在 GitHub 上找到文章中方法的實現。因爲更廣泛使用的編程語言是:C / C ++、Python 2/三、Lua 和 Matlab,以及框架:Caffe、TensorFlow、Torch。所以在編程語言和框架上的大量細分選擇使得找到你所須要的,並集成到項目中的過程變得更加複雜。docker
OpenCV 中添加的一個 DNN 模塊以某種方式減小了這些混亂,它使得你能夠直接使用一個在基本框架中訓練過的模型。我會向你展現如何在 PHP 中使用這個模塊。編程
DNN 模塊:github.com/opencv/open…ubuntu
Jeremy Howard(免費的實踐課程「machine learning for coders」的建立者)認爲現現在在學習機器學習和實際應用之間存在一個很大的界限。數組
Howard 認爲開始學習機器學習一年的編程經驗就足夠了。我徹底贊成他的觀點,而且我但願個人文章能夠幫助那些對機器學習不熟悉,以及還不清楚是否願意從事機器學習的 PHP 開發人員下降 OpenCV 的使用門檻,同時我會盡力闡述我花了大量時間獲得的觀點,因此大家甚至都不須要很長的時間就能夠了解它。bash
我曾考慮使用 SWIG 寫一個 php-opencv 模塊,並花費了大量時間在上面,可是並無取得任何成果。一切都由於我不懂 C / C++ 而且沒有爲 PHP 7 編寫過擴展文件而變得複雜。不幸的是,網上大多數材料都是基於 PHP 5 而寫的 PHP 擴展,所以我不得不一點點收集信息並本身解決問題。服務器
而後,我在 GitHub 上找到了 php-opencv 庫,它是一個用於調用 OpenCV 方法的 PHP 7 模塊。我花了幾個晚上來編譯、安裝和運行示例。我開始嘗試這個模塊的不一樣功能,但這個庫還缺乏一些方法,所以我就本身添加了它們並建立了一個 pull request,且該庫的做者接受了它們。以後,我添加了更多的功能。
php-opencv:github.com/hihozhou/ph…
這是圖像加載的方法:
$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);
複製代碼
完整測示例代碼:github.com/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
複製代碼
完整的示例代碼:github.com/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);
複製代碼
完整的示例代碼:github.com/php-opencv/…
結果:
從這個示例中能夠看出,殭屍妝容使得找到人臉上的特徵點變得更難。特徵點也會干擾人臉的定位。光照也有影響,在這個實例中,嘴裏的異物(草莓、香菸等)可能不會有干擾。
在我第一次拉拽請求以後,我受到了啓發同時開始瞭解 opencv 能夠作些什麼,偶然發現了一篇文章《Deep Learning,now in OpenCV》(OpenCV 中的深度學習)。我馬上決定在 php-opencv 中使用預訓練模型,這些模型在互聯網上有不少。儘管後來我花了不少時間學習如何使用多維矩陣並在不使用 OpenCV 的狀況下使用 Caffe / Torch / TensorFlow 模型,但事實證實加載 Caffe 模型並不困難。
Deep Learning,now in OpenCV:github.com/opencv/open…
使用 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%。
完整的示例代碼:github.com/php-opencv/…
結果:
從這個示例中能夠看出,神經網絡「在額頭上「使用時並不老是產生良好的結果。沒有找到第四張臉,可是若是將第四張照片單獨拿出來並導入神經網絡,人臉就會被找到。
使用神經網絡提高圖像質量
好久以前,我據說過 waifu2x 庫,它能夠消除噪聲並增長圖標/照片的大小。該庫使用 lua 編寫,在底層使用幾種 Torch 中訓練好的模型(爲了增長圖標大小,消除照片噪聲等)。該庫的做者將這些模型導出爲 Caffe 並幫助我在 OpenCV 中使用它們。所以,一個示例就是在 PHP 中編寫的用於增長圖標的分辨率。
圖像分類
在 ImageNet 上訓練的 MobileNet 神經網絡能夠分類圖像。總的來講,它能夠區分 1000 個類別,這對我來講還不夠。
示例的完整代碼:github.com/php-opencv/…
Tensorflow 目標檢測 API
在 COCO 數據集上使用 Tensorflow 訓練的 MobileNet SSD(Single Shot MultiBox Detector)網絡不只能夠對圖像進行分類,還能夠返回目標區域,儘管只能檢測 182 個類別。
示例的完整代碼:github.com/php-opencv/…
語法高亮和代碼補全
我還添加了 phpdoc.php 文件到版本庫中並做爲示例。多虧了它,Phpstorm 突出了函數的語法、類和它們的方法,而且還能夠用於代碼補全。這個文件不須要包含在你的代碼中(不然會出現錯誤),將其放到你的項目中就足夠了。就我的而言,它使得個人編程更輕鬆。這個文件描述了 OpenCV 中的大多數函數,但不是全部,所以歡迎發送拉拽請求。
phpdoc.php:github.com/php-opencv/…
安裝
「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 上傳包:github.com/php-opencv/…
所以如今在 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 上進行:github.com/php-opencv/…
我同時編譯了 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
複製代碼