在前面出現的融合方法中,最突出的問題就是每次運算,都須要將整個推斷的過程所有操做一遍,這樣確定是費時間的——因此咱們須要將可以獨立的地方獨立出來,可是這個過中很是容易出現溢出的錯誤——通過一段時間的嘗試,終於獲得了相對穩定的結果,這裏將結果記錄下來:
一、原始狀態:
咱們已經將算法融合到了MFC中,而且可以發揮做用:
// 用於推斷的函數
Mat CGOMfcTemplate2Dlg
:
:IEInfer(Mat m_mainframe)
{
//初始化IE
// --------------------------- 1.爲IE準備插件-------------------------------------
InferencePlugin plugin(PluginDispatcher().getSuitablePlugin(TargetDevice
:
:eCPU));
plugin.AddExtension(std
:
:make_shared
<Extensions
:
:Cpu
:
:CpuExtensions
>());
//Extension,useful
// --------------------------- 2.讀取IR模型(xml和bin)---------------------------------
CNNNetReader networkReader;
networkReader.ReadNetwork(
"./road-segmentation-adas-0001.xml");
networkReader.ReadWeights(
"./road-segmentation-adas-0001.bin");
CNNNetwork network
= networkReader.getNetwork();
// --------------------------- 3. 準備輸入輸出的------------------------------------------
InputsDataMap inputInfo(network.getInputsInfo());
//得到輸入信息
if (inputInfo.size()
!=
1)
throw std
:
:logic_error(
"錯誤,該模型應該爲單輸入");
auto lrInputInfoItem
= inputInfo[
"data"];
//開始讀入
int w
=
static_cast
<
int
>(lrInputInfoItem
-
>getTensorDesc().getDims()[
3]);
//模型要求的輸入大小
int h
=
static_cast
<
int
>(lrInputInfoItem
-
>getTensorDesc().getDims()[
2]);
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. 讀取模型 ------------------------------------------(目視第4步驟最消耗時間)
ExecutableNetwork executableNetwork
= plugin.LoadNetwork(network, {});
// --------------------------- 5. 建立推斷 -------------------------------------------------
infer_request
= executableNetwork.CreateInferRequest();
// --------------------------- 6. 將數據塞入模型 -------------------------------------------------
Blob
:
:Ptr lrInputBlob
= infer_request.GetBlob(
"data");
//data這個名字是我看出來的,實際上這裏能夠更統一一些
matU8ToBlob
<float_t
>(m_mainframe, 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);
return resultImg;
}
這樣一段代碼,包含了1-8所有8個步驟,能夠在具體的狀況下被觸發(好比按下某個按鍵)
須要注意,運行代碼以後再正常退出,會報這樣一個錯誤,多是和系統某種資源沒有銷燬相關。
二、初步設想:
可是這樣很是低效,因此要提升效率。通過分析研究,最爲消耗時間的一步爲
// --------------------------- 4. 讀取模型 ------------------------------------------(目視第4步驟最消耗時間)
ExecutableNetwork executableNetwork = plugin.LoadNetwork(network, {});
因此但願可以將這步前調到Oninitdialog中,這樣每次運行過程當中,針對不一樣輸入,只須要運行
// --------------------------- 7. 推斷結果 -------------------------------------------------
infer_request.Infer();//多張圖片屢次推斷
而後將後面的結果進行顯示就能夠了。
三、容易出錯:
簡單的想法是直接將executableNetwork變成全局變量,而且在initdialog中申明完成,覺得這樣就可以將耗時的操做解決在
init階段。
可是很是常見的方法是這個。
四、解決方法:
實際上我沒有專門想出一個什麼方法解決這個問題,我作了較多實驗,不斷去驗證設想,最後發現這個方法可以達到目標。
將前面的1-4步分解爲如下函數(經過這個過程,發現原來代碼裏面一些不須要的部分):
CNNNetwork CGOMfcTemplate2Dlg
:
:IENetWork(string strXML, string strBIN)
{
CNNNetReader networkReader;
networkReader.ReadNetwork(strXML);
networkReader.ReadWeights(strBIN);
CNNNetwork network
= networkReader.getNetwork();
return network;
}
string CGOMfcTemplate2Dlg
:
:IENetSetup(CNNNetwork network)
{
InputsDataMap inputInfo(network.getInputsInfo());
//得到輸入信息
BlobMap inputBlobs;
//保持全部輸入的blob數據
if (inputInfo.size()
!=
1)
throw std
:
:logic_error(
"錯誤,該模型應該爲單輸入");
auto lrInputInfoItem
= inputInfo[
"data"];
//開始讀入
int h
=
static_cast
<
int
>(lrInputInfoItem
-
>getTensorDesc().getDims()[
2]);
int w
=
static_cast
<
int
>(lrInputInfoItem
-
>getTensorDesc().getDims()[
3]);
//模型要求的輸入大小
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);
}
return firstOutputName;
}
InferencePlugin CGOMfcTemplate2Dlg
:
:IEplugin(CNNNetwork network)
{
InferencePlugin plugin(PluginDispatcher().getSuitablePlugin(TargetDevice
:
:eCPU));
plugin.AddExtension(std
:
:make_shared
<Extensions
:
:Cpu
:
:CpuExtensions
>());
//Extension,useful
return plugin;
}
ExecutableNetwork CGOMfcTemplate2Dlg
:
:getNetWork(InferencePlugin plugin, CNNNetwork network)
{
ExecutableNetwork executableNetwork
= plugin.LoadNetwork(network, {});
return executableNetwork;
}
然後分別在oninitdialog和業務代碼中這樣執行
std
:
:string firstOutputName
= IENetSetup(network);
InferRequest infer_request
= executableNetwork.CreateInferRequest();
Blob
:
:Ptr lrInputBlob
= infer_request.GetBlob(
"data");
matU8ToBlob
<float_t
>(m_mainframe, lrInputBlob,
0);
//重要的轉換函數,第3個參數是batchSize,應該是本身+1的
// ---------------------------推斷結果 -------------------------------------------------
infer_request.Infer();
//多張圖片屢次推斷
// ---------------------------處理結果-------------------------------------------------------
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];
int h
= outputBlob
-
>getTensorDesc().getDims()[
2];
int 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);
showImage(resultImg, IDC_PIC);
//顯示原始圖像
這個結果,的確是提升了運算的效率。
而且可以直接嵌入攝像頭循環:
//攝像頭顯示循環,全部關於採集的操做是經過主線程傳遞控制變量到採集線程,然後由採集線程完成的
DWORD WINAPI CaptureThread(LPVOID lpParameter)
{
CGOMfcTemplate2Dlg
* pDlg
= (CGOMfcTemplate2Dlg
*)lpParameter;
double t_start
= (
double)cv
:
:getTickCount();
//開始時間
Mat tmpPrydown;
//#pragma omp parallel for
while (
true)
{
if (pDlg
-
>b_closeCam)
//退出循環
break;
double t
= ((
double)cv
:
:getTickCount()
- t_start)
/ getTickFrequency();
if (t
<
=
0.
1)
//fps =10,主動下降速度
{
Sleep(
100);
continue;
}
else
{
t_start
= (
double)cv
:
:getTickCount();
}
//從directX中得到當前圖像並顯示出來
IplImage
* queryframe
= pDlg
-
>cameraDs.QueryFrame();
//在2.0版本中能夠強轉,在3.0中須要使用函數
Mat camframe
= cvarrToMat(queryframe);
pDlg
-
>showImage(camframe, IDC_CAM);
//顯示原始圖像
////根據條件,決定是否採用算法
Mat dst;
Mat img;
Mat tmp;
Mat divideGaussMin;
Mat divideGaussMiddle;
Mat divideGaussMax;
cvtColor(camframe, img, COLOR_BGR2GRAY);
cvtColor(img, img, COLOR_GRAY2BGR);
if (pDlg
-
>bMethod)
//這裏實現的是灰度轉彩色
{
//算法
if (img.empty())
{
return
-
1;
}
std
:
:string firstOutputName
= pDlg
-
>IENetSetup(pDlg
-
>network);
InferRequest infer_request
= pDlg
-
>executableNetwork.CreateInferRequest();
Blob
:
:Ptr lrInputBlob
= infer_request.GetBlob(
"data");
matU8ToBlob
<float_t
>(img, lrInputBlob,
0);
//重要的轉換函數,第3個參數是batchSize,應該是本身+1的
// ---------------------------推斷結果 -------------------------------------------------
infer_request.Infer();
//多張圖片屢次推斷
// ---------------------------處理結果-------------------------------------------------------
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];
int h
= outputBlob
-
>getTensorDesc().getDims()[
2];
int 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
:
:merge(imgPlanes, dst);
}
else
{
dst
= img.clone();
}
pDlg
-
>showImage(dst, IDC_PIC);
//顯示網絡處理圖像
}
return
0;
}
最後的結果是實時效果。那麼這裏的東西是能夠複用的。
五、小結反思。
獲得最後這個結果比較滿意,這也是不斷嘗試的結果。問題產生的緣由多是多方面的,目前也只是獲得了基本的解決方法;更系統的方法應該是類化,這個會在後面處理級聯問題的時候遇到;而我這裏總結出來的函數化的方法,對於解決單模型簡單問題,應該已是可用的了。
可以看到這裏的,必定是遇到了相似問題的同事,那也是對相關問題有較爲深刻研究的了。
感謝閱讀至此,但願有所幫助。
附件列表