級聯分類器的效果並不是很好,準確度相對深度學習較低,本章使用opencv通過tensorflow深度學習,檢測已有模型的分類。
可以猜測,1其實是人,18序號類是狗,因為筆者未找到對應的分類具體資訊。
https://github.com/opencv/opencv_extra
(注意:未找到對應的分類具體資訊。)
opencv3.4.x支援了各種模型。
opencv3.4.x支援一下深度學習的模型:
- caffe:.caffemodel
官網:http://caffe.berkeleyvision.org
- tensorflow:.pb
官網:https://www.tensorflow.org
- torch:.t7 | .net
官網:http://torch.ch
- darknet:.weights
官網:https://pjreddie.com/darknet
- DLDT:.bin
官網:https://software.intel.com/openvino-toolkit
- 步驟一:載入模型和配置檔案,建立神經網路。
根據不同的模型,使用cv::dnn::readNetFromXXX系列函式進行讀取,opencv3.4.x系列支援的dnn模型(支援模型往上看)。
舉例tensorflow模型如下:
std::string weights = "E:/qtProject/openCVDemo/dnnData/" \
"ssd_mobilenet_v1_coco_2017_11_17/frozen_inference_graph.pb";
std::string prototxt = "E:/qtProject/openCVDemo/dnnData/" \
"ssd_mobilenet_v1_coco_2017_11_17.pbtxt";
cv::dnn::Net net = cv::dnn::readNetFromTensorflow(weights, prototxt);
- 步驟二:將要預測的圖片加入到神經網路中
加入之後,需要識別圖片,那麼需要把圖片輸入到神經網路當中去,如下:
cv::Mat mat;
cv::Mat blob;
mat = cv::imread("E:/testFile/14.jpg");
cv::dnn::blobFromImage(mat, blob);
- 步驟三:分類預測,獲取識別的結果
輸入之後,就進行識別,識別是向前預測(分類預測),並且拿到結果。
cv::Mat prob = net.forward();
對於預測的結果,存於cv::Mat型別的prob,然後需要統一對prob進行處理,使其成為我們可以使用的資料,程式碼如下:
cv::Mat detectionMat(prob.size[2], prob.size[3], CV_32F, prob.ptr<float>());
對於從結果prob轉換為detectionMat後,其結構如下:
cv::Mat為多行七列,每一行代表一個檢測到的分類,具體列資訊如下表:
(注意:具體的使用,請參照“步驟四”)
- 步驟四:對達到置信度的可以通過輸出的mat進行分類和框選
cv::Mat detectionMat(prob.size[2], prob.size[3], CV_32F, prob.ptr<float>());
// 置信度預製,大於執行度的將其使用rect框出來
float confidenceThreshold = 0.75;
for(int i = 0; i < detectionMat.rows; i++)
{
float confidence = detectionMat.at<float>(i, 2);
if (confidence > confidenceThreshold)
{
// 高於置信度的,獲取其x、y、以及對應的寬度高度,進行框選
int classId = (detectionMat.at<float>(i, 1));
int xLeftBottom = static_cast<int>(detectionMat.at<float>(i, 3) * mat.cols);
int yLeftBottom = static_cast<int>(detectionMat.at<float>(i, 4) * mat.rows);
int xRightTop = static_cast<int>(detectionMat.at<float>(i, 5) * mat.cols);
int yRightTop = static_cast<int>(detectionMat.at<float>(i, 6) * mat.rows);
cv::Rect object((int)xLeftBottom,
(int)yLeftBottom,
(int)(xRightTop - xLeftBottom),
(int)(yRightTop - yLeftBottom));
cv::rectangle(mat, object, cv::Scalar(0, 255, 0), 2);
qDebug() << __FILE__ << __LINE__
<< classId
<< confidence << confidenceThreshold
<< object.x << object.y << object.width << object.height;
}
}
讀取tensorflow模型與配置檔案函式原型
Net readNetFromTensorflow(const String &model,
const String &config = String());
從檔案中讀取。
- 引數一:用二進位制協議描述網路體系結構的.pb檔案的路徑;
- 引數二:包含protobuf格式的文字圖形定義的.pbtxt檔案的路徑。生成的網路物件由文字圖構建,使用來自二進位制的權重讓我們更靈活些;
Net readNetFromTensorflow(const std::vector<uchar>& bufferModel,
const std::vector<uchar>& bufferConfig = std::vector<uchar>());
從快取中讀取。
- 引數一:包含pb檔案內容的bufferModel緩衝區;
- 引數二:包含pbtxt檔案內容的bufferConfig緩衝區;
Net readNetFromTensorflow(const char *bufferModel,
size_t lenModel,
const char *bufferConfig = NULL,
size_t lenConfig = 0);
- 引數一:包含pb檔案內容的bufferModel緩衝區;
- 引數二:bufferModel緩衝長度;
- 引數三:包含pbtxt檔案內容的bufferConfig緩衝區;
- 引數四:bufferConfig緩衝長度;
讀取圖片(需要識別的)函式原型
Mat blobFromImage(InputArray image,
double scalefactor=1.0,
const Size& size = Size(),
const Scalar& mean = Scalar(),
bool swapRB=false,
bool crop=false,
int ddepth=CV_32F);
void blobFromImage(InputArray image,
OutputArray blob,
double scalefactor=1.0,
const Size& size = Size(),
const Scalar& mean = Scalar(),
bool swapRB=false,
bool crop=false,
int ddepth=CV_32F);.
Mat blobFromImages(InputArrayOfArrays images,
double scalefactor=1.0,
Size size = Size(),
const Scalar& mean = Scalar(),
bool swapRB=false,
bool crop=false,
int ddepth=CV_32F);
void blobFromImages(InputArrayOfArrays images,
OutputArray blob,
double scalefactor=1.0,
Size size = Size(),
const Scalar& mean = Scalar(),
bool swapRB=false,
bool crop=false,
int ddepth=CV_32F);
從影像建立區域。可選擇從中心調整和裁剪影像。
- 引數一:影像輸入影像(1、3或4通道);
- 引數二:大小輸出影像的空間大小;
- 引數三:從通道中減去平均值的平均標量。價值是有意的,如果image有BGR順序,swapRB為真,則按(mean-R,mean-G,mean-B)順序排列;
- 引數四:影像值的縮放因子乘數;
- 引數五:swapRB標誌,指示交換第一個和最後一個通道,在三通道影像是必要的;
- 引數六:裁剪標誌,指示調整大小後是否裁剪影像;
- 引數七:輸出blob的深度,選擇CV_32F或CV_8U;
void cv::dnn::Net::setInput(InputArray blob,
const String& name = "",
double scalefactor = 1.0,
const Scalar& mean = Scalar());
設定網路的新輸入值。
- 引數一:一個新的blob。應具有CV_32F或CV_8U深度。
- 引數二:輸入層的名稱。
- 引數三:可選的標準化刻度。
- 引數四:可選的平均減去值。
void cv::dnn::Net::Mat forward(const String& outputName = String());
向前預測,返回指定層的第一個輸出的blob,一般是返回最後一層,可使用cv::Net::getLayarNames()獲取所有的層名稱。
- 引數一:outputName需要獲取輸出的層的名稱
void OpenCVManager::testTensorflow()
{
// 訓練好的模型以及其模型的字尾名
// .caffemodel (Caffe, http://caffe.berkeleyvision.org/)
// .pb (TensorFlow, https://www.tensorflow.org/)
// .t7 | *.net (Torch, http://torch.ch/)
// .weights (Darknet, https://pjreddie.com/darknet/)
// .bin (DLDT, https://software.intel.com/openvino-toolkit)
// https://github.com/opencv/opencv/wiki/TensorFlow-Object-Detection-API
std::string weights = "E:/qtProject/openCVDemo/dnnData/" \
"ssd_mobilenet_v1_coco_2017_11_17/"frozen_inference_graph.pb";
std::string prototxt = "E:/qtProject/openCVDemo/dnnData/" \
"ssd_mobilenet_v1_coco_2017_11_17.pbtxt";
cv::dnn::Net net = cv::dnn::readNetFromTensorflow(weights, prototxt);
if(net.empty())
{
qDebug() << __FILE__ << __LINE__ << "net is empty!!!";
return;
}
cv::Mat mat;
cv::Mat blob;
// 獲得所有層的名稱和索引
std::vector<cv::String> layerNames = net.getLayerNames();
int lastLayerId = net.getLayerId(layerNames[layerNames.size() - 1]);
cv::Ptr<cv::dnn::Layer> lastLayer = net.getLayer(cv::dnn::DictValue(lastLayerId));
qDebug() << __FILE__ << __LINE__
<< QString(lastLayer->type.c_str())
<< QString(lastLayer->getDefaultName().c_str())
<< QString(layerNames[layerNames.size()-1].c_str());
#if 0
// 視訊裡面的識別
cv::VideoCapture capture;
if(!capture.open("E:/testFile/4.avi"))
{
qDebug() << __FILE__ << __LINE__ << "Failed to open videofile!!!";
return;
}
#endif
while(true)
{
#if 1
// 讀取圖片識別
mat = cv::imread("E:/testFile/15.jpg");
if(!mat.data)
{
qDebug() << __FILE__ << __LINE__ << "Failed to read image!!!";
return;
}
#else
// 視訊裡面的識別
capture >> mat;
if(mat.empty())
{
cv::waitKey(0);
break;
}
#endif
cv::dnn::blobFromImage(mat, blob);
net.setInput(blob);
// 推理預測:可以輸入預測的圖層名稱
// cv::Mat prob = net.forward("detection_out");
cv::Mat prob = net.forward();
// 顯示識別花費的時間
std::vector<double> layersTimes;
double freq = cv::getTickFrequency() / 1000;
double t = net.getPerfProfile(layersTimes) / freq;
std::string label = cv::format("Inference time: %.2f ms", t);
cv::putText(mat, label, cv::Point(0, 15), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0));
cv::Mat detectionMat(prob.size[2], prob.size[3], CV_32F, prob.ptr<float>());
// 置信度預製,大於執行度的將其使用rect框出來
float confidenceThreshold = 0.75;
for(int i = 0; i < detectionMat.rows; i++)
{
float confidence = detectionMat.at<float>(i, 2);
if (confidence > confidenceThreshold)
{
// 高於置信度的,獲取其x、y、以及對應的寬度高度,進行框選
int classId = (detectionMat.at<float>(i, 1));
int xLeftBottom = static_cast<int>(detectionMat.at<float>(i, 3) * mat.cols);
int yLeftBottom = static_cast<int>(detectionMat.at<float>(i, 4) * mat.rows);
int xRightTop = static_cast<int>(detectionMat.at<float>(i, 5) * mat.cols);
int yRightTop = static_cast<int>(detectionMat.at<float>(i, 6) * mat.rows);
cv::Rect object((int)xLeftBottom,
(int)yLeftBottom,
(int)(xRightTop - xLeftBottom),
(int)(yRightTop - yLeftBottom));
cv::rectangle(mat, object, cv::Scalar(0, 255, 0), 2);
qDebug() << __FILE__ << __LINE__
<< classId
<< confidence << confidenceThreshold
<< object.x << object.y << object.width << object.height;
}
}
cv::imshow(_windowTitle.toStdString(), mat);
cv::waitKey(0);
}
}
openCVDemo_v1.64.0_基礎模板_tensorFlow分類檢測.rar。
錯誤
原因
.pb模型檔案與.pbtxt檔案不對應,版本也有關係。
解決
更換模型,使用正確的pb與pbtxt對應的檔案。
上一篇:《OpenCV開發筆記(七十一):紅胖子8分鐘帶你深入級聯分類器訓練》
下一篇:持續補充中…