cv dnn识别动作规范 open_OpenCV开发笔记(七十三):红胖子8分钟带你使用opencv+dnn+yolov3识别物体...
前言
級聯分類器的效果并不是很好,準確度相對深度學習較低,上一章節使用了dnn中的tensorflow,本章使用yolov3模型,識別出具體的分類。
Demo
320x320,置信度0.6
608x608,置信度0.6(.cfg里面是608)
yolov3模型下載
OpenCV深度識別基本流程
opencv3.4.x支持了各種模型。
支持的模型
操作步驟:yolov3
不同深度學習框架產生的模型,在操作上和數據輸出上有一些區別。梳理下opencv使用tensorflow訓練好的模型的使用步驟。
步驟一:讀取分類文件
模型文件對應了不同的分類文件,分類文件是以行為標識,所在的行數(0開始),就是最終識別出的分類號的第幾個分類。
std::string classesFile = "E:/qtProject/openCVDemo/dnnData/" \
"yolov3/coco.names";
// 讀入分類名稱,存入緩存
std::ifstream ifs(classesFile);
std::vector<:string> classes;
std::string classLine;
while(std::getline(ifs, classLine))
{
classes.push_back(classLine);
}
步驟二:加載模型和配置文件,建立神經網絡。
根據不同的模型,使用cv::dnn::readNetFromXXX系列函數進行讀取,opencv3.4.x系列支持的dnn模型(支持模型往上看)。
yolov3模型如下:
std::string modelWeights = "E:/qtProject/openCVDemo/dnnData/" \
"yolov3/yolov3.weights";
std::string modelCfg = "E:/qtProject/openCVDemo/dnnData/" \
"yolov3/yolov3.cfg";
// 加載yolov3模型
cv::dnn::Net net = cv::dnn::readNetFromDarknet(modelCfg, modelWeights);
if(net.empty())
{
qDebug() << __FILE__ << __LINE__ << "net is empty!!!";
return;
}
步驟三:將要預測的圖片加入到神經網絡中
加入之后,需要識別圖片,那么需要把圖片輸入到神經網絡當中去,使用yolov3模型特別注意,要先進行歸一化,然后變成指定大小的圖片,如下:
// 讀取圖片識別
mat = cv::imread("E:/testFile/15.jpg");
if(!mat.data)
{
qDebug() << __FILE__ << __LINE__ << "Failed to read image!!!";
return;
}
// cv::dnn::blobFromImage(mat, blob);
// 必須要設置,否則會跑飛
cv::dnn::blobFromImage(mat,
blob,
1.0f/255,
cv::Size(320, 320),
cv::Scalar(0, 0, 0),
true,
false);
net.setInput(blob);
寬度高度增加可以提升檢測的準確度,最好是根據cfg文件進行修改,本Demo是320x320,實際.cfg文件中的是608x608,并且經過測試,這個是識別效果最好的像素,大于608則會跑飛。
步驟四:分類預測,獲取識別的結果
輸入之后,就進行識別,識別是向前預測(分類預測),并且拿到結果,對于yolov3模型,規定了有3個輸出層,所以需要先獲取3個輸出層,然后預測的時候就需要指定預測這3個輸出層,否則會跑飛。
// 獲取輸出的層
std::vector<:string> outPutNames;
std::vector outLayers = net.getUnconnectedOutLayers();
for(int index = 0; index < outLayers.size(); index++)
{
outPutNames.push_back(layerNames[outLayers[index] - 1]);
qDebug() << __FILE__ << __LINE__
<< QString(layerNames[outLayers[index] - 1].c_str());
}
// 推理預測:可以輸入預測的圖層名稱
std::vector<:mat> probs;
net.forward(probs, outPutNames);
對于預測的結果,存于std::vectorcv::Mat類型的probs,每一個元素指定為cv::Mat類型的prob,每一行代表一個檢測到的分類,具體列信息如下表:
(注意:具體的使用,請參照“步驟五”)
步驟五:對達到置信度的可以通過輸出的mat進行分類和框選
關鍵的輸出結果步驟,不同的識別有區別,yolov3如下圖:
// 置信度預制,大于執行度的將其使用rect框出來
for(int index = 0; index < probs.size(); index++)
{
for (int row = 0; row < probs[index].rows; row++)
{
// 獲取probs中一個元素里面匹配對的所有對象中得分最高的
cv::Mat scores = probs[index].row(row).colRange(5, probs[index].cols);
cv::Point classIdPoint;
double confidence;
// Get the value and location of the maximum score
cv::minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
if(confidence > 0.6)
{
qDebug() << __FILE__ << __LINE__ << confidence << classIdPoint.x;
int centerX = (int)(probs.at(index).at(row, 0) * mat.cols);
int centerY = (int)(probs.at(index).at(row, 1) * mat.rows);
int width = (int)(probs.at(index).at(row, 2) * mat.cols);
int height = (int)(probs.at(index).at(row, 3) * mat.rows);
int left = centerX - width / 2;
int top = centerY - height / 2;
cv::Rect objectRect(left, top, width, height);
cv::rectangle(mat, objectRect, cv::Scalar(255, 0, 0), 2);
cv::String label = cv::format("%s:%.4f",
classes[classIdPoint.x].data(),
confidence);
cv::putText(mat,
label,
cv::Point(left, top - 10),
cv::FONT_HERSHEY_SIMPLEX,
0.4,
cv::Scalar(0, 0, 255));
qDebug() << __FILE__ << __LINE__
<< centerX << centerY << width << height;
}
}
}
函數原型
讀取yolov3模型與配置文件函數原型
Net readNetFromDarknet(const String &cfgFile,
const String &darknetModel = String());
從文件中讀取。
參數一:帶有網絡體系結構文本描述的.cfg文件的路徑;
參數二:已學習網絡的.weights文件的路徑;
讀取圖片(需要識別的)函數原型
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);.
從圖像創建區域。可選擇從中心調整和裁剪圖像。
參數一:圖像輸入圖像(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深度。
參數二:輸入層的名稱。
參數三:可選的標準化刻度。
參數四:可選的平均減去值。
返回所有層的名稱(按照本身的索引循序排列)
std::vector getLayerNames() const;
返回具有未連接輸出的層的索引。
std::vector getUnconnectedOutLayers() const;
深度檢測識別(向前預測)函數原型
void cv::dnn::Net::Mat forward(const String& outputName = String());
向前預測,返回指定層的第一個輸出的blob,一般是返回最后一層,可使用cv::Net::getLayarNames()獲取所有的層名稱。
參數一:outputName需要獲取輸出的層的名稱
Demo
void OpenCVManager::testYoloV3()
{
std::string classesFile = "E:/qtProject/openCVDemo/dnnData/" \
"yolov3/coco.names";
std::string modelWeights = "E:/qtProject/openCVDemo/dnnData/" \
"yolov3/yolov3.weights";
std::string modelCfg = "E:/qtProject/openCVDemo/dnnData/" \
"yolov3/yolov3.cfg";
// 讀入分類名稱,存入緩存
std::ifstream ifs(classesFile);
std::vector<:string> classes;
std::string classLine;
while(std::getline(ifs, classLine))
{
classes.push_back(classLine);
}
// 加載yolov3模型
cv::dnn::Net net = cv::dnn::readNetFromDarknet(modelCfg, modelWeights);
if(net.empty())
{
qDebug() << __FILE__ << __LINE__ << "net is empty!!!";
return;
}
cv::Mat mat;
cv::Mat blob;
// 獲得所有層的名稱和索引
std::vector<:string> layerNames = net.getLayerNames();
int lastLayerId = net.getLayerId(layerNames[layerNames.size() - 1]);
cv::Ptr<: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());
// 獲取輸出的層
std::vector<:string> outPutNames;
std::vector outLayers = net.getUnconnectedOutLayers();
for(int index = 0; index < outLayers.size(); index++)
{
outPutNames.push_back(layerNames[outLayers[index] - 1]);
qDebug() << __FILE__ << __LINE__
<< QString(layerNames[outLayers[index] - 1].c_str());
}
while(true)
{
// 讀取圖片識別
mat = cv::imread("E:/testFile/15.jpg");
if(!mat.data)
{
qDebug() << __FILE__ << __LINE__ << "Failed to read image!!!";
return;
}
// cv::dnn::blobFromImage(mat, blob);
// 必須要設置,否則會跑飛
cv::dnn::blobFromImage(mat,
blob,
1.0f/255,
cv::Size(320, 320),
cv::Scalar(0, 0, 0),
true,
false);
net.setInput(blob);
// 推理預測:可以輸入預測的圖層名稱
std::vector<:mat> probs;
net.forward(probs, outPutNames);
// 顯示識別花費的時間
std::vector 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(255, 0, 0));
// 置信度預制,大于執行度的將其使用rect框出來
for(int index = 0; index < probs.size(); index++)
{
for (int row = 0; row < probs[index].rows; row++)
{
// 獲取probs中一個元素里面匹配對的所有對象中得分最高的
cv::Mat scores = probs[index].row(row).colRange(5, probs[index].cols);
cv::Point classIdPoint;
double confidence;
// Get the value and location of the maximum score
cv::minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
if(confidence > 0.6)
{
qDebug() << __FILE__ << __LINE__ << confidence << classIdPoint.x;
int centerX = (int)(probs.at(index).at(row, 0) * mat.cols);
int centerY = (int)(probs.at(index).at(row, 1) * mat.rows);
int width = (int)(probs.at(index).at(row, 2) * mat.cols);
int height = (int)(probs.at(index).at(row, 3) * mat.rows);
int left = centerX - width / 2;
int top = centerY - height / 2;
cv::Rect objectRect(left, top, width, height);
cv::rectangle(mat, objectRect, cv::Scalar(255, 0, 0), 2);
cv::String label = cv::format("%s:%.4f",
classes[classIdPoint.x].data(),
confidence);
cv::putText(mat,
label,
cv::Point(left, top - 10),
cv::FONT_HERSHEY_SIMPLEX,
0.4,
cv::Scalar(0, 0, 255));
qDebug() << __FILE__ << __LINE__
<< centerX << centerY << width << height;
}
}
}
cv::imshow(_windowTitle.toStdString(), mat);
cv::waitKey(0);
}
}
對應工程模板v1.65.0
openCVDemo_v1.65.0_基礎模板_yolov3分類檢測.rar。
入坑
入坑一:加載模型時候錯誤
錯誤
原因
模型文件加載錯誤。
解決
檢查文件是否存在,路徑是否正確,模型文件是否能對應上。
入坑二:輸入blob時錯誤
錯誤
原因
預測的時候未輸入參數,需要輸入參數(注意:tensorflow未輸入沒有問題)。
解決
總結
以上是生活随笔為你收集整理的cv dnn识别动作规范 open_OpenCV开发笔记(七十三):红胖子8分钟带你使用opencv+dnn+yolov3识别物体...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python dbscan 如何确定ep
- 下一篇: js 获取屏幕高宽_JS获取屏幕的宽高。