發表文章

C++ dll動態程式庫建置架構筆記(CMake build) -5 如何使用LoadLibraryA()讀取class架構dll

上一篇介紹dll裡建立class,但他是使用header.h+.lib+.dll方式讀取dll程式,這篇要講解我如何使用LoadLibraryA()讀取,先說程式碼會很多,因為要把整個class重新改寫成function型式,這方法應該能讓C++ dll程式在C語言上執行。這其實是我一開始學習建置dll的方式,順便紀錄一下,可能還有更好的方式我還沒研究到。 一、void pointer說明 這個功能很強大,他可以指向所有資料型態的指標,我這邊把他拿來指向整個class。在外部使用的人只會看到這是一個void pointer,不知道他其實是一個class,所以可以將整個class隱藏在dll內部。 二、建立class指標 假如有一個 class 名稱是 foo。 class foo { void Add(int b); ... }; 使用 void pointer 指標指向 class。 //create void pointer typedef void* foo_handle; //void pointer指標指向新建立的class foo_handle foo_h = (foo_handle)new foo(); 三、刪除空指標裡的class 當 class 不再使用時,或程式結束時,要將佔用的資源釋放,pointer 要指向 NULL,因為通常會檢查這個指標如果非 NULL 表示指向的 class 還存在著。 //刪除class delete (foo*)foo_h; //將void pointer 指向空 foo_h = NULL; 四、在dll裡建立\刪除class 我這邊使用 extern "C" __declspec(dllexport) 建立dll function。 typedef void* foo_handle; extern "C" __declspec(dllexport) foo_handle createClass() { return (foo_handle)new foo(); } extern "C" __declspec(dllexport) int des

C++ dll動態程式庫建置架構筆記(CMake build) -4 如何使用class架構

其實這部分我還沒研究的很深入,只先將我所知道的部分記錄下來。 我主要會針對在 class 裡需要儲存變數的做法說明,最常見的情況就是 class 裡面會暫時儲存一些資料給多個 funciton 使用,他們的資料型態可能是 C++ 原有的,也可能是自己定義的 struct,再來考慮到這些資料型態是僅內部使用還是要傳出給外部使用等各種可能的情況。 一、使用情況 第1種情況 : 暫存變數是 C++ 資料型態,可在 class 內部及外部使用。 只要使用的人也有這些資料型態的定義就可以,假如其中有一個型態使用的人無法知道,就要附加上,如果無法附加上那只能用以下方法將它隱藏在內部,自己寫一個轉譯的 function 傳送出去。 第2種情況 : 暫存變數非 C++ 資料型態,僅在class內部使用。 第3種情況 : 暫存變數非 C++ 資料型態,需將資料傳出使用。 之後我使用以下的架構,struct newNumber 在2、3種方法裡當作非 C++ 變數 struct newNumber { int i; int j; }; class math_class { public: newNumber a; math_class(int val) { a.i=val; a.j=val*10; } void Add(int b) { a.i+=b; a.j+=b; } }; 二、做法及範例 第1種情況 可以用比較簡單作法,相似於 第一篇文章 的寫法,因為 struct 本身及 struct 內部都使用 C++ 資料型態,使用的人都知道這些資料型態,只要這個 struct 使用的人也有定義就好。以下用第2種實作範例: 最外層的header.h,編譯完能給使用者匯入的 Dll_class_c\math_class\include\my_math_class_c.h #ifndef my_math_H_ #define my_math_H_ #include "SVdef.h" struct newNumber { int i; int j; }; class SV_EXPORTS math_class {

C++ dll動態程式庫建置架構筆記(CMake build) -3 建立兩層(多層)dll程式

