1、來源ide
模型例子本身帶來副圖像
2、簡化
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <vector>
#include <string>
#include <chrono>
#include <memory>
#include <utility>
#include <format_reader_ptr.h>
#include <inference_engine.hpp>
#include <ext_list.hpp>
#include <samples/slog.hpp>
#include <samples/args_helper.hpp>
#include <samples/ocv_common.hpp>
#include <format_reader_ptr.h>
#include "segmentation_demo.h"
using namespace InferenceEngine;
using namespace std;
using namespace cv;
//-i E:/OpenVINO_modelZoo/road.png -m E:/OpenVINO_modelZoo/road-segmentation-adas-0001.xml
void main()
{
std::vector<std::string> images;
string imageNames = "E:/OpenVINO_modelZoo/road.png";
images.push_back(imageNames);
// --------------------------- 1.爲IE準備插件-------------------------------------
InferencePlugin plugin(PluginDispatcher().getSuitablePlugin(TargetDevice::eCPU));
printPluginVersion(plugin, std::cout);//正確回顯表示成功
plugin.AddExtension(std::make_shared<Extensions::Cpu::CpuExtensions>());//Extension,useful
// --------------------------- 2.讀取IR模型(xml和bin)---------------------------------
CNNNetReader networkReader;
networkReader.ReadNetwork("E:/OpenVINO_modelZoo/road-segmentation-adas-0001.xml");
networkReader.ReadWeights("E:/OpenVINO_modelZoo/road-segmentation-adas-0001.bin");
CNNNetwork network = networkReader.getNetwork();
// --------------------------- 3. 準備輸入輸出的------------------------------------------
InputsDataMap inputInfo(network.getInputsInfo());//得到輸入信息
BlobMap inputBlobs; //保持全部輸入的blob數據
if (inputInfo.size() != 1) throw std::logic_error("錯誤,該模型應該爲單輸入");
auto inputInfoItem = *inputInfo.begin();//開始讀入
std::vector<std::shared_ptr<unsigned char>> imagesData;
for (auto & i : images) {
FormatReader::ReaderPtr reader(i.c_str()); //使用FormatReader來讀取圖片數據,這裏的images是一個vector,注意對於批量數據的讀取
if (reader.get() == nullptr) {
slog::warn << "Image " + i + " 沒法讀取!" << slog::endl;
continue;
}
/** 得到圖片數據 **/
std::shared_ptr<unsigned char> data(
reader->getData(inputInfoItem.second->getTensorDesc().getDims()[3],
inputInfoItem.second->getTensorDesc().getDims()[2]));
if (data.get() != nullptr) {
imagesData.push_back(data);
}
}
if (imagesData.empty()) throw std::logic_error("錯誤的格式,請檢查!");
network.setBatchSize(imagesData.size());
slog::info << "Batch size is " << std::to_string(networkReader.getNetwork().getBatchSize()) << slog::endl;
inputInfoItem.second->setPrecision(Precision::U8);
//準備輸出數據
OutputsDataMap outputInfo(network.getOutputsInfo());
std::string firstOutputName;
for (auto & item : outputInfo) {
if (firstOutputName.empty()) {
firstOutputName = item.first;
}
DataPtr outputData = item.second;
if (!outputData) {
throw std::logic_error("錯誤的格式,請檢查!");
}
item.second->setPrecision(Precision::FP32);
}
// --------------------------- 4. 讀取模型 ------------------------------------------(後面這些操做應該能夠合併了)
ExecutableNetwork executableNetwork = plugin.LoadNetwork(network, {});
// --------------------------- 5. 建立推斷 -------------------------------------------------
InferRequest infer_request = executableNetwork.CreateInferRequest();
// --------------------------- 6. 將數據塞入模型 -------------------------------------------------
for (const auto & item : inputInfo) {
/** 建立輸入BLOB **/
Blob::Ptr input = infer_request.GetBlob(item.first);
/** 3 通道塞數據 **/
size_t num_channels = input->getTensorDesc().getDims()[1];
size_t image_size = input->getTensorDesc().getDims()[3] * input->getTensorDesc().getDims()[2];
auto data = input->buffer().as<PrecisionTrait<Precision::U8>::value_type*>();
for (size_t image_id = 0; image_id < imagesData.size(); ++image_id) {
for (size_t pid = 0; pid < image_size; pid++) {
for (size_t ch = 0; ch < num_channels; ++ch) {
data[image_id * image_size * num_channels + ch * image_size + pid] = imagesData.at(image_id).get()[pid*num_channels + ch];
}
}
}
}
// --------------------------- 7. 推斷結果 -------------------------------------------------
for (size_t iter = 0; iter < images.size(); ++iter) {
infer_request.Infer();//多張圖片屢次推斷
}
// --------------------------- 8. 處理結果-------------------------------------------------------
slog::info << "輸出結果" << slog::endl;
const Blob::Ptr output_blob = infer_request.GetBlob(firstOutputName);
const auto output_data = output_blob->buffer().as<float*>();
size_t N = output_blob->getTensorDesc().getDims().at(0);
size_t C = output_blob->getTensorDesc().getDims().at(1);
size_t H = output_blob->getTensorDesc().getDims().at(2);
size_t W = output_blob->getTensorDesc().getDims().at(3);
size_t image_stride = W * H * C;
for (size_t image = 0; image < N; ++image) {
std::vector<std::vector<size_t>> outArrayClasses(H, std::vector<size_t>(W, 0));
std::vector<std::vector<float>> outArrayProb(H, std::vector<float>(W, 0.));
for (size_t w = 0; w < W; ++w) {
for (size_t h = 0; h < H; ++h) {
if (C == 1) {
outArrayClasses[h][w] = static_cast<size_t>(output_data[image_stride * image + W * h + w]);
}
else {
for (size_t ch = 0; ch < C; ++ch) {
auto data = output_data[image_stride * image + W * H * ch + W * h + w];
if (data > outArrayProb[h][w]) {
outArrayClasses[h][w] = ch;
outArrayProb[h][w] = data;
}
}
}
}
}
std::string fileName = "out_" + std::to_string(image) + ".bmp";
std::ofstream outFile(fileName, std::ofstream::binary);
if (!outFile.is_open()) {
throw std::logic_error("Can't open file : " + fileName);
}
writeOutputBmp(outArrayClasses, C, outFile); //輸出的代碼
slog::info << "File : " << fileName << " was created" << slog::endl;
}
// -----------------------------------------------------------------------------------------------------
}
在改寫的過程當中有幾點注意
一、添加lib和對應的dll文件,主要就是用於文件讀取的;
以及format_reader.dll 文件放到目錄下面;
二、頭文件修改正確
3、改寫
這個代碼裏面使用的是format_reader,使用起來頗爲不方便,修改成OpenCV負責輸入輸出。
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <vector>
#include <string>
#include <chrono>
#include <memory>
#include <utility>
#include <format_reader_ptr.h>
#include <inference_engine.hpp>
#include <ext_list.hpp>
#include <samples/slog.hpp>
#include <samples/args_helper.hpp>
#include <samples/ocv_common.hpp>
#include <format_reader_ptr.h>
#include "segmentation_demo.h"
using namespace InferenceEngine;
using namespace std;
using namespace cv;
//-i E:/OpenVINO_modelZoo/road.png -m E:/OpenVINO_modelZoo/road-segmentation-adas-0001.xml
void main()
{
std::vector<std::string> images;
string imageNames = "E:/OpenVINO_modelZoo/road.png";
images.push_back(imageNames);
// --------------------------- 1.爲IE準備插件-------------------------------------
InferencePlugin plugin(PluginDispatcher().getSuitablePlugin(TargetDevice::eCPU));
printPluginVersion(plugin, std::cout);//正確回顯表示成功
plugin.AddExtension(std::make_shared<Extensions::Cpu::CpuExtensions>());//Extension,useful
// --------------------------- 2.讀取IR模型(xml和bin)---------------------------------
CNNNetReader networkReader;
networkReader.ReadNetwork("E:/OpenVINO_modelZoo/road-segmentation-adas-0001.xml");
networkReader.ReadWeights("E:/OpenVINO_modelZoo/road-segmentation-adas-0001.bin");
CNNNetwork network = networkReader.getNetwork();
// --------------------------- 3. 準備輸入輸出的------------------------------------------
InputsDataMap inputInfo(network.getInputsInfo());//得到輸入信息
BlobMap inputBlobs; //保持全部輸入的blob數據
if (inputInfo.size() != 1) throw std::logic_error("錯誤,該模型應該爲單輸入");
//auto lrInputInfoItem = *inputInfo.begin();//開始讀入
//int w = static_cast<int>(lrInputInfoItem.second->getTensorDesc().getDims()[3]); //這種寫法也是能夠的,它的first就是data
//int h = static_cast<int>(lrInputInfoItem.second->getTensorDesc().getDims()[2]);
auto lrInputInfoItem = inputInfo["data"]; //開始讀入
int w = static_cast<int>(lrInputInfoItem->getTensorDesc().getDims()[3]); //模型要求的輸入大小
int h = static_cast<int>(lrInputInfoItem->getTensorDesc().getDims()[2]);
Mat src = imread(imageNames);
if (src.empty())
return;
network.setBatchSize(1);//只有1副圖片,故BatchSize = 1
//準備輸出數據
OutputsDataMap outputInfo(network.getOutputsInfo());//得到輸出信息
std::string firstOutputName;
for (auto &item : outputInfo) {
if (firstOutputName.empty()) {
firstOutputName = item.first;
}
DataPtr outputData = item.second;
if (!outputData) {
throw std::logic_error("錯誤的格式,請檢查!");
}
item.second->setPrecision(Precision::FP32);
}
// --------------------------- 4. 讀取模型 ------------------------------------------(後面這些操做應該能夠合併了)
ExecutableNetwork executableNetwork = plugin.LoadNetwork(network, {});
// --------------------------- 5. 建立推斷 -------------------------------------------------
InferRequest infer_request = executableNetwork.CreateInferRequest();
// --------------------------- 6. 將數據塞入模型 -------------------------------------------------
Blob::Ptr lrInputBlob = infer_request.GetBlob("data"); //data這個名字是我看出來的,實際上這裏能夠更統一一些
matU8ToBlob<float_t>(src, lrInputBlob, 0);//重要的轉換函數,第3個參數是batchSize,應該是本身+1的
// --------------------------- 7. 推斷結果 -------------------------------------------------
infer_request.Infer();//多張圖片屢次推斷
// --------------------------- 8. 處理結果-------------------------------------------------------
const Blob::Ptr outputBlob = infer_request.GetBlob(firstOutputName);
const auto outputData = outputBlob->buffer().as<PrecisionTrait<Precision::FP32>::value_type*>();
size_t numOfImages = outputBlob->getTensorDesc().getDims()[0];
size_t numOfChannels = outputBlob->getTensorDesc().getDims()[1];
h = outputBlob->getTensorDesc().getDims()[2];
w = outputBlob->getTensorDesc().getDims()[3];
size_t nunOfPixels = w * h; //寫在內存裏的結果,仍是要拼出來的
std::vector<cv::Mat> imgPlanes{ cv::Mat(h, w, CV_32FC1, &(outputData[0])),
cv::Mat(h, w, CV_32FC1, &(outputData[nunOfPixels])),
cv::Mat(h, w, CV_32FC1, &(outputData[nunOfPixels * 2])) };
for (auto & img : imgPlanes) //原本是平的
img.convertTo(img, CV_8UC1, 255);
cv::Mat resultImg;
cv::merge(imgPlanes, resultImg);
cv::imshow("result", resultImg);
cv::waitKey();
}
這裏須要注意的一點是在讀取圖片的大小的時候,我這裏使用了
Blob::Ptr lrInputBlob = infer_request.GetBlob("data"); //data這個名字是我看出來的,實際上這裏能夠更統一一些
其前提是我知道這裏叫作 data,這裏能夠改爲更統一的方式。
從結果來看,我認爲OpenCV轉換後的結果更好。固然差異只是在着色而已。
4、數據集測試
使用UAS Dataset進行測試,主要是想看一看批量數據的處理。使用Sample中的程序進行處理:
這個操做應該就是多張圖片。
這個輸入輸出的界面就LOW了,我認爲沒有必要在函數中進行這個處理,函數處理單張就能夠。原模型也是不支持視頻的。
該造後的效果就很好
5、融合
在GOMFCTemplate中運行
其中,容易犯錯的地方(release版本和debug版本的 cpu_extension重名,因此不能放到system目錄下面):
以及OCV_COMMON可能引發混亂
第一步是直接替換
目前存在的一個突出問題,就是模型的建立和模型的infer獨立的問題。這個東西在OpenVINO中可能有,但要去尋找,不是直接告訴你的東西。
作到這一步,雖然代碼已經能夠運行,可是突出的問題就是沒有模塊化,整個運算步驟都在循環中,這樣效率確定是很低下的。而且在資源的銷燬處還存在問題。
能夠進一步將其封裝爲函數:
好比相似這裏面的
就是下一步須要參考的。