在OpenCV3.3版本發布中把DNN模塊從擴展模塊移到了OpenCV正式發布模塊中,當前DNN模塊最早來自Tiny-dnn,可以加載預先訓練好的Caffe模型數據,OpenCV做了近一步擴展支持所有主流的深度學習框架訓練生成與導出模型數據加載,常見的有如下:
Caffe
TensorFlow
Torch/PyTorch
OpenCV中DNN模塊已經支持與測試過這些常見的網絡模塊
AlexNet
GoogLeNet v1 (also referred to as Inception-5h)
ResNet-34/50/...
SqueezeNet v1.1
VGG-based FCN (semantical segmentation network)
ENet (lightweight semantical segmentation network)
VGG-based SSD (object detection network)
MobileNet-based SSD (light-weight object detection network)
一:GoogleNet Caffe模型數據說明OpenCV通過支持加載這些預先訓練好的模型,實現圖像分類、對象檢測、語義分割、風格遷移等功能。支持Android/iOS等移動端平臺開發。下面我們就以OpenCV3.3 使用Caffe的GoogleNet數據模型為例,實現對圖像常見分類,OpenCV3.3的DNN模塊使用的模型支持1000種常見圖像分類、googlenet深度學習網絡模型是2014圖像分類比賽的冠軍、首先是下載相關的數據模型文件
其中prototxt是一個文本的JSON文件、一看就明白啦,另外一個文件二進位文件。文本文件只有你下載了OpenCV3.3解壓縮之後就會在對應的目錄發現。模型文件需要從以下地址下載即可: http://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodel
二:編程實現首先我們需要加載它官方指定的一張測試圖像space_shuttle.jpg 是一張太空梭的圖片、OpenCV中加載圖像的代碼如下:
Mat testImage = imread("D:/vcprojects/images/dnn/football.jpg");
if (testImage.empty()) {
printf("could not load image...\n");
return -1;
}
然後我們需要聲明模型數據的路徑與標記數據路徑,加載創建網絡模型,代碼實現如下:
// create googlenet with caffemodel text and bin
Net net = dnn::readNetFromCaffe(modelTxt, modelBin);
if (net.empty())
{
std::cerr << "Can't load network by using the following files: " << std::endl;
std::cerr << "prototxt: " << modelTxt << std::endl;
std::cerr << "caffemodel: " << modelBin << std::endl;
return -1;
}
// 讀取分類數據
vector<String> labels = readClasslabels();
//GoogLeNet accepts only 224x224 RGB-images
Mat inputBlob = blobFromImage(testImage, 1, Size(224, 224), Scalar(104, 117, 123));
然後開始分類預測,根據prototxt中的開始的要求,我們需要輸入迭代10次,輸出預測分類的結果,代碼實現如下:
// 支持1000個圖像分類檢測
Mat prob;
// 循環10+
for (int i = 0; i < 10; i++)
{
// 輸入
net.setInput(inputBlob, "data");
// 分類預測
prob = net.forward("prob");
}
// 讀取分類索引,最大與最小值
Mat probMat = prob.reshape(1, 1); //reshape the blob to 1x1000 matrix // 1000個分類
Point classNumber;
double classProb;
minMaxLoc(probMat, NULL, &classProb, NULL, &classNumber); // 可能性最大的一個
int classIdx = classNumber.x; // 分類索引號
printf("\n current image classification : %s, possible : %.2f \n", labels.at(classIdx).c_str(), classProb);
putText(testImage, labels.at(classIdx), Point(20, 20), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 0, 255), 2, 8);
imshow("Image Category", testImage);
其中讀取圖像分類索引與文本描述的方法代碼如下:
vector<String> readClasslabels() {
std::vector<String> classNames;
std::ifstream fp(labelFile);
if (!fp.is_open())
{
std::cerr << "File with classes labels not found: " << labelFile << std::endl;
exit(-1);
}
std::string name;
while (!fp.eof())
{
std::getline(fp, name);
if (name.length())
classNames.push_back(name.substr(name.find(' ') + 1));
}
fp.close();
return classNames;
}
三:效果顯示太空梭測試圖像
霸氣威武的J10戰鬥機
玩具店
足球場上
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <iostream>
using namespace cv;
using namespace cv::dnn;
using namespace std;
String modelTxt = "D:/vcprojects/images/dnn/bvlc_googlenet.prototxt";
String modelBin = "D:/vcprojects/images/dnn/bvlc_googlenet.caffemodel";
String labelFile = "D:/vcprojects/images/dnn/synset_words.txt";
vector<String> readClasslabels();
int main(int argc, char** argv) {
Mat testImage = imread("D:/vcprojects/images/dnn/football.jpg");
if (testImage.empty()) {
printf("could not load image...\n");
return -1;
}
// create googlenet with caffemodel text and bin
Net net = dnn::readNetFromCaffe(modelTxt, modelBin);
if (net.empty())
{
std::cerr << "Can't load network by using the following files: " << std::endl;
std::cerr << "prototxt: " << modelTxt << std::endl;
std::cerr << "caffemodel: " << modelBin << std::endl;
return -1;
}
// 讀取分類數據
vector<String> labels = readClasslabels();
//GoogLeNet accepts only 224x224 RGB-images
Mat inputBlob = blobFromImage(testImage, 1, Size(224, 224), Scalar(104, 117, 123));
// 支持1000個圖像分類檢測
Mat prob;
// 循環10+
for (int i = 0; i < 10; i++)
{
// 輸入
net.setInput(inputBlob, "data");
// 分類預測
prob = net.forward("prob");
}
// 讀取分類索引,最大與最小值
Mat probMat = prob.reshape(1, 1); //reshape the blob to 1x1000 matrix // 1000個分類
Point classNumber;
double classProb;
minMaxLoc(probMat, NULL, &classProb, NULL, &classNumber); // 可能性最大的一個
int classIdx = classNumber.x; // 分類索引號
printf("\n current image classification : %s, possible : %.2f \n", labels.at(classIdx).c_str(), classProb);
putText(testImage, labels.at(classIdx), Point(20, 20), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 0, 255), 2, 8);
imshow("Image Category", testImage);
waitKey(0);
return 0;
}
/* 讀取圖像的1000個分類標記文本數據 */
vector<String> readClasslabels() {
std::vector<String> classNames;
std::ifstream fp(labelFile);
if (!fp.is_open())
{
std::cerr << "File with classes labels not found: " << labelFile << std::endl;
exit(-1);
}
std::string name;
while (!fp.eof())
{
std::getline(fp, name);
if (name.length())
classNames.push_back(name.substr(name.find(' ') + 1));
}
fp.close();
return classNames;
}
居不隱者,思不遠也;
身不危者,志不廣也!
關注【OpenCV學堂】
長按或者掃碼下面二維碼即可關注
+OpenCV學習群 376281510
進群暗號:OpenCV