目录
- 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;
}
问题根因
不熟悉显卡唤醒的方法。