寫這系列筆記主要就是為了要記錄"如何建立多層 dll 程式"的做法而生,網路上看到很少有人說明 dll 去使用 dll 的做法怎麼做,也可能沒輸入到對的關鍵字,雖然其實學會編譯 dll 及使用執行程式測試做出來的 dll,就可能已經大概知道怎麼建立兩層以上的 dll,但我本人就是沒有那個慧根,也可能是因為我一開始是學 LoadLibraryA() 匯入 dll 檔的作法,以至於覺得第二層也是要這樣讀取很複雜,為了探討怎麼輕鬆的做出多層 dll 程式,才有這篇文章。 一、架構說明 Dll_1Layer_math.dll 內容: 建立一個簡單的 dll 數學運算程式,加減乘除等...。 Dll_2Layer_tool.dll 內容: 重新建立函數名稱,使用 Dll_1Layer_math.dll 內函數。 test.exe 內容: 測試 Dll_2Layer_tool.dll 內的函數。 編譯順序: Dll_1Layer_math.dll → Dll_2Layer_tool.dll → test.exe  二、建議作法 學習了兩種匯入 dll 的方式後,我發現使用 LoadLibraryA() 比較繁瑣,在重寫函數名稱時容易出錯,修改上也比較麻煩,所以我覺得在必要時才去使用這個做法。 Dll_2Layer_tool.dll → test.exe: 建議兩種做法都可以:1. LoadLibraryA() 2. header.h+.lib+.dll Dll_1Layer_math.dll → Dll_2Layer_tool.dll: 建議做法 header.h+.lib+.dll 三、實作Dll_2Layer_tool.dll Dll_1Layer_math.dll 的實作我寫在 第一篇文章 。 檔案結構: 根目錄 ├─common │ └─SVdef.h ├─Dll_2Layer │ ├─main │ │ ├─test.cpp │ │ └─CMakeFiles.txt │ ├─tool │ │ ├─include │ │ │ └─tool_API.h │ │ └─src │ │ └─tool_API.cpp │ └─CMakeF

C++ dll動態程式庫建置架構筆記(CMake build) -2 如何使用dll程式

這篇介紹如何讀取及使用 C++ 動態程式庫。 我的系統環境: Win10 CMake 3.18.4 VisualStudio 2019 一、介紹做法 我所知道的有兩種做法: 1.需要header、lib檔     優點:程式碼較少、不用重寫定義     缺點:還不知道 2.只需要dll檔     優點:可在程式執行中期匯入及釋放      缺點:程式碼較繁瑣、使用的人要知道 dll 裡面的 function 名稱及參數架構 二、說明 作法一、需要header、lib檔 需要 include dll 程式提供給外部使用的 function 定義檔,也就是有加 __declspec(dllexport) 的部分。 以下是使用 上一篇文章 所做出來的 dll 程式。 test.cpp #include <stdio.h> #include "my_math.h" int main() { int a = 10; int b = 20; int res = Add(a,b); printf("Add %d\n", res); res = Subtraction(a,b); printf("Subtraction %d\n", res); res = Multiply(a,b); printf("Multiply %d\n", res); math_class math_cls; res = math_cls.Add(a,b); printf("math_class Add %d\n", res); res = math_cls.Subtraction(a,b); printf("math_class Subtraction %d\n", res); res = math_cls.Multiply(a,b); printf("math_class Multiply %d\n", r

C++ dll動態程式庫建置架構筆記(CMake build) -1 dll程式寫法

這篇介紹如何建置一個基本的 C++ 動態程式庫。 我的系統環境: Win10 CMake 3.18.4 VisualStudio 2019 一、程式寫法差異 1.一般的寫法 my_math.h int Add(int a, int b); class math_class { public: int Add(int a, int b); }; my_math.cpp int Add(int a, int b) { return a+b; } int math_class::Add(int a, int b) { return a+b; } 2.dll的寫法 在需要提供給外部使用的 function 或是 class 加上 __declspec(dllexport) ,告訴編譯器這是能提供給外部使用的 API 介面。 my_math.h extern "C" __declspec(dllexport) int Add(int a, int b); class __declspec(dllexport) math_class { public: int Add(int a, int b); }; my_math.cpp int Add(int a, int b) { return a+b; } int math_class::Add(int a, int b) { return a+b; } extern "C" 探討 我看其他人講解應該是能讓程式在 C 與 C++ 之間混用,如果都是 C++ 環境是不是就可以不用寫,這部分我不是很清楚,求大神解惑了。 我覺得應該是將 C++ 轉為 C 語言的格式,能讓 C++ dll 程式在 C 語言上執行,因為曾經在 function 參數的部分使用 std::string 資料型態,結果使用這個 dll 的人說他無法讀到 std::string 裡面的資料,須注意可能只能使用 C 語言有的資料型態。 二、實作 檔案架構 根目錄 ├─common │ └─SVdef.h ├─Dll_1Layer │

