目录
- 3.2.1 图像载入:本地图像的载入方法
- 3.2.2 相机取流:相机SDK取流的方法
- 3.2.3 输入图像:给算子模块输入图像数据的方法
- 3.2.4 实时取流:实时取流的实现方法
- 3.2.5 卡尺ROI:卡尺型ROI的生成方法
- 3.2.6 DL算子耗时:深度学习算子长时间停止再运行耗时变长问题的解决方法
3.2.1 图像载入:本地图像的载入方法
描述
 环境:MVDAlgrithm SDK3.4及以上 + VS2013及以上
 现象:彩色图像如何载入?
 解答
 示例代码如下:
 一、彩色图像的载入
1.	C#
2.	 
3.	CMvdImage cMvdImage = new CMvdImage();
4.	cMvdImage.InitImage(ImagePathStr,MVD_PIXEL_FORMAT.MVD_PIXEL_RGB_RGB24_C3);
二、灰度图像的载入
1.	C#
2.	 
3.	CMvdImage cMvdImage = new CMvdImage();
4.	cMvdImage.InitImage(ImagePathStr,MVD_PIXEL_FORMAT.MVD_PIXELMONO_08);
问题根因
 不熟悉彩色图像的像素格式。
 提示
 如果原图像素格式为BGR,需要转换通道灰度值,再调用上述像素格式。其它接口,例如MVD_PIXEL_RGB_BGR24_C3等不可使用。
3.2.2 相机取流:相机SDK取流的方法
描述
 环境:MVDAlgrithm SDK3.4及以上 + VS2013及以上
 现象:如何使用相机SDK取流?
 解答
 可以通过调用相机SDK来实现相机取流。
 1.截取相机帧数据。示例代码如下。
1.	C#
2.	//相机相关变量
3.	private VisionDesigner.Camera.CCameraTool m_cMyCameraToolObj = null;
4.	 
5.	/// <summary>
6.	/// 打开相机并开始取图
7.	/// </summary>
8.	public int OpenCamera(int cameraindex = 0)
9.	{
10.	    try
11.	    {
12.	        //DeviceListAcq();//获取相机列表方法
13.	        //创建相机算子实例
14.	        if (null == m_cMyCameraToolObj)
15.	        {
16.	            m_cMyCameraToolObj = new VisionDesigner.Camera.CCameraTool();
17.	            if (null == m_cMyCameraToolObj)
18.	            {
19.	                return -1;
20.	            }
21.	        }
22.	        //设置连续采集模式
23.	        m_cMyCameraToolObj.SelectDevice(cameraindex);//默认选择索引为0的相机
24.	        m_cMyCameraToolObj.OpenDevice();
25.	        m_cMyCameraToolObj.SetEnumValue("AcquisitionMode", (uint)MVD_CAM_ACQUISITION_MODE.MVD_ACQ_MODE_CONTINUOUS);
26.	        m_cMyCameraToolObj.SetEnumValue("TriggerMode", (uint)MVD_CAM_TRIGGER_MODE.MVD_TRIGGER_MODE_OFF);
27.	        m_cMyCameraToolObj.SetEnumValue("TriggerSource", (uint)MVD_CAM_TRIGGER_SOURCE.MVD_TRIGGER_SOURCE_SOFTWARE);
28.	        m_cMyCameraToolObj.StartGrab();//开始取图
29.	        return 0;
30.	    }
31.	    catch (Exception ex)
32.	    {
33.	        return -1;
34.	    }
35.	}
36.	/// <summary>
37.	/// 从相机获取一帧图像
38.	/// </summary>
39.	private void GetStreamThreadProc()
40.	{
41.	    int nRet = 0;
42.	    CMvdImage imgtemp = null;
43.	    nRet = 0;
44.	    nRet = m_cMyCameraToolObj.CameraGrabResult.GetOneFrameTimeout (ref imgtemp);
45.	    if (0 == nRet && imgtemp != null)
46.	    {
47.	        ImageData imageshow1 = CMvdImageToImageData(imgtemp);
48.	     }
49.	}
2.将相机帧类型CMvdImage图像数据转换成ImageData类型数据,代码如下。
50. C#
51. 	
52.	/// <summary>
53.	/// CMvdImage格式的图像转为imagedata图像
54.	/// </summary>
55.	/// <param name="image"></param>
56.	/// <returns></returns>
57.	public ImageData CMvdImageToImageData(CMvdImage image)
58.	{
59.	    if (image != null)
60.	    {
61.	        ImageData imageData = new ImageData();
62.	        imageData.Width = (int)image.Width;
63.	        imageData.Height = (int)image.Height;
64.	        imageData.PixelFormat = PixelFormats.Gray8;
65.	        imageData.ImageBuffer = new byte[image.GetImageData(0).arrDataBytes.Length];
66.	        Array.Copy(image.GetImageData(0).arrDataBytes, imageData.ImageBuffer, imageData.ImageBuffer.Length);
67.	        return imageData;
68.	    }
69.	    else
70.	    {
71.	        return null;
72.	    }
73.	}
问题根因
 不熟悉相机SDK及其接口。
