編者按:opencv4nodejs及face-recognition.js維護者Vincent Mühler介紹了如何在Node.js環(huán)境下使用訓(xùn)練好的神經(jīng)網(wǎng)絡(luò)模型識別圖像中的物體。
今天我們將看看Node.js的OpenCV深度神經(jīng)網(wǎng)絡(luò)模塊。
如果你希望釋放神經(jīng)網(wǎng)絡(luò)的魔力,來辨識和分類圖像中的物體,卻對深度學(xué)習(xí)是如何工作的毫無頭緒(像我一樣),更不知道如何創(chuàng)建和訓(xùn)練神經(jīng)網(wǎng)絡(luò),那么本文正適合你!
所以我們今天將創(chuàng)建什么?
在這一篇教程中,我們將了解如何通過OpenCV的DNN模塊,從Tensorflow和Caffe加載預(yù)訓(xùn)練的模型,然后我們將深入兩個基于Node.js和OpenCV進(jìn)行物體識別的例子。
首先我們將使用Tensorflow的Inception模型來辨識圖像中的物體,之后我們將使用COCO SSD模型檢測和辨識同一圖像中的多個不同物體。
你可以在我的github倉庫上找到樣例代碼:justadudewhohacks/opencv4nodejs
Tensorflow Inception
訓(xùn)練過的Tensorflow Inception模型可以辨別約1000個分類的物體。如果你將圖像傳入網(wǎng)絡(luò),它將給出圖像中的物體的每個分類的似然。
要在OpenCV下使用Inception模型,我們需要加載二進(jìn)制文件tensorflow_inception_graph.pb以及分類名稱列表imagenet_comp_graph_label_strings.txt。你可以下載inception5h.zip并解壓以獲得這些文件(下面的代碼內(nèi)有下載鏈接):
// 替換路徑為你解壓縮inception模型的路徑
const inceptionModelPath = '../data/dnn/tf-inception'
const modelFile = path.resolve(inceptionModelPath, 'tensorflow_inception_graph.pb');
const classNamesFile = path.resolve(inceptionModelPath, 'imagenet_comp_graph_label_strings.txt');
if (!fs.existsSync(modelFile) || !fs.existsSync(classNamesFile)) {
console.log('退出: 找不到inception模型');
console.log('從以下網(wǎng)址下載模型: https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip');
return;
}
// 讀取classNames(分類名稱),然后在數(shù)組中儲存它們
const classNames = fs.readFileSync(classNamesFile).toString().split(" ");
// 從modelFile初始化tensorflow inception模塊
const net = cv.readNetFromTensorflow(modelFile);
分類圖像中的物品
為了分類圖像中的物品,我們將編寫以下幫助函數(shù):
const classifyImg = (img) => {
// inception模型使用224 x 224 圖像,
// 因此我們調(diào)整輸入圖像的大小,
// 并使用白像素補齊圖像
const maxImgDim = 224;
const white = new cv.Vec(255, 255, 255);
const imgResized = img.resizeToMax(maxImgDim).padToSquare(white);
// 網(wǎng)絡(luò)接受blob作為輸入
const inputBlob = cv.blobFromImage(imgResized);
net.setInput(inputBlob);
// 前向傳播輸入至整個網(wǎng)絡(luò),
// 將返回包含每個分類的置信度的1xN矩陣分類結(jié)果
const outputBlob = net.forward();
// 找到大于最小置信度的所有標(biāo)簽
const minConfidence = 0.05;
const locations =
outputBlob
.threshold(minConfidence, 1, cv.THRESH_BINARY)
.convertTo(cv.CV_8U)
.findNonZero();
const result =
locations.map(pt => ({
confidence: parseInt(outputBlob.at(0, pt.x) * 100) / 100,
className: classNames[pt.x]
}))
// 根據(jù)置信度排序
.sort((r0, r1) => r1.confidence - r0.confidence)
.map(res => `${res.className} (${res.confidence})`);
return result;
}
這一函數(shù)做了這些事:
準(zhǔn)備輸入圖像
Tensorflow Inception網(wǎng)絡(luò)接受224x224大小的輸入圖像。因此我們調(diào)整圖像大小,使其最大邊的大小為224,然后用白像素補齊。
讓圖像穿過網(wǎng)絡(luò)
我們可以直接從圖像創(chuàng)建blob,然后調(diào)用net.forward()前向傳播輸入,然后獲取輸出blob.
從輸出blob提取結(jié)果
為了通用性,輸出blob的表達(dá)形式直接是矩陣(cv.Mat),而它的維度取決于模型。在Inception下這很簡單。blob不過是一個1xN矩陣(其中N等于分類數(shù)),描述了所有分類的概率分布。每個條目為一個浮點數(shù),表示相應(yīng)分類的置信度。所有條目相加,總和為1.0(100%)。
我們想仔細(xì)看看圖像可能性最大的分類,因此我們查看所有置信度大于minConfidence(這個例子中是5%)。最后,我們根據(jù)置信度排序結(jié)果,并返回className、confidence對。
測試
現(xiàn)在我們將讀取一些我們希望網(wǎng)絡(luò)辨識的樣本數(shù)據(jù):
const testData = [
{
image: '../data/banana.jpg',
label: 'banana'
},
{
image: '../data/husky.jpg',
label: 'husky'
},
{
image: '../data/car.jpeg',
label: 'car'
},
{
image: '../data/lenna.png',
label: 'lenna'
}
];
testData.forEach((data) => {
const img = cv.imread(data.image);
console.log('%s: ', data.label);
const predictions = classifyImg(img);
predictions.forEach(p => console.log(p));
console.log();
cv.imshowWait('img', img);
});
輸出為:(你可以參考本文開頭的圖片)
banana:
banana (0.95)
husky:
Siberian husky (0.78)
Eskimo dog (0.21)
car:
sports car (0.57)
racer (0.12)
lenna:
sombrero (0.34)
cowboy hat (0.3)
很有趣。我們得到了愛基斯摩犬和香蕉圖像非常準(zhǔn)確的描述。對于汽車圖像而言,汽車的具體類別不太準(zhǔn),但模型確實辨識出了圖像中的汽車。當(dāng)然,網(wǎng)絡(luò)不可能在無限的分類上進(jìn)行訓(xùn)練,因此它沒有為最后一張圖像返回“婦女”描述。然而,它確實辨識出了帽子。
COCO SSD
好,模型表現(xiàn)不錯。但是我們?nèi)绾翁幚戆鄠€物體的圖像呢?為了辨識單一圖像中的多個物體,我們將利用單圖多盒檢測器(Single Shot Multibox Detector, SSD)。在我們的第二個例子中,我們將查看一個在COCO(Common Object in Context)數(shù)據(jù)集上訓(xùn)練的SSD模型。我們使用的這一模型在84個不同分類上訓(xùn)練過。
這一模型來自Caffe,因此我們將加載二進(jìn)制文件VGG_coco_SSD_300x300_iter_400000.caffemodel,以及protoxt文件deploy.prototxt:
// 替換路徑為你解壓縮coco-SSD模型的路徑
const ssdcocoModelPath = '../data/dnn/coco-SSD_300x300'
const prototxt = path.resolve(ssdcocoModelPath, 'deploy.prototxt');
const modelFile = path.resolve(ssdcocoModelPath, 'VGG_coco_SSD_300x300_iter_400000.caffemodel');
if (!fs.existsSync(prototxt) || !fs.existsSync(modelFile)) {
console.log('退出: 找不到ssdcoco模型');
console.log('從以下網(wǎng)址下載模型 https://drive.google.com/file/d/0BzKzrI_SkD1_dUY1Ml9GRTFpUWc/view');
return;
}
// 從prototxt和modelFile初始化ssdcoco模型
const net = cv.readNetFromCaffe(prototxt, modelFile);
基于COCO分類
我們的分類函數(shù)和基于Inception的分類函數(shù)幾乎一樣,不過這次輸入將是300x300的圖像,而輸出將是1x1xNx7矩陣。
const classifyImg = (img) => {
const white = new cv.Vec(255, 255, 255);
// ssdcoco模型接受300 x 300圖像
const imgResized = img.resize(300, 300);
// 網(wǎng)絡(luò)接受blob作為輸入
const inputBlob = cv.blobFromImage(imgResized);
net.setInput(inputBlob);
// 前向傳播輸入至整個網(wǎng)絡(luò),
// 將返回1x1xNxM矩陣作為分類結(jié)果
let outputBlob = net.forward();
// 提取NxM矩陣
outputBlob = outputBlob.flattenFloat(outputBlob.sizes[2], outputBlob.sizes[3]);
const results = Array(outputBlob.rows).fill(0)
.map((res, i) => {
const className = classNames[outputBlob.at(i, 1)];
const confidence = outputBlob.at(i, 2);
const topLeft = new cv.Point(
outputBlob.at(i, 3) * img.cols,
outputBlob.at(i, 6) * img.rows
);
const bottomRight = new cv.Point(
outputBlob.at(i, 5) * img.cols,
outputBlob.at(i, 4) * img.rows
);
return ({
className,
confidence,
topLeft,
bottomRight
})
});
return results;
};
我不是很清楚為何輸出是1x1xNx7矩陣,不過我們實際上只關(guān)心Nx7部分。我們可以使用flattenFloat工具函數(shù)映射第三、第四維至2D矩陣。與Inception輸出矩陣相比,這次N不對應(yīng)每個分類,而是檢測到的每個物體。另外,每個物體對應(yīng)7個條目。
為什么是7個條目?
記住,這里我們遇到的問題和之前有點不一樣。我們想要檢測單張圖像中的多個物體,因此我們不可能僅僅給出每個分類的置信度。我們實際上想要得到的是一個指示每個物體在圖中的位置的矩形。7個條目分別為:
我其實毫無頭緒
物體的分類標(biāo)簽
分類的置信度
矩形左端的x
矩形底部的y
矩形右端的x
矩形頂部的y
輸出矩陣給了我們不少關(guān)于結(jié)果的信息,這看起來相當(dāng)整潔。我們同樣可以根據(jù)置信度再次過濾結(jié)果,并為每個辨識出的物體在圖像中繪制邊框。
看看它的效果!
出于行文的簡潔,我將跳過繪制矩形的代碼,以及其他可視化的代碼。如果你想知道具體是怎么做的,可以訪問前面提到的github倉庫。
讓我們傳入一張汽車圖像到網(wǎng)絡(luò),然后過濾結(jié)果,看看是否檢測到了car分類:
很棒!下面提高一下難度。讓我們試下……一張早餐桌?
很不錯!
-
神經(jīng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
42文章
4771瀏覽量
100712
原文標(biāo)題:當(dāng)Node.js遇上OpenCV深度神經(jīng)網(wǎng)絡(luò)
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論