熱影像物件偵測 (Object Detection on Thermal Image) - 3 使用TensorRT C++ Win10 - 寫TensorRT C++

圖片
上篇 文章 我們測試了onnx格式的model,確認這個model是可以使用的。接下來就要在 TensorRT 上寫C++ 程式執行。 一、安裝tensorRT 可以參考我之前寫 安裝tensorRT的文章 ,依照步驟安裝。 二、複製tensorRT sample 程式修改 我使用 samples\sampleOnnxMNIST 做修改,因為都是讀入onnx格式的model。 將 sampleOnnxMNIST 資料夾複製一份出來,一樣放在 samples 資料夾下,修改檔名叫做sampleOnnxThermalObjDetection,由於model input output 格式跟sample的不一樣,所以要稍微修改。 三、專案屬性加入opencv設定 我們需要讀入圖片,以及對圖片的處理。 這裡我借用openvino裡的opencv來用。 1.開啟專案的屬性頁面,在"其他 Include 目錄"裡添加 opencv include 路徑: 2.在"其他程式庫目錄"裡添加 opencv lib 路徑: 3.在"其他相依性"裡添加 opencv 使用到的 lib 檔名: 四、修改程式碼 開啟 sampleOnnxMNIST.cpp 檔案做修改 1. include opencv #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgcodecs.hpp> #include <opencv2/imgproc.hpp> 2.修改 model input output name: 將 initializeSampleParams() 修改成以下,之後要將model及image放在 samples/sampleOnnxThermalObjDetection/ 路徑之下。 samplesCommon::OnnxSampl

熱影像物件偵測 (Object Detection on Thermal Image)- 2 使用TensorRT C++ Win10 - 測試onnx model

圖片
上篇 文章 我們得到了onnx格式的model,先做個測試確認這個model是可以使用的,需要在onnx framework下執行這個model,這邊使用到python onnx runtime。 一、下載onnx runtime 可以在 https://www.onnxruntime.ai/ 下載你需要的版本。 我下載的是GPU版,在python環境下輸入: pip install onnxruntime-gpu 二、寫執行程式 1.import import cv2> import numpy as np import onnxruntime as ort import onnxruntime.backend 2.讀入 onnx model modelName = 'weights/export.onnx' sess = ort.InferenceSession(modelName) 3.顯示讀入model的相關資訊 print("device", ort.get_device()) print("in count", len(sess.get_inputs())) print("out count", len(sess.get_outputs())) input_name = sess.get_inputs()[0].name print("input name", input_name) input_shape = sess.get_inputs()[0].shape print("input shape", input_shape) input_type = sess.get_inputs()[0].type print("input type", input_type) output_name0 = sess.get_outputs()[0].name print("output0 name", output_name0) output_shape0 = sess.get_outputs()[0].shape pr

熱影像物件偵測 (Object Detection on Thermal Image) - 1 使用TensorRT C++ Win10 - 測試model及匯出onnx model

圖片
最近疫情的關係熱影像攝影機的應用瞬間發展很快,所以想說有能不能以熱影像畫面為基礎,作物件辨識。在網路上尋找到 這篇文章 ,使用yolov3-spp模型架構訓練,model細節及介紹請看原文,這裡主要是要說明我怎麼將他的model轉換成tensorRT可以執行的部分。想研究如果用c++執行這個model能不能更快一些。 一、clone github 1.clone  github專案 下來 2.Download pre-trained weights 我下載下來的 pre-trained weights 長這樣,根本不知道要使用哪個才對,如果有人嘗試其他的可以留言跟我說喔。 二、測試程式可以執行 專案根目錄裡有個detect.py,這是我們開始的主程式,我嘗試出以下組合是可以使用的。 類別名稱檔:data/coco.data model_config:cfg/yolov3-spp.cfg model:weights/yolov3-spp.weights 如果在data資料夾裡沒有找到 coco.data、coco.names檔案的話請解壓縮labels.zip 帶入所需的參數執行: --data <類別名稱檔> --cfg <model_config> --weights <model> --source <要辨識的圖片資料夾或影片檔> python detect.py --data data/coco.data --cfg cfg/yolov3-spp.cfg --weights weights/weights/yolov3-spp.weights --source data/samples 這是他deteset裡面的一張圖的便是結果: 三、執行速度 我使用的系統環境是: CPU:i7-10700 GPU:GTX1660 SUPER CUDA:10.2 python:3.6.8 pytorch:1.8.1 onnx:1.8.0 每一張處理時間是:0.02秒左右 四、model匯出成onnx格式 1.切換匯出onnx model模式 在models.