3.2.3 输入图像:给算子模块输入图像数据的方法
描述
 环境:MVDAlgrithm SDK3.4及以上 + VS2013及以上
 现象:如何通过图像数据给算子模块输入图像?
 解答
 以字符识别算子模块为例,代码如下所示,分为c++和c#。
//C++
//设置输入图像
int width = 2048;   //图像宽度
int height = 2024;  //图像高度
unsigned char* data = new unsigned char[2048*2024];
memset(data, '0', 2048 * 2024);//内存中图像数据
MVD_IMAGE_DATA_INFO stImageData;
stImageData.stDataChannel[0].pData = data;
stImageData.stDataChannel[0].nRowStep = width;
stImageData.stDataChannel[0].nSize = width * height;
stImageData.stDataChannel[0].nLen = width * height;
MVD_PIXEL_FORMAT pixelFormat = MVD_PIXEL_MONO_08;   //灰度图
IMvdImage* pInputImage = NULL;
CreateImageInstance(&pInputImage);
pInputImage->InitImage(width, height , pixelFormat, stImageData); //加载内存图像的唯一方法,SetPrivateData/SetPixel均不能使用
//字符识别算子
IOCRSegmenter* pOCRSegmentTool = NULL;
CreateOCRSegmenterInstance(&pOCRSegmentTool);
pOCRSegmentTool->SetInputImage(pInputImage);
//c#
//设置输入图像
uint width = 2048;
uint height = 2024;
byte[] data = new byte[2048 * 2024];
MVD_IMAGE_DATA_INFO stImageData = new MVD_IMAGE_DATA_INFO();
stImageData.stDataChannel[0].arrDataBytes = data;
stImageData.stDataChannel[0].nRowStep = width;
stImageData.stDataChannel[0].nSize = width * height;
stImageData.stDataChannel[0].nLen = width * height;
MVD_PIXEL_FORMAT pixelFormat = MVD_PIXEL_FORMAT.MVD_PIXEL_MONO_08;
VisionDesigner.CMvdImage cInputImg = new CMvdImage();
cInputImg.InitImage(width, height , pixelFormat, stImageData);
//字符识别算子
VisionDesigner.OCR.COCRTool cOCRToolObj = new VisionDesigner.OCR.COCRTool ();
cOCRToolObj.InputImage = cInputImg;
问题根因
 不熟悉算子模块函数接口
3.2.4 实时取流:实时取流的实现方法
描述
 环境:MVDAlgrithm SDK3.4及以上 + VS2013及以上
 现象:在算子SDK开发中,如何实现实时取流?
 解答
 VM算子SDK中提供了有关相机的操作算子,首先,我们需要了解一般步骤。对于设备进行操作,实现图像采集、参数配置等功能,需要先连接设备(打开设备),其具体流程如下图所示。
 用代码实现步骤如下,连接相机并开始取流。
