【VM服务管家】VM4.x算法模块开发_4.1 开发配置类

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
现象:自定义算子模块开发中如何实现文件交互?
解答

  1. 打开模块对应的***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>

界面示意如下:
在这里插入图片描述

  1. 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);
}
  1. 完成以上两步就可以实现界面选择文件并加载文件操作,要实现每次执行时能同步加载最新的文件还需添加刷新标志位并在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”,如下图所示。
在这里插入图片描述

问题根因
不熟悉自研算法模块参数自执行的机制。

猜你喜欢

转载自blog.csdn.net/MVExpert/article/details/130410537