Matlab与C/C++混合编程 (基于Opencv库)
之前用過基于VS2018 與MATLAB2018a 混合編程(C++特性)(見https://blog.csdn.net/wwwoowww/article/details/83013801),奈何后來matlab版本換成了2016a,混合編程的方式不一樣了,自己嘗試了幾天,終于搞定了。
目錄
版本選擇
matlab與c++的混合編程有兩個方法:
利用mex的方法
創建C++動態庫
版本選擇
版本: Win10 ,VS2015, Matlab2016a
注意:Matlab2016支持的VS版本有兼容問題,matlab具體支持哪些VS版本請見matlab的安裝文件夾路徑:C:\Program Files\MATLAB\R2016a\bin\win64\mexopts 中包含了哪些編譯器。如下圖所示,我的文件夾中包含了vs2015,說明該版本的matlab支持vs2015的編譯器。
若想要支持的vs版本文件夾中沒有,需要自己去下載對應版本的文件。詳細的內容請見或自己找找吧,我是放棄了:https://blog.csdn.net/cztqwan/article/details/78902530
?
matlab與c++的混合編程有兩個方法:
mex 和調用C++動態庫
?
利用mex的方法
若是不在c/c++中使用其他第三方庫,可以使用簡單的編譯方法。
- ?matlab中使用mex -setup 命令,選擇c++ 的編譯器 (第一次可以使用mex setup -v?,先查找所有的已安裝并且支持的編譯器)
- 創建test.cpp 文件, 文件輸出 傳入的第一個參數和參數的個數
matlab官方文檔,對于mexFunction的解釋:https://ww2.mathworks.cn/help/matlab/apiref/mexfunction.html
簡單來說,mexFunction是matlab調用C/C++的入口,類似main函數,
nlhs 輸出的個數, plhs matlab的array類型,是輸出參數的地址
nrhs 輸入數組的個數, prhs matlab的array類型,包含了輸入參數地址
- matlab中使用命令 mex **.cpp。編譯成功后使用文件名調用
- 若要使用第三方庫,需要創建make.m文件(但是我自己按網上的嘗試,沒有鏈接成功,放棄了)
?
創建C++動態庫
上面我們使用了matlab中的vs2015編譯器,鑒于我之前的混合編程經驗(C++,vs2018,matlab2019,見https://blog.csdn.net/wwwoowww/article/details/83013801),我在嘗試使用mex鏈接第三方庫不成功后,想在vs中生成dll文件,再改后綴為.mexw64 。開干
1. 新建Win32 控制臺應用程序
2. 配置項目的屬性
需要配置的地方有四點:
2.1.注意選擇平臺為x64(因為我自己編譯的opencv版本是64位的)
2.2.常規 -》 配置類型改為 .dll
2.3.VC++目錄-》 2.3.1包含目錄:添加opencv的include, 添加matlab安裝目錄下的/extern/include目錄
? ? ? ? ? ? ? ? ? ? ? ? 2.3.2 庫目錄:添加opencv的 x64\vc14\lib(這是我自己編譯的基于vs2015的opencv庫), 添加matlab安裝目錄下的\extern\lib\win64\microsoft目錄
2.4.連接器-》輸入-》 附件依賴項 中 添加:
? ? ? ? ? opencv_world346d.lib (我在opencv編譯的時候選擇了生成world庫,該庫將opencv所有的功能集合成一個文件。所以不用想一些博客中要包含n多個opencv_****.dll )
? ? ? ? ? ? ? ? ? ? ? ?libmat.lib ? (以下都是matlab的庫文件)
? ? ? ? ? ? ? ? ? ? ? ?libmx.lib
? ? ? ? ? ? ? ? ? ? ? ?libmex.lib
? ? ? ? ? ? ? ? ? ? ? ?libeng.lib
3 創建 ***.def 文件?
文件內容:
LIBRARY detect_mark.DLLEXPORTSmexFunction
?
4 創建源文件
功能:matlab傳入一個圖像矩陣,c++識別圖像是否有aruco mark,并返回mark的id,和中心位置
提醒下, opencv的aruco頭文件很可能找不到,因為該庫在opencv_contrib包中,需要自己在編譯opencv的時候加上這個額外的包
具體的詳情見:https://blog.csdn.net/ezhchai/article/details/80557936
#include "stdafx.h" #include "opencv2/opencv.hpp" #include "opencv2/aruco.hpp" #include <iostream> #include "mex.h" using namespace std; using namespace cv;void exit_with_help() {mexPrintf("Usage: [id, x, y] = detect_mark(img_data);\n"); }/**檢查圖像中的mark,并返回mark的id和中心點坐標 */ void detect_marks(cv::Mat& image, vector<Vec3d>¢er) {Ptr<aruco::DetectorParameters> detectorParams = aruco::DetectorParameters::create();Ptr<aruco::Dictionary> dictionary =aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(1));vector< int > ids;vector< vector< Point2f > > corners, rejected;vector< Vec3d > rvecs, tvecs;// detect markers and estimate pose/*markerCorners 是檢測出的圖像的角的列表。對于每個marker,將返回按照原始順序排列的四個角(從左上角順時針開始)。因此,第一個點是左上角的角,緊接著右上角、右下角和左下角。markerIds 是在markerCorners檢測出的所有maker的id列表.注意返回的markerCorners和markerIds 向量具有相同的大小。*/aruco::detectMarkers(image, dictionary, corners, ids, detectorParams, rejected);//獲取中心點坐標for (int i = 0; i < ids.size(); i++) {double x = (corners[i][0].x + corners[i][2].x) / 2;double y = (corners[i][0].y + corners[i][2].y) / 2;center.push_back(Vec3d(ids[i], x, y));//cout << "id: " << ids[i] << " ;center: " << x << "," << y << endl;}return ; }static void fake_answer(mxArray *plhs[]){plhs[0] = mxCreateDoubleMatrix(0, 0, mxREAL); }void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {if (nrhs == 1){//獲取傳入數據的指針,類型只能為double*double *data = mxGetPr(prhs[0]);//將傳入的圖像數據類型強轉為uint8_t來解釋,因為我們傳入的是圖像矩陣,圖像在matlab中是uint8類型uint8_t *i_data = (uint8_t*) data;int mrows, ncols;mrows = mxGetM(prhs[0]); //行數,,matlab按照uint8來計算的個數ncols = mxGetN(prhs[0]); //列數mexPrintf("m = %d, n = %d", mrows, ncols);/**創建Opencv格式下的圖像數據matlab和opencv的圖像格式不一樣。matlab是三個通道(RGB)分成了三張圖片的保存,每一張圖片的數據類型都是uint8。所以需要轉換。但是如果是一個通道的灰度圖,不需要轉換opencv是三個通道同時保存,只有一張圖片,每個圖片的值為(BGR)[uint8,uint8,uint8]matlab和opencv的圖像通道排列方式不一樣,MATLAB中RGB,OPENCV BGR*///獲取灰度圖,只采了matlab圖像中的R通道數據Mat img_gray = Mat(mrows, ncols / 3, CV_8UC1);for (int i = 0; i<mrows; i++) {for (int j = 0; j < ncols/3; j++) {*(img_gray.data + img_gray.step[0] * i + img_gray.step[1] * j) = (uchar)i_data[j*mrows + i];}}/**// 獲取彩色圖Mat img_col = Mat(mrows, ncols / 3, CV_8UC3);for (int i = 0; i<mrows; i++) {for (int j = 0; j < ncols; j++) {//改變通道,matlab和opencv的圖像通道排列方式不一樣,MATLAB中RGB,OPENCV BGR,寫的真難看,不忍直視。。。。int chan = j / (ncols / 3);int n;if (chan == 0)n = 2;else if (chan == 2)n = 0;else n = 1;*(img_col.data + img_col.step[0] * i + img_col.step[1] * (j % (ncols / 3)) + img_col.elemSize1()* n) = (uchar)i_data[j*mrows + i];}}*/vector<Vec3d> center;detect_marks(img_gray, center);if (center.size() == 0) {plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);double *resultMat = mxGetPr(plhs[0]);*resultMat = -1;return;}int rows = center.size();int cols = 3;//創建返回給matlab的數據plhs[0] = mxCreateDoubleMatrix(rows, cols, mxREAL);//獲取指針double *resultMat = mxGetPr(plhs[0]);//賦值for (int i = 0; i < rows; i++)for (int j = 0; j < cols; j++)*(resultMat + i + j * rows) = (double)center.at(i)[j];//imwrite("out_file_color.png", img_col);}else{exit_with_help();fake_answer(plhs);return;} }說明下Matlab的圖像存儲方式:
matlab文檔:https://ww2.mathworks.cn/help/matlab/matlab_external/matlab-data.html
原圖是500*312*3的彩色圖(不可否認,我媳婦兒很漂亮)
matlab讀入上圖并傳入到C/C++圖像的格式是1500*312*1的二維矩陣,排列方式如下,從左到右分別是R、G、B通道的值。
?
5. 生成.dll文件
最后使用 控制界面 生成-》生成解決方案
把生成的.dll 文件后綴改為 .mexw64?
使用matlab調用就可以了
?
附 我的結果:
傳入圖像:
總結
以上是生活随笔為你收集整理的Matlab与C/C++混合编程 (基于Opencv库)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Matlab C++混合编程 在Visu
- 下一篇: VS与Matlab混合编译 - mexw