C# 
using VisionDesigner.Camera; //引用命名空间
private CCameraTool m_cMyCameraToolObj = null;//定义一个设备对象
Thread m_hReceiveThread = null;//定义取流线程
private bool _bGrabbing = false;
// 打开相机并开始取图方法
public int OpenCamera(int cameraIndex = 0)
{
    try
    {
        if (null == m_cMyCameraToolObj)
        {
            m_cMyCameraToolObj = new CCameraTool();
            if (null == m_cMyCameraToolObj)
            {
                return -1;
            }
        }
        //1、通过MVD_TRANSFER_LAYER_TYPE类型枚举设备,返回设备个数
        int nRet = CCameraTool.EnumDevices((uint)MVD_TRANSFER_LAYER_TYPE.MVD_USB_DEVICE | (uint)MVD_TRANSFER_LAYER_TYPE.MVD_GIGE_DEVICE);
        if (0 == nRet)
        {
            return -1;
        }
        //2、选择索引的设备并判断是否可达
        m_cMyCameraToolObj.SelectDevice(cameraIndex);
        if (!m_cMyCameraToolObj.IsDeviceAccessible(1))
            return -1;
        //3、打开设备,并设置相机参数(相机参数根据实际需求进行设置)
        m_cMyCameraToolObj.OpenDevice();
        m_cMyCameraToolObj.SetEnumValue("AcquisitionMode", (uint)MVD_CAM_ACQUISITION_MODE.MVD_ACQ_MODE_CONTINUOUS);
        m_cMyCameraToolObj.SetEnumValue("TriggerMode", (uint)MVD_CAM_TRIGGER_MODE.MVD_TRIGGER_MODE_OFF);
        m_cMyCameraToolObj.SetEnumValue("PixelFormat", (uint)MVD_PIXEL_FORMAT.MVD_PIXEL_RGB_RGB24_C3);
        //4、开始取图
        m_cMyCameraToolObj.StartGrab();
        _bGrabbing = true;
        m_hReceiveThread = new Thread(GetStreamThreadProc);
        m_hReceiveThread.Start();//启动线程主动取流
        return 0;
    }
    catch (Exception)
    {
        return -1;
    }
}
// 取流线程
private void GetStreamThreadProc()
{
    CMvdImage cFrameImage = new CMvdImage();
    while (_bGrabbing)
    {
        //5、获取一帧图像
        m_cMyCameraToolObj.CameraGrabResult.GetOneFrameTimeout(ref cFrameImage);
        //判断图像格式,将图像加载到UI界面的控件mvdRenderActivex1上
        if ((VisionDesigner.MVD_PIXEL_FORMAT.MVD_PIXEL_MONO_08 == imgtemp.PixelFormat) || (VisionDesigner.MVD_PIXEL_FORMAT.MVD_PIXEL_RGB_RGB24_C3 == imgtemp.PixelFormat))
        {
            mvdRenderActivex1.LoadImageFromObject(cFrameImage);
            mvdRenderActivex1.Display();
        }
    }
}
// 停止抓图
private void StopGrab()
{
    try
    {
        _bGrabbing = false;
        if (null != m_hReceiveThread)
        {
            m_hReceiveThread.Abort();
            m_hReceiveThread = null;
        }
        //6、停止抓图、关闭设备、释放资源
        m_cMyCameraToolObj.StopGrab();
        m_cMyCameraToolObj.CloseDevice();
        m_cMyCameraToolObj.Dispose();
    }
    catch (Exception ex)
    { }
}
除了直接使用VisionDesigner.Camera.CCameraTool,通过以上步骤完成,也可以使用VM算子SDK的相机控件mvdCameraEdit,需要调用mvdCameraEdit.GetSubject()将算子对象赋给CCameraTool,再调用GetOneFrameTimeout()获取帧图像即可,详细可以参考路径“C:\Program Files (x86)\MVDAlgorithmSDK\Samples\CSharp\ControlSamples”下的CameraControlDemo示例。
 问题根因
 VM算子SDK中相机取流的步骤不熟悉。
3.2.5 卡尺ROI:卡尺型ROI的生成方法
描述
 环境:MVDAlgrithm SDK3.4及以上 + VS2013及以上
 现象:算子SDK开发,默认支持的ROI类型不包含直线卡尺ROI和圆卡尺ROI,那么该如何生成这两种类型的ROI,并且正确传递给算子使用呢?
 解答
 1.直线卡尺ROI
 直线卡尺ROI是由线段、卡尺数量、卡尺宽、卡尺高四个元素组合生成的,生成示例代码如下:
