C++调用Mtalab接口
实现步骤:
1、创建项目,配置环境,加载对应的库和头文件;
环境搭建可以参考文档:https://blog.csdn.net/m0_63694181/article/details/139148961
2、创建一个类,去初始化库文件;
3、调用matlab接口;
1、接口调用实现
1.1、算法接口
#ifndef Test_h
#define Test_h 1
#if defined(__cplusplus) && !defined(mclmcrrt_h) && defined(__linux__)
# pragma implementation "mclmcrrt.h"
#endif
#include "mclmcrrt.h"
#include "mclcppclass.h"
#ifdef __cplusplus
extern "C" { // sbcheck:ok:extern_c
#endif
/* This symbol is defined in shared libraries. Define it here
* (to nothing) in case this isn't a shared library.
*/
#ifndef LIB_test_C_API
#define LIB_test_C_API/* No special import/export declaration */
#endif
/* GENERAL LIBRARY FUNCTIONS -- START */
extern LIB_test_C_API
bool MW_CALL_CONV TestInitializeWithHandlers(
mclOutputHandlerFcn error_handler,
mclOutputHandlerFcn print_handler);
extern LIB_test_C_API
bool MW_CALL_CONV TestInitialize(void);
extern LIB_test_C_API
void MW_CALL_CONV TestTerminate(void);
extern LIB_test_C_API
void MW_CALL_CONV TestPrintStackTrace(void);
/* GENERAL LIBRARY FUNCTIONS -- END */
/* C INTERFACE -- MLX WRAPPERS FOR USER-DEFINED MATLAB FUNCTIONS -- START */
extern LIB_test_C_API
bool MW_CALL_CONV mlxTest_info_API(int nlhs, mxArray *plhs[], int nrhs, mxArray
*prhs[]);
/* C INTERFACE -- MLX WRAPPERS FOR USER-DEFINED MATLAB FUNCTIONS -- END */
#ifdef __cplusplus
}
#endif
/* C++ INTERFACE -- WRAPPERS FOR USER-DEFINED MATLAB FUNCTIONS -- START */
#ifdef __cplusplus
/* On Windows, use __declspec to control the exported API */
#if defined(_MSC_VER) || defined(__MINGW64__)
#ifdef EXPORTING_test
#define PUBLIC_test_CPP_API __declspec(dllexport)
#else
#define PUBLIC_test_CPP_API __declspec(dllimport)
#endif
#define LIB_test_CPP_API PUBLIC_test_CPP_API
#else
#if !defined(LIB_test_CPP_API )
#if defined(LIB_test_C_API)
#define LIB_test_CPP_API LIB_test_C_API
#else
#define LIB_test_CPP_API /* empty! */
#endif
#endif
#endif
extern LIB_test_CPP_API void MW_CALL_CONV test_info_API(int nargout, mwArray& signal, mwArray& width, mwArray& height, mwArray& dx, mwArray& dy, const mwArray& img, const mwArray& arr, const mwArray& num);
/* C++ INTERFACE -- WRAPPERS FOR USER-DEFINED MATLAB FUNCTIONS -- END */
#endif
#endif
1.2、接口初始化
创建一个类初始化算法接口
//load_calib.h
#ifndef LOADCALIB_H
#define LOADCALIB_H
#pragma once
#include "atlstr.h"
#include <mclcppclass.h>
typedef bool(__cdecl* faculaInitialize)(void);//__stdcall
typedef void(__cdecl* mlxCalc_test_info_API)(int nlhs, mxArray *plhs[], int nrhs, mxArray* prhs[]);
/// <summary>
//nlhs为整数类型,是用户执行name所包含的命令时期望输出的参数的个数,系统规定nlhs不超过50。
//plhs(*)为包含命令name输出参数内存地址的整数类型的数组,其中数组元素plhs(nlhs)包含了name第nlhs个输出参数的内存地址。
//nrhs为整数类型,是用户期望执行的命令name的输入元素的个数,nrhs不超过50。
//prhs(*)为包含命令name输入参数内存地址的整数类型的数组,其中数组元素prhs(nrhs)包含了name第nrhs个输入参数的内存地址。
class LoadCalib
{
public:
LoadCalib();
~LoadCalib();
private:
HMODULE m_hDll;
CString m_strLastErr; //存放错误信息
public:
bool Load(CString DLLFileName);//加载DLL,DLLFileName:DLL文件的全路径,ex.D:\SimpleMath.dll
CString GetLastError(); //返回报错信息
faculaInitialize _faculaInitialize;
mlxCalc_test_info_API _calc_test_info_API;
};
#endif // LOADDLL_H
#include "load_calib.h"
LoadCalib::LoadCalib()
{
_faculaInitialize = NULL;
_calc_test_info_API = NULL;
m_hDll = NULL;
m_strLastErr = "";
}
LoadCalib::~LoadCalib()
{
if (m_hDll) { FreeLibrary(m_hDll); m_hDll = NULL; }
}
bool LoadCalib::Load(CString DLLFileName)
{
if (m_hDll) { FreeLibrary(m_hDll); m_hDll = NULL; }
//加载动态库
m_hDll = LoadLibraryEx(DLLFileName, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
if (NULL == m_hDll)
{
m_strLastErr = "Load DLL Failed: " + DLLFileName;
return FALSE;
}
_faculaInitialize = (faculaInitialize)GetProcAddress(m_hDll, "faculaInitialize");
if (_faculaInitialize == NULL)
{
m_strLastErr = "Can not find[_faculaInitialize]";
goto EXIT;
}
//加载函数接口
_calc_test_info_API = (mlxCalc_test_info_API)GetProcAddress(m_hDll, "mlxCalc_test_info_API");
if (_calc_test_info_API == NULL)
{
m_strLastErr = "Can not find[_calc_test_info_API]";
goto EXIT;
}
return TRUE;
EXIT:
if (m_hDll) { FreeLibrary(m_hDll); m_hDll = NULL; }
return FALSE;
}
CString LoadCalib::GetLastError()
{
return m_strLastErr;
}
1.3、接口调用
核心代码:在于调用算法接口,如何给其接口传参,以及内存释放的问题
//hdl_calib.h
#pragma once
#include<iostream>
#include<fstream>
#include"load_calib.h"
#include"engine.h" //引擎例程的函数原型
#include"mat.h" //mat 例程的函数原型
#include"matrix.h" //矩阵访问例程的mxArray结构体和函数原型的定义
#include "opencv2\opencv.hpp"
using namespace std;
using namespace cv;
typedef struct ImgInfo_st
{
double numw; //光韵的宽和高
double numh;
double dx;
double dy; //显示光韵中心坐标,相对图像中心而言
int32_t signal; //判断函数是否调用成功,1-成功,0-失败
}ImgInfo;
class CHdlCalib
{
public:
//CHdlCalib(MemManager *memMng);
CHdlCalib();
~CHdlCalib();
public:
bool loadDllMethod(const std::string & dll_path); //初始化库文件
void readFile(); //从文件中读取图像数据
bool calibFocalInfo(uchar *data, uint32_t imgWidth, uint32_t imgHeight); //调用算法接
private:
std::string m_dll_path = (QCoreApplication::applicationDirPath() + "/test.dll").toUtf8().constData(); //库在项目中的路径
LoadCalib::Dll_load_Type curr_dlltype;
LoadCalib currCalibDll;
bool m_isLoadDll = false;
ImgInfo m_imgInfo;
};
//hdl_calib.cpp
CHdlCalib::CHdlCalib(){
loadDllMethod(m_dll_path);
}
CHdlCalib::~CHdlCalib(){}
bool CHdlCalib::loadDllMethod(const std::string & dll_path){
try{
if (currCalibDll.Load(dll_path.c_str())){
///初始化Matla
if (currCalibDll._faculaInitialize()){
//dblog_debug(CalibAlgoProcessor, "Matlab init succeed");
//printf("Matlab init succeed \n");
m_isLoadDll = true;
return true;
}
}
m_isLoadDll = false;
return false;
}
catch (...){
m_isLoadDll = false;
return false;
}
}
void CHdlCalib::readFile()
{
const char* fileName = "E:\\data.dat"; //数据类型为uchar,RGB888
// 打开二进制文件
std::ifstream inputFile(fileName, std::ios::binary);
if (!inputFile) {
std::cerr << "无法打开文件: " << fileName << std::endl;
return 1;
}
// 获取文件大小
inputFile.seekg(0, std::ios::end);
std::streamsize fileSize = inputFile.tellg();
inputFile.seekg(0, std::ios::beg);
// 分配足够的内存来存储数据
m_data = new unsigned char[fileSize];
inputFile.read(reinterpret_cast<char*>(m_data), fileSize);
calibFocalInfo(m_data, 1280, 720);//该数据类型为RGB888
}
bool CHdlCalib::calibFocalInfo(uchar *data, uint32_t imgWidth,uint32_t imgHeight)
{
try{
if (!m_isLoadDll) //判断库是否初始化成功
{
cout << "matlab init failed!" << endl;
return false;
}
//调用opencv,将数据从CV_8UC3转换为CV_8UC1,将rgb888三通道数据转换为一通道数据
cv::Mat imgCV_8UC3(imgHeight, imgWidth, CV_8UC3, data);
cv::Mat imgCV_8UC1;
cv::cvtColor(imgCV_8UC3, imgCV_8UC1, cv::COLOR_BGR2GRAY);
//1-0、传入第一个参数,图像数据,二维数组
mwSize dims[] = { static_cast<mwSize>(imgWidth), static_cast<mwSize>(imgHeight) }; //创建一个mwSize类型的数组,初始化数组大小
mxArray *mxData = mxCreateNumericArray(2, dims, mxUINT8_CLASS, mxREAL); //创建一个 mxArray 类型的多维数组对象,存储二维数据数据
// 获取mxData中存储uchar数据的指针,mxGetUint8s(mxData)
// 将数据从 sourceData 拷贝到 mxData
uint8_t* mxDataPtr = static_cast<uint8_t*>(mxGetUint8s(mxData));
//std::memcpy(mxDataPtr, (uchar*)n_mem->getAddr(), n_mem->m_size);
std::memcpy(mxDataPtr, imgCV_8UC1.data, imgWidth*imgHeight);
//将读取到的数据保存为png格式,验证读取到的数据和原数据效果是否一致;
// 使用OpenCV将数据转换为Mat对象,以便显示图像
/*cv::Mat img(imgCV_8UC1.rows, imgCV_8UC1.cols, CV_8UC1, mxDataPtr);
cv::imshow("1",image);
cv::imwrite("1.png", img);
cv::waitKey(0);
cv::destroyAllWindows(); */
// 将mxData数据写入文件保存下来,就用uint8_t格式写入,验证数据是否和原数据一致,保存后用nopade++,二进制格式打开
/*{
std::ofstream outFile("output_data.bin", std::ios::binary);
if (!outFile) {
std::cerr << "无法打开文件用于写入数据!" << std::endl;
return false;
}
// 计算要写入文件的总字节数,根据mxData的维度信息
size_t totalBytes = height * width;
// 将mxData中的数据逐个字节写入文件
for (size_t i = 0; i < totalBytes; ++i) {
outFile.put(mxDataPtr[i]);
}
outFile.close();
std::cout << "数据已成功写入文件 output_data.bin!" << std::endl;
}*/
//2-0、出入一个一维数组,数组元素有两个,元素类型为double
double ArrInfo[2];
ArrInfo[1] = 12.11;
ArrInfo[0] = 23.1212;
//2-1、创建内存空间
mxArray *mxArrNum= mxCreateNumericMatrix(1, 2, mxDOUBLE_CLASS, mxREAL);
//2-2、创建内存空间 将C++数据复制到MATLAB变量中
std::memcpy(mxGetDoubles(mxArrNum), ArrInfo, 2 * sizeof(double));
获取mxArrNum中存储的double类型数据的指针
//uint32_t* mxDataPtr2 = mxGetUint32s(mxArrNum);
输出mxArrNum中的数据
//std::cout << "从mxArrNum中获取的数据:" << std::endl;
//std::cout << "第一个值:" << mxDataPtr2[0] << std::endl;
//std::cout << "第二个值:" << mxDataPtr2[1] << std::endl;
//3-0、传入一个double类型的数据值,mm
double val=12.1123;
mxArray *mxVal = mxCreateDoubleScalar(val);
// 打印输出mxVal 的值
//double value = mxGetScalar(mxVal);
//std::cout << "pixelTomrad : " << value << std::endl;
//输入值
mxArray *mw_MemSet[3]= { mxData,mxArrNum,mxVal};
//接口输出值,第一个为int类型,其他为double类型,没有找到float处理方法,所以一致采用double
mxArray* mw_MemRes[5];
mwSize dims0[1]= {1}; //创建int类型的一维数组,元素个数为1
mxArray *mxNumInt0 = mxCreateNumericArray(1, dims0, mxINT32_CLASS, mxREAL); //创建指定值的n维逻辑
mwSize dims1[1] = {1}; //创建double类型的一维数组,元素个数为1
mxArray *mxNumInt1 = mxCreateNumericArray(1, dims1, mxDOUBLE_CLASS, mxREAL);
mwSize dims2[1] = {1};
mxArray *mxNumInt2 = mxCreateNumericArray(1, dims2, mxDOUBLE_CLASS, mxREAL);
mwSize dims3[1] = {1};
mxArray *mxNumInt3 = mxCreateNumericArray(1, dims3, mxDOUBLE_CLASS, mxREAL);
mwSize dims4[1] = {1};
mxArray *mxNumInt4= mxCreateNumericArray(1, dims4, mxDOUBLE_CLASS, mxREAL);
//调用接口,mw_MemRes为接收返回参数,mw_MemSet为传入参数
currCalibDll._calc_test_info_API(5, mw_MemRes, 3, mw_MemSet);
m_imgInfo.signal = static_cast<int32_t>(mxGetScalar(mw_MemRes[0]));
for (int i = 1; i < 5; i++) {
if (mw_MemRes[i] != nullptr && mxGetNumberOfElements(mw_MemRes[i]) == 1) {
switch (i)
{
case 1:
m_imgInfo.numw = mxGetScalar(mw_MemRes[i]);
break;
case 2:
m_imgInfo.numh = mxGetScalar(mw_MemRes[i]);
break;
case 3:
m_imgInfo.dx = mxGetScalar(mw_MemRes[i]);
break;
case 4:
m_imgInfo.dy = mxGetScalar(mw_MemRes[i]);
break;
default:
break;
}
}
}
//释放发的数据内存
mxDestroyArray(mxData);
mxDestroyArray(mxArrNum);
mxDestroyArray(mxVal);
// 释放内存,因为我们创建了mxArray,需要释放它们以避免内存泄漏
for (int i = 0; i < 5; i++) {
mxDestroyArray(mw_MemRes[i]);
mw_MemRes[i] = nullptr;
}
return true;
}
catch (...){}
}