訂午餐/訂飲料/團購訂單 使用excel製作 - 4 統計項目唯一值/去除重複值 使用office365 新UNIQUE函數

圖片
新的 office 365 在 excel 回傳多個數值推出了一個新功能"溢出",意思是如果公式得出多個數值,無法在一個儲存格內全部顯示,他會溢出到鄰近的空白儲存格內,但是當周遭都沒有空白儲存格時會顯示"溢出",所以要注意溢出的地方要有空白儲存格讓他溢出。 一、公式介紹 主要是用以下兩個公式: UNIQUE (array,[by_col],[exactly_once]) FILTER(array,include,[if_empty]) 1.UNIQUE (array,[by_col],[exactly_once]) 描述:傳回清單或範圍中的唯一值清單。 array - 尋找唯一值的範圍 [by_col] - TRUE:欄彼此比較 FALSE:(可省略)列彼此比較 [exactly_once] - TRUE:剛好發生一次之列或欄 FALSE:(可省略)返回所有不同的列或欄 2.FILTER(array,include,[if_empty]) 描述:根據自定義的準則來篩選資料範圍。 array - 要篩選的陣列或範圍 include - 布林陣列的高度或寬度必須與該陣列相同 [if_empty] - 若所包含陣列中的全部值均為空時要傳回的值 二、取得項目的唯一值(使用office365) 使用 FILTER 函數是因為當取唯一值範圍裡有空白儲存格時,空白儲存格會被當作一個唯一值看待,但我的資料有時量多有時量少資料長度不固定,公式每次都調整資料範圍非常麻煩所以要將空白儲存格捨去。 1.有空白儲存格的情況 A12、A13為空白儲存格,取唯一值後空白儲存格會當作0。 =UNIQUE(A2:A13) 2.解決方法 不等於的寫法: <> 空白值 ""  公式一:先移除空白再取唯一值 =UNIQUE(FILTER(A2:A13, A2:A13<>"")) 黃色為公式拆解步驟結果 拆解步驟公式

訂午餐/訂飲料/團購訂單 使用excel製作 - 3 統計項目唯一值/去除重複值 使用陣列公式

圖片
上篇 文章 統計了商品項目,但是中間會有空白的欄位,不太符合一般視覺感官,接下來我們就要將空白的部分移除掉。 作法是將抓出來的商品項目移除掉空白值,重新排序商品項目,由於要一次計算多個項目,所以我們這邊會需要用到陣列公式。 一、陣列公式簡單介紹 案例一:一次計算多個儲存格,回傳一個值 多個值相乘後再加總(A欄 * B欄)後加總,公式為下圖: 計算結果: 如果改為陣列公式計算只需要一行公式就可以解決,只佔用一格儲存格空間,省空間也美觀。 公式改為: {=SUM(A2:A4*B2:B4)} 注意:A欄的格數要與B來的格數一致 {}為陣列公式符號,不能直接輸入{},要使用 ctrl + shift + enter 輸入 步驟: 在儲存格內輸入 =SUM(A2:A4*B2:B4) ,按  ctrl + shift + enter  完成儲存格輸入 計算結果: 案例二:一次計算多個儲存格回傳多個值 判斷數值是否大於50 公式為: {=A2:A11>50} {}為陣列公式符號,不能直接輸入{},要使用  ctrl + shift + enter  輸入 步驟: 框選C2:C11,接著輸入公式 =A2:A11>50 ,按  ctrl + shift + enter  完成儲存格輸入 二、所需公式介紹 OFFSET(reference, rows, cols, [height], [width]) INDEX(array, row_num, [column_num]) SMALL(array, k) • OFFSET(reference, rows, cols, [height], [width]) 描述:傳回指定列數及欄數之儲存格或儲存格範圍 reference - 參考位置 rows, cols - 以參考位置為基準的欄列位移量 [height], [width] - 回傳儲存格範圍的寬高 範例:回傳A3到A5儲存格範圍 公式