C#
private void GenLineCaliperROIAlg(MVD_POINT_F startPoint, MVD_POINT_F endPoint, float angle, int caliperCount, float caliperWidth, float caliperHeight, ref List<CMvdRectangleF> cMvdRectangleFList, ref CMvdRectangleF minRect)
{
      CaliperCenters.Clear();
      float fLineHeight = endPoint.fY - startPoint.fY;
      float fLineWidth = endPoint.fX - startPoint.fX;
      float xOffset = fLineWidth / (caliperCount + 1);
      float yOffset = fLineHeight / (caliperCount + 1);
      float fRotateAngle = angle;
      MVD_POINT_F stCenter = new MVD_POINT_F();
      CMvdRectangleF rectangleF;
      // 生成卡尺框列表
      for (int i = 1; i < caliperCount + 1; i++)
      {
           stCenter.fX = startPoint.fX + xOffset * i;
           stCenter.fY = startPoint.fY + yOffset * i;
           CaliperCenters.Add(stCenter);
           rectangleF = new CMvdRectangleF(stCenter.fX, stCenter.fY, caliperWidth, caliperHeight);
           rectangleF.Angle = fRotateAngle;
           rectangleF.BorderColor = blue;
           cMvdRectangleFList.Add(rectangleF);
      }
      // 生成最小外接矩形
      float LineMidX = (float)(0.5 * (endPoint.fX + startPoint.fX));
      float LineMidY = (float)(0.5 * (endPoint.fY + startPoint.fY));
      minRect = new CMvdRectangleF(LineMidX, LineMidY, fLineWidth, caliperHeight);
      minRect.Angle = fRotateAngle;
}
生成直线卡尺ROI后,一般提供给直线查找算子使用。使用时,算子的ROI设置为直线卡尺ROI的最小外接矩形,算子基本参数的运行模式设置为ONLY_FIND模式,算子基本参数的卡尺框列表设置为卡尺框的列表。调用示例代码如下:
C#
private List<CMvdRectangleF> cMvdRectangleFs = new List<CMvdRectangleF>();
cMvdRectangleFs.Clear();
CMvdRectangleF minRect = new CMvdRectangleF(cMvdImage.Width / 2, cMvdImage.Height / 2, cMvdImage.Width / 4, cMvdImage.Height / 4);
MVD_POINT_F lineStartPoint = new MVD_POINT_F(1700, 1122);
MVD_POINT_F lineEndPoint = new MVD_POINT_F(2286, 1122);
float angleRad = (float)Math.Atan2((lineEndPoint.fY - lineStartPoint.fY), (lineEndPoint.fX - lineStartPoint.fX));
float angle = (float)(angleRad / Math.PI * 180.0);
GenLineCaliperROIAlg(lineStartPoint, lineEndPoint, angle, caliperCount, caliperWidth, caliperHeight, ref cMvdRectangleFs, ref minRect);
// 直线查找
CLineFindTool cLineFindTool = new CLineFindTool();
cLineFindTool.InputImage = cMvdImage;
cLineFindTool.ROI = minRect;
cLineFindTool.BasicParam.RunningMode = VisionDesigner.LineFind.MVD_RUNNING_MODE.MVD_RUNNING_MODE_ONLY_FIND; // 查找结果受ROI和CaliperList影响     
cLineFindTool.BasicParam.CaliperList = cMvdRectangleFs;
cLineFindTool.SetRunParam("LineFindMode", "Best");
cLineFindTool.SetRunParam("EdgePolarity", "Both");
cLineFindTool.Run();
CLineFindResult cLineFindRes = cLineFindTool.Result;
List<CLineFindEdgePointInfo> cLineFindEdgePointInfos = cLineFindRes.EdgePointInfo;
// 直线轮廓点
CMvdPointSetF lineEdgePoint = new CMvdPointSetF();
lineEdgePoint.BorderColor = green;
for (int i = 0; i < cLineFindEdgePointInfos.Count; i++)
{
lineEdgePoint.AddPoint(cLineFindEdgePointInfos[i].EdgePoint.fX, cLineFindEdgePointInfos[i].EdgePoint.fY, i);
}
mvdRenderActivex1.AddShape(lineEdgePoint);
// 输出直线
CMvdLineSegmentF line = new CMvdLineSegmentF(new MVD_POINT_F(cLineFindRes.LineStartPoint.fX, cLineFindRes.LineStartPoint.fY), new MVD_POINT_F(cLineFindRes.LineEndPoint.fX, cLineFindRes.LineEndPoint.fY));
line.BorderColor = green;
mvdRenderActivex1.AddShape(line);
// 直线卡尺框
for (int i = 0; i < caliperCount; i++)
{
mvdRenderActivex1.AddShape(cMvdRectangleFs[i]);
}
// 直线检测区域
minRect.BorderColor = blue;
mvdRenderActivex1.AddShape(minRect);
mvdRenderActivex1.Display();
2.圆卡尺ROI
 圆卡尺ROI是由圆心、半径、卡尺数量、卡尺宽、卡尺高五个元素组合生成的,生成示例代码如下:
C#
private void GenCircleCaliperROIAlg(MVD_POINT_F centerPoint, float radius, int caliperCount, float caliperWidth, float caliperHeight, ref List<CMvdRectangleF> cMvdRectangleFList, ref CMvdRectangleF minRect)
{
     CaliperCenters.Clear();
      float angleOffset = 360.0f / caliperCount;
      float angleStart = 180.0f / caliperCount;
      MVD_POINT_F stCenter = new MVD_POINT_F();
      CMvdRectangleF rectangleF;
      // 生成卡尺框列表
      for (int i = 0; i < caliperCount; i++)
      {
           float fRotateAngle = angleStart + angleOffset * i;
           if (fRotateAngle > 180.0f)
           {
                fRotateAngle -= 360.0f;
           }
           stCenter.fX = (float)(radius * Math.Cos(fRotateAngle / 180.0f * Math.PI) + centerPoint.fX);
           stCenter.fY = (float)(radius * Math.Sin(fRotateAngle / 180.0f * Math.PI) + centerPoint.fY);
           CaliperCenters.Add(stCenter);
           rectangleF = new CMvdRectangleF(stCenter.fX, stCenter.fY, caliperHeight, caliperWidth);
           rectangleF.Angle = fRotateAngle;
           rectangleF.BorderColor = blue;
           cMvdRectangleFList.Add(rectangleF);
      }
      // 生成最小外接矩形
      float CircleMidX = centerPoint.fX;
      float CircleMidY = centerPoint.fY;
      minRect = new CMvdRectangleF(CircleMidX, CircleMidY, (float)(2 * radius + caliperHeight), (float)(2 * radius + caliperHeight));
      minRect.Angle = 0.0f;
}
生成圆卡尺ROI后,一般提供给圆查找算子使用。使用时,算子的ROI设置为圆卡尺ROI的最小外接矩形,算子基本参数的运行模式设置为ONLY_FIND模式,算子基本参数的卡尺框列表设置为卡尺框的列表。调用示例代码如下:
C#
private List<CMvdRectangleF> cMvdRectangleFs = new List<CMvdRectangleF>();
cMvdRectangleFs.Clear();
CMvdRectangleF minRect = new CMvdRectangleF(cMvdImage.Width / 2, cMvdImage.Height / 2, cMvdImage.Width / 4, cMvdImage.Height / 4);
MVD_POINT_F circleCenter = new MVD_POINT_F(2258, 1961);
float circleRadius = 357;
GenCircleCaliperROIAlg(circleCenter, circleRadius, caliperCount, caliperWidth, caliperHeight, ref cMvdRectangleFs, ref minRect);
// 圆查找
CCircleFindTool cCircleFindTool = new CCircleFindTool();
cCircleFindTool.InputImage = cMvdImage;
cCircleFindTool.ROI = minRect;
cCircleFindTool.BasicParam.RunningMode = VisionDesigner.CircleFind.MVD_RUNNING_MODE.MVD_RUNNING_MODE_ONLY_FIND; // 查找结果受ROI和CaliperList影响     
cCircleFindTool.BasicParam.CaliperList = cMvdRectangleFs;
float fMinRadius = (float)(circleRadius - 0.5 * caliperHeight);
float fMaxRadius = (float)(circleRadius + 0.5 * caliperHeight);
cCircleFindTool.SetRunParam("MinRadius", Convert.ToInt32(fMinRadius).ToString());
cCircleFindTool.SetRunParam("MaxRadius", Convert.ToInt32(fMaxRadius).ToString());
cCircleFindTool.SetRunParam("EdgeThresh", "15");
cCircleFindTool.SetRunParam("CircleFindMode", "Best");
cCircleFindTool.SetRunParam("EdgePolarity", "WhiteToBlack");
cCircleFindTool.SetRunParam("RejectNum", "0");
cCircleFindTool.SetRunParam("RejectDist", "5");
cCircleFindTool.Run();
CCircleFindResult cCircleFindRes = cCircleFindTool.Result;
List<CCircleFindEdgePointInfo> cCircleFindEdgePointInfos = cCircleFindRes.EdgePointInfo;
// 圆轮廓点
CMvdPointSetF circleEdgePoint = new CMvdPointSetF();
circleEdgePoint.BorderColor = green;
for (int i = 0; i < cCircleFindEdgePointInfos.Count; i++)
{
circleEdgePoint.AddPoint(cCircleFindEdgePointInfos[i].EdgePoint.fX, cCircleFindEdgePointInfos[i].EdgePoint.fY, i);
}
mvdRenderActivex1.AddShape(circleEdgePoint);
// 输出圆环
CMvdCircleF circle = new CMvdCircleF(cCircleFindRes.Circle.Center, cCircleFindRes.Circle.Radius);
circle.BorderColor = green;
mvdRenderActivex1.AddShape(circle);
// 输出ROI圆弧
CMvdAnnularSectorF circleAnnu = new CMvdAnnularSectorF(circleCenter, fMinRadius, fMaxRadius, 0.0f, 360.0f);
circleAnnu.BorderColor = blue;
mvdRenderActivex1.AddShape(circleAnnu);
// 圆卡尺框
for (int i = 0; i < caliperCount; i++)
{
mvdRenderActivex1.AddShape(cMvdRectangleFs[i]);
}
// 圆检测区域
minRect.BorderColor = blue;
mvdRenderActivex1.AddShape(minRect);
mvdRenderActivex1.Display();
运行效果如下图所示。
 
