目录
- 4.1.1 算法开发:算法模块的开发流程
- 4.1.2 参数操作:获取与设置模块参数的方法
- 4.1.3 文件交互:文件交互操作的配置方法
- 4.1.4 输出显示:设置输出并显示在VM界面的方法
- 4.1.5 模板配置:模板配置界面的实现方法
- 4.1.6 命名翻译:自定义模块在VM界面显示中文的方法
- 4.1.8 多图像源:算法模块输入多幅图像的方法
- 4.1.9 断点调试:附加断点调试的注意事项
- 4.1.10 点集参数:点集输入、输出实现方法
- 4.1.11 直线参数:获取输入直线的方法
- 4.1.12 矩形参数:输出和显示矩形检测框的方法
- 4.1.13 多ROI:获取多个ROI的方法
- 4.1.14 日志打印:日志打印的方法
- 4.1.15 参数自执行:参数自执行的方法
4.1.1 算法开发:算法模块的开发流程
描述
环境:VM4.0及以上 + VS2013
现象:图像类算子模块的开发流程。
解答
第一步,打开算子生成器,配置算法模块名称、自定义输入输出参数,单击下一步。
第二步,配置算法模块基本参数和运行参数,并依次生成XML、C++工程、C#工程。
第三步,打开生成的C#工程,选择Release,编译生成dll并拷贝到XML文件夹。
第四步,打开生成的C++工程,选择Release、x64,禁用优化。
第五步,在C++工程的AlgorithmModule.cpp中的Process()函数中实现自定义算法。
第六步,编译C++工程,生成的dll拷贝到XML文件夹。
第七步,将XML文件夹拷贝到VisionMaster4.0.0\Applications\Module(sp)\x64\XX (工具箱名,例如Measurement),即完成图像类算子模块的开发与嵌入。
问题根因
不熟悉图像类算子模块的开发流程。
4.1.2 参数操作:获取与设置模块参数的方法
描述
环境:VM4.0及以上 + VS2013
现象:算法模块开发中如何获取和设置参数?
解答
在大多数的VM模块中,模块参数可以分为基本参数(基本参数包含输入输出参数,输入参数一般通过订阅的方式获取,输出参数不在VM参数配置界面上,而是在VM右边用来显示结果)和运行参数(算法参数),在算子模块开发中这两种参数的获取和设置的方式是不同的。
参数类型主要包括整型(int)、浮点型(float)、字符串型(string)、二进制型(byte)、图像类型(image)、点集型(pointset)六种。
基本参数通过VM_M_Getxxxx()函数来获取,如,通过VM_M_Setxxxx()来输出相应的结果,其用法如下。当需要输入输出浮点数组时,可以通过遍历函数来输入输出,也可以调用VmModule_GetInputVectorIndex_32f()或者VmModule_OutputVector_32f()函数一次性输入输出。
C++
//运行函数
int CAlgorithmModule::Process(IN void* hInput, IN void* hOutput, IN MVDSDK_BASE_MODU_INPUT* modu_input)
{
HKA_F32 fValue = 0;
int nArrayCount = 0;
int nRet = IMVS_EC_UNKNOWN;
//从模块界面中拿到名为ImagePointA的数组中索引为0的浮点值
nRet = VM_M_GetFloat(hInput, "ImagePointA", 0, &fValue, &nArrayCount);
//将模块输出中名为ModuStatus的整型数组中索引为0的值设置为1
VM_M_SetInt(hOutput, "ModuStatus", 0, 1);
}
针对运行参数(算法参数),可在GetParam( )或SetParam( )函数里加上相应的判断,从而在每一次运行时自动获取或设置算法参数;也可在函数里调用函数里调用GetParam( )或SetParam( )来获取或设置算法参数,如下所示:
C++
//运行函数
int CAlgorithmModule::Process(IN void* hInput, IN void* hOutput, IN MVDSDK_BASE_MODU_INPUT* modu_input)
{
char pBuff[1024] = {1024};
int pDataLen=1024;
//获取模块界面中名为threshold的运行参数的值
GetParam("threshold", pBuff, sizeof(pBuff), &pDataLen);
//获取到的值做转换
int thresh = atoi(pBuff);
//将模块中名为threshold的运行参数设置为240
SetParam("threshold", "240", pDataLen);
}
注意:基本参数和运行参数的参数名可以在模块对应的xxxxAlgorithmTab.xml文件中找到,即参数配置界面xml,文件路径如下。另外,输入输出参数需要在xxxxModu.xml文件中定义。
问题根因
算子模块开发中模块参数的相关接口
4.1.3 文件交互:文件交互操作的配置方法
描述
环境:VM4.0及以上 + VS2013
现象:自定义算子模块开发中如何实现文件交互?
解答
- 打开模块对应的***AlgorithmTab.xml配置文件,在Name是Tab_Basic Params的Tab中添加如下方格式的xml信息;
<OpenFileForCalibDialog Name="CalibPathName" NameSpace="Standard">
<Description>Load Calib File Path</Description>
<DisplayName>RunParam_Load Calibration File</DisplayName>
<Visibility>Beginner</Visibility>
<AccessMode>RW</AccessMode>
<CurValue></CurValue>
<DefaultValue></DefaultValue>
<FileOption>
<IsMultiselect>false</IsMultiselect>
<FilterName>.xml|*.xml;*.iwcal</FilterName>
</FileOption>
</OpenFileForCalibDialog>
界面示意如下:
- C++工程中添加底层逻辑代码,主要通过SetParam和GetParam两个函数去设置和获取文件路径代码如下。
C++
//SetParam函数中加入以下代码来获取文件路径并设置到底层,选择路径时触发读取文件函数
else if (0 == strcmp("CalibPathName", szParamName))
{
// m_chSaveCalibPathName为char型指针
if (m_chSaveCalibPathName != NULL)
{
free(m_chSaveCalibPathName);
m_chSaveCalibPathName = NULL;
}
//获取文件路径
m_chSaveCalibPathName = (char*)malloc(MAX_FILE_PATH);
memcpy_s(m_chSaveCalibPathName, MAX_FILE_PATH, pData, strlen(pData));
//读取文件操作
nErrCode = LoadFile(m_chSaveCalibPathName);
}
//GetParam函数中加入以下代码来获取底层文件路径信息
else if (0 == strcmp("CalibPathName", szParamName))
{
sprintf_s(pBuff, nBuffSize, "%s", m_chSaveCalibPathName);
}
- 完成以上两步就可以实现界面选择文件并加载文件操作,要实现每次执行时能同步加载最新的文件还需添加刷新标志位并在process函数中添加触发代码。
刷新标志位控件添加方法:打开模块对应的***AlgorithmTab.xml配置文件,在Name是Tab_Basic Params的Tab中添加如下方格式的xml信息(在添加文件操作控件下面添加);
<Boolean Name="RefreshFileEnable" NameSpace="Standard">
<CurValue>False</CurValue>
<DefaultValue>False</DefaultValue>
<Description>RefreshFileEnable</Description>
<DisplayName>RefreshFileEnable</DisplayName>
<Visibility>Beginner</Visibility>
<AccessMode>RW</AccessMode>
</Boolean>
界面示意如下:
C++工程中添加底层逻辑代码,主要通过SetParam和GetParam两个函数去设置和获取文件路径代码如下。
C++
//SetParam函数中加入以下代码来将更新文件使能设置到底层,更改状态时触发
else if (0 == strcmp("RefreshFileEnable", szParamName))
{
if (0 == strncmp(pData, "True", strlen("True")))
{
m_bRefreshFileEnable = true;
}
else
{
m_bRefreshFileEnable = false;
}
}
//GetParam函数中加入以下代码来获取底层使能状态反馈给界面
else if (0 == strcmp("RefreshFileEnable", szParamName))
{
if (m_bRefreshFileEnable)
{
sprintf_s(pBuff, nBuffSize, "%s", "True");
}
else
{
sprintf_s(pBuff, nBuffSize, "%s", "False");
}
}
并在process函数中添加触发代码:
C++
if (m_bRefreshFileEnable)
{
m_calibPathName = m_chSaveCalibPathName;
SetParam("CalibPathName", m_calibPathName.c_str(), m_calibPathName.length());
}
问题根因
不熟悉算子模块开发文件交互方法。
4.1.4 输出显示:设置输出并显示在VM界面的方法
描述
环境:VM4.0及以上 + VS2013
现象:算法模块开发中,如何设置输出并显示在VM界面上
解答
1、在xxxxModu.xml中配置要输出的参数名称和类型,这些其实一一对应着VM中模块运行之后的数据结果。以直线查找模块为例,如下图所示,当在此xml中将某一个参数的属性Visible=“false”或者注释掉这个参数,这个参数将不在界面上显示。这个xml最后一个是输出基准点和运行点相关信息。
2、在xxxxModu.xml文件中配置输出参数之后,还要在c++工程代码里添加代码,主要在Process函数里用VM_M_Setxxxx()函数来设置相应输出参数的值。
//c++设置模块状态
VM_M_SetInt(hOutput, "ModuStatus", 0, 1);
3、拿到模块的结果数值之后,就可以进行任意组合将结果显示在图像上,通过配置xxxxDisplay.xml文件来进行渲染操作。以直线查找模块为例,每一项里面都有Mapping映射到输出值,从而显示相关内容。下图中各项显示内容分别为:“InputImage”显示图像结果;“Line Result”显示直线结果,“Through Line”也是显示直线结果,但它是贯穿线;“Contour Point”显示每个边缘点;“CaliperBox”显示卡尺框,“DetectCaliperBox”显示卡尺框检测区域,其实观察输出数值,也可以发现这两者的显示是一样的;“ROI”显示检测区域;“Fixtured Point”显示基准点;“Unfixtured Point”显示运行点;“Data Record”显示历史结果;“Result List”显示当前结果;“ExternROI”观察里面的映射值,其实是显示屏蔽区。
此处参考其它模块,在图像区域显示文本,在xxxxDisplay.xml中文件中添加“Show Text”及其中的内容。重新拖拉直线模块,不然不会生效,拖拉直线模块并执行,此时图像显示就会显示文本信息
4、提示:在进行算法模块开发时,关于界面所显示的内容,可以参考已有的模块进行相应的xml配置。
问题根因
不熟悉如何设置输出并显示在VM界面上
4.1.5 模板配置:模板配置界面的实现方法
描述
环境:VM4.0及以上 + VS2013
现象:自定义模块如何实现模板配置界面。
解答
开发模板匹配等模块时,需要进行模板界面的配置,并实现界面与底层数据交互。
第一步:在算法模块生成器中配置建模的配置参数,如速度尺度、特征尺度等参数。
第二步:定义数据源类。
第三步:配置AlgorithmTab.xml。
第四步:新建参数配置界面模板控件。
第五步:将新建的控件应用于DataTemplate。
第六步:新建模板配置弹出窗口。
第七步:建模
界面与算法底层数据交互实现。
数据交互代码示例:
1)界面数据传给底层
(paramsConfig as IUserStringData)["SetImageWidth"] = model.ImageWidth.ToString(); //int
(paramsConfig as IUserStringData)["TextBoxMsg"] = textMsg; //string
(paramsConfig as IUserBytesData)["SetImageData"] = model.ImageBuffer; //byte[]
2)底层数据传给界面
int ImageWidth = int.Parse((paramsConfig as IUserStringData)["GetImageWidth"]); //int
byte[] ImageBuffer = (paramsConfig as IUserBytesData)["GetImageData"]; //byte[]
byte[] roiBuffer = (paramsConfig as IUserBytesData)["GetRoiData"]; //byte[]
3)底层获取界面数据并处理
在AlgorithmModule.cpp的GetParam函数中获取界面参数。
在AlgorithmModule.cpp的SetParam函数中进行建模及参数处理。
按照上述步骤开发完成后,运行模块效果如下图所示。
问题根因
不熟悉自定义模块界面配置开发的方法。
4.1.6 命名翻译:自定义模块在VM界面显示中文的方法
描述
环境:VM4.0及以上 + VS2013
现象:如何给自定义模块添加中文名称。
解答
第一步,使用VisionMaster4.X.0\Applications\Lang中LanguageTool工具,给算法模块增加中英文资源。具体步骤:【通过Key查找资源】中输入模块名称,然后下方分别添加模块中文值(中文名称)和英文值(英文名称),最后点击“增加/编辑”按扭。
第二步,重新打开VM,模块中文名添加成功。
问题根因
不熟悉给算法模块添加中英文资源的方法。
4.1.7 无图像源:算法模块无输入图像的方法
描述
环境:VM4.0及以上 + VS2013
现象:算法模块通常是输入一张图片,进行相应的处理。当算法模块不需要图像时,如何进行修改?
解答
1)修改xml,使得界面无输入图像项
注释图像输入部分,模块的参数配置窗口就没有图像输入部分。
2)修改C++代码,重载Process函数。
C++
int CAlgorithmModule::Process(IN void* hInput, IN void* hOutput)//模块无输入图片时
{
int nRet = IMVS_EC_OK;
//......
//......
//......
return IMVS_EC_OK;
}
问题根因
不熟悉如何修改自定义算法模块为无图像输入。
4.1.8 多图像源:算法模块输入多幅图像的方法
描述
环境:VM4.0及以上 + VS2013
现象:自定义算法模块如何添加多幅图像输入,从而扩展到双目视觉算法领域?
解答
根据算法模块生成器生成的模块默认只支持单幅图像输入,如果需要添加多幅图像输入需要按照下述步骤对模块xml文件和C++算法代码进行修改和补充。
1)界面区别
单目视图算法模块基本参数界面:
双目视图算法模块基本参数界面:
2)界面XML修改
在模块名.xml的图像输入部分,增加图像输入源2,名称可自定义(图像名、宽、高、像素格式均需要重命名)。
接着打开模块名AlgorithmTab.xml,将图像输入Category段改为如下所示(注意:OperationParams名必须与模块名.xml中Filter Name一致):
<Category Name="图像输入">
<Items>
<EnumerationG Name="ImageSource">
<Description>Input Source 1</Description>
<DisplayName>RunParam_Input Source 1</DisplayName>
<Visibility>Beginner</Visibility>
<AccessMode>O</AccessMode>
<Triggers>
<Trigger>
<Property>CurValue</Property>
<Setters>
<Setter>
<OperationName>SetCombinationSourceOperation</OperationName>
<OperationParams>InputImage</OperationParams>
</Setter>
</Setters>
</Trigger>
</Triggers>
<Initers>
<Setter>
<TargetName>EnumEntrys</TargetName>
<OperationName>GetFrontParamItemsOperation</OperationName>
<OperationParams>IMAGE</OperationParams>
</Setter>
<Setter>
<TargetName>CurValue</TargetName>
<OperationName>GetSelectedCombinationOperation</OperationName>
<OperationParams>InputImage</OperationParams>
</Setter>
</Initers>
</EnumerationG>
<EnumerationG Name="ImageSource2">
<Description>Input Source 2</Description>
<DisplayName>RunParam_Input Source 2</DisplayName>
<Visibility>Beginner</Visibility>
<AccessMode>O</AccessMode>
<Triggers>
<Trigger>
<Property>CurValue</Property>
<Setters>
<Setter>
<OperationName>SetCombinationSourceOperation</OperationName>
<OperationParams>InputImage2</OperationParams>
</Setter>
</Setters>
</Trigger>
</Triggers>
<Initers>
<Setter>
<TargetName>EnumEntrys</TargetName>
<OperationName>GetFrontParamItemsOperation</OperationName>
<OperationParams>IMAGE</OperationParams>
</Setter>
<Setter>
<TargetName>CurValue</TargetName>
<OperationName>GetSelectedCombinationOperation</OperationName>
<OperationParams>InputImage2</OperationParams>
</Setter>
</Initers>
</EnumerationG>
</Items>
</Category>
3)底层算法代码修改
由于输入为两幅图像,而标准Demo中GenerateImage函数是获取单幅图像,因此需要重写取图方法,示例代码如下:
/// <summary>
/// 获取两幅输入图像(HKA_IMAGE)
/// </summary>
/// <param name="hInput">输入体指针</param>
/// <returns></returns>
int CAlgorithmModule::GetInputImage(IN void* hInput, bool &bCamera1, bool &bCamera2, HKA_IMAGE struInputImg[2])
{
HKA_S32 nRet = IMVS_EC_UNKNOWN;
HKA_U32 nImageStatus = 0;
//HKA_IMAGE struInputImg[2];
do
{
nRet = VmModule_GetInputImageByName(hInput,
"InImage",
"InImageWidth",
"InImageHeight",
"InImagePixelFormat",
&struInputImg[0],
&nImageStatus);
HKA_CHECK_BREAK(IMVS_EC_OK != nRet);
bCamera1 = true;
} while (0);
do
{
nRet = VmModule_GetInputImageByName(hInput,
"InImage2",
"InImage2Width",
"InImage2Height",
"InImage2PixelFormat",
&struInputImg[1],
&nImageStatus);
HKA_CHECK_BREAK(IMVS_EC_OK != nRet);
bCamera2 = true;
} while (0);
return nRet;
}
最后,在Process函数中调用该方法,即可获取两张输入图像,示例代码如下:
//获取输入的两张图片
bool bCamera1 = false;
bool bCamera2 = false;
HKA_IMAGE struInputImg[2];
int nRet = GetInputImage(hInput, bCamera1, bCamera2, struInputImg);
if (!bCamera1 && !bCamera2)
{
//两个图片都没有,返回错误
return IMVS_EC_MODULE_SUB_RST_NOT_FOUND;
}
if (IMVS_EC_OK != nRet)
{
return IMVS_EC_MODULE_INPUT_NOT_FOUND;
}
如果基类中没有VmModule_GetInputImageByName方法,则将下面这段代码复制粘贴到VmModule_IO.cpp中。
HKA_S32 VmModule_GetInputImageByName(IN const void * const hInput,
char *strImage,
char *strWidth,
char *strHeight,
char *strFormat,
HKA_IMAGE *image,
HKA_U32 *imageStatus)
{
HKA_S32 nRet = IMVS_EC_UNKNOWN;
HKA_S32 format = 0;
HKA_IMAGE_FORMAT formatAlg = HKA_IMG_MONO_08;
HKA_U32 nStatusImage = IMVS_MODU_ENUM_STATUS_ERROR;
HKA_U32 nStatusWidth = IMVS_MODU_ENUM_STATUS_ERROR;
HKA_U32 nStatusHeight = IMVS_MODU_ENUM_STATUS_ERROR;
HKA_U32 nStatusFormat = IMVS_MODU_ENUM_STATUS_ERROR;
char* addr = 0;
HKA_CHECK_ERROR(HKA_NULL == hInput, IMVS_EC_PARAM);
HKA_CHECK_ERROR(HKA_NULL == image, IMVS_EC_PARAM);
HKA_CHECK_ERROR(HKA_NULL == imageStatus, IMVS_EC_PARAM);
nRet = VmModule_GetInputImageAddress(hInput, strImage, &addr, &nStatusImage);
HKA_CHECK_ERROR(IMVS_EC_OK != nRet, nRet);
nRet = VmModule_GetInputScalar_32i(hInput, strWidth, &(image->width), &nStatusWidth);
HKA_CHECK_ERROR(IMVS_EC_OK != nRet, nRet);
nRet = VmModule_GetInputScalar_32i(hInput, strHeight, &(image->height), &nStatusHeight);
HKA_CHECK_ERROR(IMVS_EC_OK != nRet, nRet);
nRet = VmModule_GetInputScalar_32i(hInput, strFormat, &format, &nStatusFormat);
HKA_CHECK_ERROR(IMVS_EC_OK != nRet, nRet);
if(image->height <= 0)
{
nStatusHeight = IMVS_MODU_ENUM_STATUS_INPUT_INVALID;
}
if(image->width <= 0)
{
nStatusWidth = IMVS_MODU_ENUM_STATUS_INPUT_INVALID;
}
*imageStatus = (IMVS_MODU_ENUM_STATUS_OK == nStatusImage)
&& (IMVS_MODU_ENUM_STATUS_OK == nStatusWidth)
&& (IMVS_MODU_ENUM_STATUS_OK == nStatusHeight)
&& (IMVS_MODU_ENUM_STATUS_OK == nStatusFormat);
nRet = VmModule_iMVSFormatToAlgFormat(format, &formatAlg);
HKA_CHECK_ERROR(HKA_TRUE != nRet, nRet);
image->format = formatAlg;
image->data[0] = addr;
image->step[0] = (HKA_IMG_RGB_RGB24_C3 == formatAlg) ? (3 * image->width) : image->width;
return IMVS_EC_OK;
}
问题根因
不熟悉自定义算法模块添加多幅图像输入的方法。
4.1.9 断点调试:附加断点调试的注意事项
描述
环境:VM4.0及VM4.2 + VS2013
现象:自定义算法模块附加断点调试的注意事项有哪些?
解答
自定义算法模块附加断点调试的注意事项如下:
1.勾选“显示所有用户的进程”,进程选择“VmModuleProxy.exe”
2.附加代码类型选择“本机”
问题根因
不熟悉自定义算法模块附加断点调试的注意事项。
4.1.10 点集参数:点集输入、输出实现方法
描述
环境:VM4.0及以上 + VS2013
现象:自定义算法模块如何获取输入点集、输出点集,从而扩展到几何基元拟合等算法领域?
解答
1.在算法模块生成器的自定义输入输出中配置输入点集、输出点集参数。
编译界面工程、算法工程,并将文件拷贝到安装目录工具箱后,效果如下图所示。
输入点集、输出点集的参数名称从模块.xml中查看,子元素类型均为float。
2.在算法工程中获取输入点集的数据。
首先在AlgorithmModule.h文件中定义输入、输出点集数组变量,示例代码如下:
#define MVBCALIBTRANS_MAX_POINT_NUM (100000)
HKA_F32 m_fImagePointX[MVBCALIBTRANS_MAX_POINT_NUM]; // 像素坐标X
HKA_F32 m_fImagePointY[MVBCALIBTRANS_MAX_POINT_NUM]; // 像素坐标Y
HKA_F32 m_fWorldPointX[MVBCALIBTRANS_MAX_POINT_NUM]; // 物理坐标X
HKA_F32 m_fWorldPointY[MVBCALIBTRANS_MAX_POINT_NUM]; // 物理坐标Y
然后在AlgorithmModule.cpp文件的Process()函数中获取输入的点集数据,示例代码如下:
int nRet = IMVS_EC_UNKNOWN;
HKA_CHECK_ERROR((IMVS_NULL == hInput || IMVS_NULL == hOutput), IMVS_EC_PARAM);
HKA_F32 fValue = 0;
int nCount = 0;
int nArrayCount = 0; // 数组大小
int pointXCount = 0; // X坐标数组大小
int pointYCount = 0; // Y坐标数组大小
// 获取输入点集
nRet = VM_M_GetFloat(hInput, "ImagePointPointX", 0, &fValue, &nArrayCount);
if (IMVS_EC_OK == nRet && nArrayCount > 0)
{
for (int i = 0; i<nArrayCount; ++i)
{
nRet = VM_M_GetFloat(hInput, "ImagePointPointX", i, &fValue, &nArrayCount);
if (IMVS_EC_OK != nRet)
{
break;
}
m_fImagePointX[i] = fValue;
}
pointXCount = nArrayCount;
}
HKA_MODU_CHECK_ERROR(IMVS_EC_OK != nRet, nRet);
nRet = VM_M_GetFloat(hInput, "ImagePointPointY", 0, &fValue, &nArrayCount);
if (IMVS_EC_OK == nRet && nArrayCount > 0)
{
for (int i = 0; i<nArrayCount; ++i)
{
nRet = VM_M_GetFloat(hInput, "ImagePointPointY", i, &fValue, &nArrayCount);
if (IMVS_EC_OK != nRet)
{
break;
}
m_fImagePointY[i] = fValue;
}
pointYCount = nArrayCount;
}
HKA_MODU_CHECK_ERROR(IMVS_EC_OK != nRet, nRet);
HKA_MODU_CHECK_ERROR((pointXCount != pointYCount), IMVS_EC_PARAM);
3.在算法工程中对输入点集进行处理,给输出点集数组赋值。示例代码如下:
// 输出物理点
for (int i = 0; i < pointXCount; ++i)
{
m_fWorldPointX[i] = m_fImagePointX[i] + 400;
m_fWorldPointY[i] = m_fImagePointY[i] + 500;
}
4.在算法工程中设置点集输出。示例代码如下:
if (IMVS_EC_OK == nRet)
{
VM_M_SetInt(hOutput, "ModuStatus", 0, 1); // 模块状态
int nProcessStatus = 1; // 1表示模块正常,0表示模块异常
nRet = VmModule_OutputVector_32f(hOutput, nProcessStatus, m_fWorldPointX, "WorldPointPointX", nArrayCount);
HKA_MODU_CHECK_ERROR(IMVS_EC_OK != nRet, nRet);
nRet = VmModule_OutputVector_32f(hOutput, nProcessStatus, m_fWorldPointY, "WorldPointPointY", nArrayCount);
HKA_MODU_CHECK_ERROR(IMVS_EC_OK != nRet, nRet);
}
else
{
VM_M_SetInt(hOutput, "ModuStatus", 0, 0);
}
模块运行效果如下图所示。
问题根因
不熟悉自定义算法模块点集输入与输出的方法。
4.1.11 直线参数:获取输入直线的方法
描述
环境:VM4.2及以上 + VS2013
现象:自定义算法模块如何获取输入直线,从而扩展到直线边缘缺陷检测等算法领域?
解答
1.在算法模块生成器的自定义输入输出中配置输入直线参数。
编译界面工程、算法工程,并将文件拷贝到工具箱目录后,模块界面如下图所示。
输入直线的参数名称从模块.xml中查看,子元素类型均为float。
2.在算法工程中获取输入直线的数据。
首先在AlgorithmModule.h文件中定义输入直线的起点、终点变量,示例代码如下:
float lineStartPX;
float lineStartPY;
float lineEndPX;
float lineEndPY;
然后在AlgorithmModule.cpp文件的Process()函数中获取输入直线的数据,示例代码如下:
int nRet = IMVS_EC_UNKNOWN;
HKA_CHECK_ERROR((IMVS_NULL == hInput || IMVS_NULL == hOutput), IMVS_EC_PARAM);
HKA_F32 fValue = 0;
int nArrayCount = 0;
nRet = VM_M_GetFloat(hInput, "lineStartPX", 0, &fValue, &nArrayCount);
if (IMVS_EC_OK == nRet && nArrayCount > 0)
{
lineStartPX = fValue;
}
HKA_MODU_CHECK_ERROR(IMVS_EC_OK != nRet, nRet);
VM_M_GetFloat(hInput, "lineStartPY", 0, &fValue, &nArrayCount);
if (IMVS_EC_OK == nRet && nArrayCount > 0)
{
lineStartPY = fValue;
}
HKA_MODU_CHECK_ERROR(IMVS_EC_OK != nRet, nRet);
VM_M_GetFloat(hInput, "lineEndPX", 0, &fValue, &nArrayCount);
if (IMVS_EC_OK == nRet && nArrayCount > 0)
{
lineEndPX = fValue;
}
HKA_MODU_CHECK_ERROR(IMVS_EC_OK != nRet, nRet);
VM_M_GetFloat(hInput, "lineEndPY", 0, &fValue, &nArrayCount);
if (IMVS_EC_OK == nRet && nArrayCount > 0)
{
lineEndPY = fValue;
}
HKA_MODU_CHECK_ERROR(IMVS_EC_OK != nRet, nRet);
问题根因
不熟悉自定义算法模块获取输入直线的方法。
4.1.12 矩形参数:输出和显示矩形检测框的方法
描述
环境:VM4.2及以上 + VS2013
现象:自定义算法模块如何输出和显示矩形检测框,从而扩展到深度学习目标检测等算法领域?
解答
1.在算法模块生成器的自定义输入输出中配置矩形框输出参数。
编译界面工程、算法工程,并将文件拷贝到工具箱目录后,模块界面如下图所示。
输出矩形框的参数名称从模块.xml中查看,子元素类型均为float。
2.在算法工程中输出检测框的数据。开发者可以输出单个矩形框,也可以输出多个。
在AlgorithmModule.cpp文件的Process()函数中输出矩形框数据,示例代码如下:
//输出矩形框1
VM_M_SetFloat(hOutput, "RectCenterX", 0, 50);
VM_M_SetFloat(hOutput, "RectCenterY", 0, 50);
VM_M_SetFloat(hOutput, "RectWidth", 0, 50);
VM_M_SetFloat(hOutput, "RectHeight", 0, 50);
VM_M_SetFloat(hOutput, "RectAngle", 0, 10);
//输出矩形框2
VM_M_SetFloat(hOutput, "RectCenterX", 1, 150);
VM_M_SetFloat(hOutput, "RectCenterY", 1, 150);
VM_M_SetFloat(hOutput, "RectWidth", 1, 150);
VM_M_SetFloat(hOutput, "RectHeight", 1, 150);
VM_M_SetFloat(hOutput, "RectAngle", 1, 20);
模块运行结果如下图所示。
3.进一步地,如果希望在图像图层上显示输出的矩形检测框图形,则需要让模块输出图像(图形显示依赖图像),具体方法和示例代码参见FAQ专题篇1.4章节,或者VM4.0-第4章-4.1.4。获取输入图像并输出图像后,模块运行结果如下图所示。
4.打开模块Display.xml,添加矩形框输出,示例如下图所示,每一项里面都有Mapping映射到输出值。
模块运行效果如下图所示。可以看到,输出图像图层上已叠加检测框图层。
问题根因
不熟悉自定义算法模块输出和显示矩形检测框的方法。
4.1.13 多ROI:获取多个ROI的方法
描述
环境:VM4.0.0及以上 + VS2013
现象:自研算法模块如何获取多个ROI,从而封装类似BLOB、边缘交点、矩形查找等模块?
解答
1.在模块名AlgorithmTab.xml中找到ROISelecter节点,在ROISelection属性中添加DoubleBox(双矩形框)。
添加ROI类型后,从工具箱中拖拽相应模块到流程编辑区,效果如下图所示。
2.在算法工程的AlgorithmModule.cpp文件的Process()函数中获取界面绘制的多ROI。
C++
if (modu_input->vtFixRoiShapeObj.size() == 2)
{
IMvdRectangleF *rectangleRoi = dynamic_cast<IMvdRectangleF*>(modu_input->vtFixRoiShapeObj[0]);
width = rectangleRoi->GetWidth();
height = rectangleRoi->GetHeight();
centerX = rectangleRoi->GetCenterX();
centerY = rectangleRoi->GetCenterY();
IMvdRectangleF *rectangleRoi2 = dynamic_cast<IMvdRectangleF*>(modu_input->vtFixRoiShapeObj[1]);
width2 = rectangleRoi2->GetWidth();
height2 = rectangleRoi2->GetHeight();
centerX2 = rectangleRoi2->GetCenterX();
centerY2 = rectangleRoi2->GetCenterY();
}
3.在算法工程的VmAlgModuBase.cpp文件的ResetDefaultRoi()函数中,屏蔽原有所有代码,改成下述代码:
C++
int CVmAlgModuleBase::ResetDefaultRoi(OUT BASE_MODU_ROI_DATA* stBaseModuROIData)
{
int nRet = IMVS_EC_OK;
stBaseModuROIData->stRoiBox.clear();
IMVS_ROI_BOX roiBox = { 0.0f };
// 第一个0度Box
roiBox.fRoiCenterX = 0.375f;
roiBox.fRoiCenterY = 0.375f;
roiBox.fRoiHeight = 0.5f;
roiBox.fRoiWidth = 0.5f;
roiBox.fRoiAngle = 0.0f;
stBaseModuROIData->stRoiBox.push_back(roiBox);
// 第二个90度Box
roiBox.fRoiCenterX = 0.7f;
roiBox.fRoiCenterY = 0.7f;
roiBox.fRoiHeight = 0.5f;
roiBox.fRoiWidth = 0.5f;
roiBox.fRoiAngle = 90.0f;
stBaseModuROIData->stRoiBox.push_back(roiBox);
stBaseModuROIData->iRoiTypeIndex = IMVS_ROI_TYPE_BOX;
return nRet;
}
问题根因
不熟悉自研算法模块的ROI获取机制。
4.1.14 日志打印:日志打印的方法
描述
环境:VM4.0.0及以上 + VS2013
现象:自研算法模块如何打印日志,从而协助定位问题点?
解答
1.针对VM4.3之前版本,在算法工程的AlgorithmModule.cpp文件中,在需要打印日志的位置调用MLOG_ERROR接口,调用示例如下图所示。(针对vm4.2,需要联系结束人员获取spdlog文件,注意日志打印接口初始化,以及获取模块id)
MLOG日志接口,支持不同等级的日志输出,严重级别从高到低分别是ERROR、WARN、INFO、DEBUG、TRACE。开发者可根据实际情况选择相应等级的日志接口。
2.针对VM4.3及之后版本,在算法工程的AlgorithmModule.cpp文件中,在需要打印日志的位置调用MLOG_ERROR或者LOG_ERROR接口,调用示例如下图所示。
MLOG和LOG日志接口,支持不同等级的日志输出,严重级别从高到低分别是ERROR、WARN、INFO、DEBUG、TRACE。开发者可根据实际情况选择相应等级的日志接口。
3.模块中打印的日志,会记录在Applications\log\Module\对应模块名.log文件中。
问题根因
不熟悉自研算法模块的日志打印机制。
4.1.15 参数自执行:参数自执行的方法
描述
环境:VM4.3.0及以上 + VS2013
现象:自研算法模块如何实现参数自执行,从而调整参数时能实时看到模块结果?
解答
在模块名AlgorithmTab.xml中找到Tab_ROI Area节点,增加CanTriggerRun = “TRUE”,如下图所示。
问题根因
不熟悉自研算法模块参数自执行的机制。