问题根因
 不熟悉卡尺型ROI的生成方法。
3.2.6 DL算子耗时:深度学习算子长时间停止再运行耗时变长问题的解决方法
描述
 环境:MVDAlgrithm SDK3.4及以上 + VS2013及以上
 现象:深度学习算子长时间停止再运行,第一次运行耗时会变长,如何解决?
 解答
 原因是显卡休眠,深度学习算子运行前需要初始化CUDA资源,故第一次耗时较长。长时间停止将导致显卡休眠,从而导致再运行耗时变长。所以,需每隔一定周期将显卡唤醒,防止软件一段时间未运行时显卡休眠。显卡唤醒进程代码如下:
C++
int main()
{
	printf("====================Awake Gpu start====================\n");
	// 覆盖写入文件,不必删除文件
	std::ofstream outFile("AwakenGPUToolLog.txt");
	outFile << "Awake Gpu start" << std::endl;
	//开启线程进行防GPU空闲
	int       data_c[2] = { 0 };
	int      *data_g = NULL;
	int                        gpu_count = 0;
	cudaError_t                    err = cudaSuccess;
	struct cudaDeviceProp          prop = { 0 };
	do 
	{
		err = cudaGetDeviceCount(&gpu_count);
		if ((cudaSuccess != err) || (1 > gpu_count))
		{
			printf("Have no cuda device.err = %d\n", err);
			outFile << "Have no cuda device, err = " << err << std::endl;
			break;
		}
		err = cudaMalloc((void**)&data_g, sizeof(int));
		if (cudaSuccess != err)
		{
			printf("Cuda malloc error. err = %d\n", err);
			outFile << "Cuda malloc error, err = " << err << std::endl;
			break;
		}
		outFile << "GPU OK." << std::endl;
		while (1)
		{
			Sleep(10000);
			err = cudaMemcpy(data_g, data_c, sizeof(int), cudaMemcpyHostToDevice);
			if (cudaSuccess != err)
			{
				printf("Cuda memcpy error.\n");
				outFile << "Cuda memcpy error, err = " << err << std::endl;
				continue;
			}
			printf("GPU OK.\n");
		}
	} while (false);
	outFile << "Awake Gpu end." << std::endl;
	if (NULL != data_g)
	{
		cudaFree(data_g);
		data_g = NULL;
	}
	outFile.close();
	//system("Pause");
	return 0;
}
问题根因
 不熟悉显卡唤醒的方法。


















