一 前言
发现工作中好多计算机视觉上位机项目都用海康相机,为了能够更好的学习和工作,我自己依据同事的源码和网上的一些总结编写本博客。通过本次学习,让我明白一点,无论学习什么技术都要学会自己看技术文档,而不是第一时间上网找源码。以工业相机SDK使用说明.chm为例,如何检索相机,打开相机,取流,抓取,保存图片,关闭相机,释放资源都有详细的介绍。
二 海康相机使用流程
以下部分图片引用自工业相机SDK使用手册 v2.4.1,仅作为学习使用,以下简称使用手册
这里要说明以下几点
1 这个接口流程是C接口流程,除了C接口流程以外还有C++接口流程,有朋友可能会问为什么标题是C#工业相机操作,这里却介绍C接口。实际上通过DoNet源码可以看出虽然方法名以_NET结尾,但是方法的实现引用了C接口,如下图所示
2 可能随着使用手册版本的更新导致变量名,方法名等发生小幅度改变,掌握方法是关键
3 由上述流程图可以看出使用相机的流程分为:查找子网内相机设备->创建管理相机的句柄->打开设备->获取数据包->数据包处理->关闭设备->释放资源
三 相机操作
3.1 检索网络设备
3.1.1 枚举相机类型
示例代码如下
using System;using System.IO;using MvCamCtrl.NET;int nTransLayers = MyCamera.MV_CC_EnumerateTls_NET();if ((nTransLayers & MyCamera.MV_GIGE_DEVICE) == MyCamera.MV_GIGE_DEVICE) { Console.WriteLine("MV_GIGE_DEVICE"); }
3.1.2 枚举相机信息
大部分情况是不需要检索相机类型的,相机是你根据项目需求买的,你应该知道它是什么相机。而且相机通过网线与计算机联系,通过配置IP就可以知道是正在对哪台相机操作。所以正常情是直接从网络中枚举相机。也就是如下的方法
注意:此枚举方法的第二个参数类型是MV_CC_DEVICE_INFO_LIST 相机设备信息列表,以下分别是使用手册中的数据类型定义和VS中的数据类型定义。
可以看到VS源码中多了一行代码[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)],这里的MarshalAs在VS里官方的注释是指示如何在托管代码与非托管代码之间封送数据,那么有牵扯了两个名词。通过百度百科得到以下总结
名称 | 释义与区别 |
托管代码 | 托管代码 (managed code)同受管制的代码,由公共语言运行库环境(而不是直接由操作系统)执行的代码。托管代码应用程序可以获得公共语言运行库服务,例如自动垃圾回收、运行库类型检查和安全支持等。这些服务帮助提供独立于平台和语言的、统一的托管代码应用程序行为。 |
非托管代码 | 在公共语言运行库环境的外部,由操作系统直接执行的代码,非托管代码必须提供自己的垃圾回收、类型检查、安全支持等服务,它与托管代码不同,后者从公共语言运行库中获得这些服务,而非托管代码是在运行库之外运行的代码。例如COM 组件、ActiveX 接口和 Win32 API 函数都是非托管代码的示例。 |
通过VS追踪MarshalAS可以看出其来自public sealed class MarshalAsAttribute : Attribute这个类中的方法。至于方法内部实现暂时不是我们需要考虑的,我们需要知道以下几点(引用自MarshalAs的简单总结-CSDN博客)
[MarshalAs(UnmanagedType unmanagedType, 命名参数)]
实际上相当于构造一个MarshalAsAttribute类的对象
常用的UnmanagedType枚举值:
BStr 长度前缀为双字节的 Unicode 字符串;
LPStr 单字节、空终止的 ANSI 字符串。;
LPWStr 一个 2 字节、空终止的 Unicode 字符串;
ByValArray 用于在结构中出现的内联定长字符数组,应始终使用MarshalAsAttribute的 SizeConst字段来指示数组的大小。
注意:
在用Marshal.SizeOf(),即获取对象的非托管大小时,获得的是自己定义的大小;
但在实际处理的时候,是按照实际的大小来获取的。
接下来介绍相关相机信息数据类型,注意这几个结构体在使用手册查找的时候要到索引中找
那么到底如何进行网络检索相机设备呢
/*虽然系统有专门对相机信息列表数据类型的定义,但是我们最好还是自定义一个相机信息数据类为什么要专门定义一个类呢,原因如下1 MV_CC_DEVICE_INFO通用结构体转化为专用的相机结构体中对于相机IP的并不是192.168.*.*的形式,需要们自行偏移获取如MV_CC_DEVICE_INFO->MV_GIGE_DEVICE_INFO MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO));UInt32 nNetIp1 = (gigeInfo.nNetExport & 0xFF000000) >> 24;UInt32 nNetIp2 = (gigeInfo.nNetExport & 0x00FF0000) >> 16;UInt32 nNetIp3 = (gigeInfo.nNetExport & 0x0000FF00) >> 8;UInt32 nNetIp4 = (gigeInfo.nNetExport & 0x000000FF);// ch:显示IP | en:Display IPUInt32 nIp1 = (gigeInfo.nCurrentIp & 0xFF000000) >> 24;UInt32 nIp2 = (gigeInfo.nCurrentIp & 0x00FF0000) >> 16;UInt32 nIp3 = (gigeInfo.nCurrentIp & 0x0000FF00) >> 8;UInt32 nIp4 = (gigeInfo.nCurrentIp & 0x000000FF);*///定义一个相机信息类 这个类定义的位置建议在一个全局模块中public class CameraInfor{ public string userDefinedName { get; set; } //用户定义的名字 public string serialNumber { get; set; } //序列号 public string deviceIPAdd { get; set; } //设备IP地址 public string manufacturerName { get; set; }//制造厂商名 public string modelName { get; set; } //型号名称 public string deviceType { get; set; } //型号名称 public IntPtr pDeviceInfo; //相机句柄 public float ExposureTime { get; set; } //曝光时间 public float Gain { get; set; } //增益 public float ResultingFrameRate { get; set; } //帧率 public bool isOpenTriggerMode { get; set; } //是否打开触发方式}//*********************************************************************//相机模块部分//创建一个全局的相机字典,IP为键,相机信息(CameraInfor)为值 public static Dictionary<string, CameraInfor> mapOfGigeCameras = new Dictionary<string, CameraInfor>();//创建一个全局的相机对象来保存当前正在使用的相机信息private CameraInfor currentCameraInfor;//根据相机枚举方法参数声明变量MyCamera.MV_CC_DEVICE_INFO_LIST m_stDeviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();//创建一个相机对象 注意只是创建了一个相机对象,并没有指定管理哪个相机private MyCamera m_MyCamera = new MyCamera(); //回调函数声明 用来处理拍照 public delegate void Process(Mat src); public Process callback;//抓图标志public bool m_bGrabbing=false; Thread m_hReceiveThread = null; MyCamera.MV_FRAME_OUT_INFO_EX m_stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX(); // ch:用于从驱动获取图像的缓存 UInt32 m_nBufSizeForDriver = 0; IntPtr m_BufForDriver = IntPtr.Zero; private static Object BufForDriverLock = new Object();//封装枚举相机的方法public void enumDevice(){ System.GC.Collect();//强制对所有代进行及时垃圾回收 m_stDeviceList.nDeviceNum = 0; //初始化信息列表 int nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_stDeviceList); if (0 != nRet) { return; }mapOfGigeCameras.Clear(); //清空设备列表CameraInfor cameraInfor ;for (int i = 0; i < m_stDeviceList.nDeviceNum; i++){ MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));//获取第i个相机句柄的相机信息 cameraInfor = new CameraInfor(); cameraInfor.pDeviceInfo = m_stDeviceList.pDeviceInfo[i];//获取相机句柄 if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE) { MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO));//将通用相机信息结构体转化为Gige相机结构体 UInt32 nNetIp1 = (gigeInfo.nNetExport & 0xFF000000) >> 24; UInt32 nNetIp2 = (gigeInfo.nNetExport & 0x00FF0000) >> 16; UInt32 nNetIp3 = (gigeInfo.nNetExport & 0x0000FF00) >> 8; UInt32 nNetIp4 = (gigeInfo.nNetExport & 0x000000FF); // ch:显示IP | en:Display IP UInt32 nIp1 = (gigeInfo.nCurrentIp & 0xFF000000) >> 24; UInt32 nIp2 = (gigeInfo.nCurrentIp & 0x00FF0000) >> 16; UInt32 nIp3 = (gigeInfo.nCurrentIp & 0x0000FF00) >> 8; UInt32 nIp4 = (gigeInfo.nCurrentIp & 0x000000FF); cameraInfor.serialNumber = gigeInfo.chSerialNumber; cameraInfor.userDefinedName = gigeInfo.chUserDefinedName; cameraInfor.manufacturerName = gigeInfo.chManufacturerName; cameraInfor.modelName = gigeInfo.chModelName; cameraInfor.deviceType = "GEV"; //千兆网工业相机技术之GigE Vision(GEV) cameraInfor.deviceIPAdd = nIp1.ToString() + "." + nIp2.ToString() + "." + nIp3.ToString() + "." + nIp4.ToString(); mapOfGigeCameras.Add(cameraInfor.deviceIPAdd, cameraInfor); } else if (device.nTLayerType == MyCamera.MV_USB_DEVICE) { MyCamera.MV_USB3_DEVICE_INFO usbInfo = (MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO)); //本文主要讲解Gige相机的使用,实际工作中可以根据需要编写此部分代码 } }}
3.2 打开相机
3.2.1 打开相机的流程:
1 创建管理相机的句柄:通过创建句柄,指定我们之前创建的相机对象管理哪个相机
2 打开相机对象
3.2.2 打开相机的相关方法
3.2.3 打开相机操作
public void OpenDeviceByIP(string deviceIp){ if (m_stDeviceList.nDeviceNum == 0 || deviceIp=="")//枚举设备个数为0或者传入IP为空 { return; } currentCameraInfor = mapOfGigeCameras[deviceIp];//之前创建的全局当前相机信息用于获取指定IP的相机 MyCamera.MV_CC_DEVICE_INFO device =(MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(mapOfGigeCameras[deviceIp].pDeviceInfo,typeof(MyCamera.MV_CC_DEVICE_INFO));//IP索引到指定相机句柄的信息 if (null == m_MyCamera) //判断之前创建的全局相机对象是否创建成功 { m_MyCamera = new MyCamera(); if (null == m_MyCamera) { return; } } int nRet = m_MyCamera.MV_CC_CreateDevice_NET(ref device);//指定对象管理哪一个相机 if (MyCamera.MV_OK != nRet) { return; } nRet = m_MyCamera.MV_CC_OpenDevice_NET();//打开对象管理的相机 if (MyCamera.MV_OK != nRet) { m_MyCamera.MV_CC_DestroyDevice_NET(); return; } // ch:探测网络最佳包大小(只对GigE相机有效) /*这里介绍一下相关方法 当我们要获取或者设置相机对象参数的时候注意查看使用手册中对应的参数信息 1 目标参数是什么数据类型 2 调用对应的获取整形,浮点型的方法通过参数名获取 后面详细介绍 */ if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE) { int nPacketSize = m_MyCamera.MV_CC_GetOptimalPacketSize_NET(); if (nPacketSize > 0) { nRet = m_MyCamera.MV_CC_SetIntValue_NET("GevSCPSPacketSize", (uint)nPacketSize);//设置网络包大小 if (nRet != MyCamera.MV_OK) { } } else { } } // ch:设置采集连续模式 m_MyCamera.MV_CC_SetEnumValue_NET("AcquisitionMode", (uint)MyCamera.MV_CAM_ACQUISITION_MODE.MV_ACQ_MODE_CONTINUOUS);//采集模式:单帧,多帧,连续 m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF);//触发模式 0-关 1-开 // ch:获取参数 MyCamera.MVCC_FLOATVALUE stParam = new MyCamera.MVCC_FLOATVALUE(); nRet = m_MyCamera.MV_CC_GetFloatValue_NET("ExposureTime", ref stParam);//获取曝光时间 if (MyCamera.MV_OK == nRet) { mapOfGigeCameras[deviceIp].ExposureTime = stParam.fCurValue; } nRet = m_MyCamera.MV_CC_GetFloatValue_NET("Gain", ref stParam);//获取相机增益 if (MyCamera.MV_OK == nRet) { mapOfGigeCameras[deviceIp].Gain = stParam.fCurValue; } nRet = m_MyCamera.MV_CC_GetFloatValue_NET("ResultingFrameRate", ref stParam);//采集帧率 if (MyCamera.MV_OK == nRet) { mapOfGigeCameras[deviceIp].ResultingFrameRate = stParam.fCurValue; } }
3.3. 拍照
3.3.1 拍照流程
1 采集图像
2 取流抓图
3 保存图片
4 释放流数据
3.3.2 相关方法与数据类型
MV_FRAME_OUT在使用手册中并没有找到但是在VS中反编译中有源码,其中pBufAddr是帧输出结果在内存中的地址
3.3.3 相关操作
public void CaptureFile(Process proc) //Process为委托数据类型 之前在全局代码中生命{ this.callback = proc; m_bGrabbing = true; m_hReceiveThread = new Thread(ReceiveThreadProcess); m_hReceiveThread.Start(); m_stFrameInfo.nFrameLen = 0;//取流之前先清除帧长度 m_stFrameInfo.enPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Undefined; // ch:开始采集 int nRet = m_MyCamera.MV_CC_StartGrabbing_NET(); if (MyCamera.MV_OK != nRet) { m_bGrabbing = false; m_hReceiveThread.Join(); return; }}public void ReceiveThreadProcess(){ MyCamera.MV_FRAME_OUT stFrameInfo = new MyCamera.MV_FRAME_OUT();//定义输出的帧信息结构体 MyCamera.MV_DISPLAY_FRAME_INFO stDisplayInfo = new MyCamera.MV_DISPLAY_FRAME_INFO(); int nRet = MyCamera.MV_OK; while (m_bGrabbing) { nRet = m_MyCamera.MV_CC_GetImageBuffer_NET(ref stFrameInfo, 1000);//获取一帧图片 并将图片信息保留在stFrameInfo中,超时时间设置为1秒 加的这一秒就是等待上一个线程采集完毕 if (nRet == MyCamera.MV_OK) { lock (BufForDriverLock) //同步互斥操作 { if (m_BufForDriver == IntPtr.Zero || stFrameInfo.stFrameInfo.nFrameLen > m_nBufSizeForDriver) //帧长度大于0 { if (m_BufForDriver != IntPtr.Zero) { Marshal.Release(m_BufForDriver); m_BufForDriver = IntPtr.Zero; } m_BufForDriver = Marshal.AllocHGlobal((Int32)stFrameInfo.stFrameInfo.nFrameLen);//申请数据流大小的内存 if (m_BufForDriver == IntPtr.Zero)//申请失败 { return; } m_nBufSizeForDriver = stFrameInfo.stFrameInfo.nFrameLen;//帧长度赋值 } m_stFrameInfo = stFrameInfo.stFrameInfo;//帧信息 CopyMemory(m_BufForDriver, stFrameInfo.pBufAddr, stFrameInfo.stFrameInfo.nFrameLen);//到内存指定的地址去获取帧数据 } if (RemoveCustomPixelFormats(stFrameInfo.stFrameInfo.enPixelType)) { m_MyCamera.MV_CC_FreeImageBuffer_NET(ref stFrameInfo); continue; } stDisplayInfo.hWnd = hWnd_pictureBox; stDisplayInfo.pData = stFrameInfo.pBufAddr; stDisplayInfo.nDataLen = stFrameInfo.stFrameInfo.nFrameLen; stDisplayInfo.nWidth = stFrameInfo.stFrameInfo.nWidth; stDisplayInfo.nHeight = stFrameInfo.stFrameInfo.nHeight; stDisplayInfo.enPixelType = stFrameInfo.stFrameInfo.enPixelType;//像素格式 if (isDisplayOnPictureBox)//初始化完成之后 { m_MyCamera.MV_CC_DisplayOneFrame_NET(ref stDisplayInfo);//显示一帧图像 将这帧图像的信息保存在变量中 } Mat dst1 = new Mat(stDisplayInfo.nHeight, stDisplayInfo.nWidth,MatType.CV_8UC3, m_BufForDriver); callback(dst1); //回调函数对图像进行相应的处理操作 m_MyCamera.MV_CC_FreeImageBuffer_NET(ref stFrameInfo); } else { if (currentCameraInfor.isOpenTriggerMode) { Thread.Sleep(5); } } }}//去除自定义的像素格式private bool RemoveCustomPixelFormats(MyCamera.MvGvspPixelType enPixelFormat){ Int32 nResult = ((int)enPixelFormat) & (unchecked((Int32)0x80000000)); if (0x80000000 == nResult) //错误或者无效句柄 { return true; } else { return false; }}public void SaveJPG(string path){MyCamera.MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam = new MyCamera.MV_SAVE_IMG_TO_FILE_PARAM(); if (m_stFrameInfo.nFrameLen == 0) { return; } stSaveFileParam.enImageType = MyCamera.MV_SAVE_IAMGE_TYPE.MV_Image_Jpeg; stSaveFileParam.enPixelType = m_stFrameInfo.enPixelType; stSaveFileParam.pData = m_BufForDriver; stSaveFileParam.nDataLen = m_stFrameInfo.nFrameLen; stSaveFileParam.nHeight = m_stFrameInfo.nHeight; stSaveFileParam.nWidth = m_stFrameInfo.nWidth; stSaveFileParam.nQuality = 80; stSaveFileParam.iMethodValue = 2; if (string.IsNullOrEmpty(path)) stSaveFileParam.pImagePath = System.Environment.CurrentDirectory + "\\Image\\" + picSavePath + "Image_w" + stSaveFileParam.nWidth.ToString() + "_h" + stSaveFileParam.nHeight.ToString() + "_fn" + m_stFrameInfo.nFrameNum.ToString() + ".jpg"; else { stSaveFileParam.pImagePath = path; } int nRet = m_MyCamera.MV_CC_SaveImageToFile_NET(ref stSaveFileParam); //Mat dst1 = new Mat(stSaveFileParam.nHeight, stSaveFileParam.nWidth,MatType.CV_8UC3, m_BufForDriver); //dst1.SaveImage("D:\\111.jpg"); if (MyCamera.MV_OK != nRet) { return; }}
4 关闭相机
public void CloseDevice(){ // ch:取流标志位清零 if (m_BufForDriver != IntPtr.Zero) //如果用于存储流数据的内存不为空则清空 { Marshal.Release(m_BufForDriver); } // ch:关闭设备 m_MyCamera.MV_CC_CloseDevice_NET(); m_MyCamera.MV_CC_DestroyDevice_NET();}
5 完整相机模块 里面还有其他模块的数据类型声明但是本博客不在扩展
using System;using System.Collections.Generic;using System.Linq;using System.Runtime.InteropServices;using System.Text;using System.Threading;using System.Threading.Tasks;using MvCamCtrl;using MvCamCtrl.NET;using OpenCvSharp;namespace GH{ public class HKCameraHelper { [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] //非重叠区域的拷贝CopyMemory 完全拷贝MoveMemory public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count); //复制内存块的目的地址,复制内存块的源地址 复制大小(单位字节) /// <summary> /// 枚举出来的子网信息,都在相机结构体 MV_CC_DEVICE_INFO_LIST 里面 /// </summary> MyCamera.MV_CC_DEVICE_INFO_LIST m_stDeviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST(); private MyCamera m_MyCamera = new MyCamera(); bool m_bGrabbing = false; Thread m_hReceiveThread = null; MyCamera.MV_FRAME_OUT_INFO_EX m_stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX(); // ch:用于从驱动获取图像的缓存 | en:Buffer for getting image from driver UInt32 m_nBufSizeForDriver = 0; IntPtr m_BufForDriver = IntPtr.Zero; private static Object BufForDriverLock = new Object(); //枚举所有相机设备列表 public static Dictionary<string, CameraInfor> mapOfCameraDevice = new Dictionary<string, CameraInfor>(); //保存获取的当前相机 private CameraInfor currentCameraInfor; public string picSavePath { get; set; } public IntPtr hWnd_pictureBox { get; set; } public ASCameraParamInfor aSCameraParamInfor { get; set; } //相机参数 //回调函数声明 public delegate void ProcessBussess(Mat src); public ProcessBussess callback; public bool isDisplayOnPictureBox { get; set; } //获取相机设备列表 public void DeviceListAcq() { // ch:创建设备列表 | en:Create Device List System.GC.Collect(); m_stDeviceList.nDeviceNum = 0; int nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_stDeviceList); if (0 != nRet) { return; } mapOfCameraDevice.Clear(); //清空设备列表 CameraInfor cameraInfor ; // ch:在窗体列表中显示设备名 | en:Display device name in the form list for (int i = 0; i < m_stDeviceList.nDeviceNum; i++) { MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO)); cameraInfor = new CameraInfor(); cameraInfor.pDeviceInfo = m_stDeviceList.pDeviceInfo[i]; if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE) { MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO)); UInt32 nNetIp1 = (gigeInfo.nNetExport & 0xFF000000) >> 24; UInt32 nNetIp2 = (gigeInfo.nNetExport & 0x00FF0000) >> 16; UInt32 nNetIp3 = (gigeInfo.nNetExport & 0x0000FF00) >> 8; UInt32 nNetIp4 = (gigeInfo.nNetExport & 0x000000FF); // ch:显示IP | en:Display IP UInt32 nIp1 = (gigeInfo.nCurrentIp & 0xFF000000) >> 24; UInt32 nIp2 = (gigeInfo.nCurrentIp & 0x00FF0000) >> 16; UInt32 nIp3 = (gigeInfo.nCurrentIp & 0x0000FF00) >> 8; UInt32 nIp4 = (gigeInfo.nCurrentIp & 0x000000FF); cameraInfor.serialNumber = gigeInfo.chSerialNumber; cameraInfor.userDefinedName = gigeInfo.chUserDefinedName; cameraInfor.manufacturerName = gigeInfo.chManufacturerName; cameraInfor.modelName = gigeInfo.chModelName; cameraInfor.deviceType = "GEV"; //千兆网工业相机技术之GigE Vision(GEV) cameraInfor.deviceIPAdd = nIp1.ToString() + "." + nIp2.ToString() + "." + nIp3.ToString() + "." + nIp4.ToString(); mapOfCameraDevice.Add(cameraInfor.deviceIPAdd, cameraInfor); } else if (device.nTLayerType == MyCamera.MV_USB_DEVICE) { MyCamera.MV_USB3_DEVICE_INFO usbInfo = (MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO)); cameraInfor.serialNumber = usbInfo.chSerialNumber; cameraInfor.userDefinedName = usbInfo.chUserDefinedName; cameraInfor.manufacturerName = usbInfo.chManufacturerName; cameraInfor.modelName = usbInfo.chModelName; cameraInfor.deviceType = "U3V"; mapOfCameraDevice.Add(cameraInfor.deviceIPAdd, cameraInfor); } } } //打开相机 public void OpenDeviceByIPAdd(string deviceIpAdd) { if (m_stDeviceList.nDeviceNum == 0 || deviceIpAdd=="") { return; } currentCameraInfor = mapOfCameraDevice[deviceIpAdd]; // ch:获取选择的设备信息 | en:Get selected device information MyCamera.MV_CC_DEVICE_INFO device =(MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(mapOfCameraDevice[deviceIpAdd].pDeviceInfo, typeof(MyCamera.MV_CC_DEVICE_INFO)); // ch:打开设备 | en:Open device if (null == m_MyCamera) { m_MyCamera = new MyCamera(); if (null == m_MyCamera) { return; } } int nRet = m_MyCamera.MV_CC_CreateDevice_NET(ref device); if (MyCamera.MV_OK != nRet) { return; } nRet = m_MyCamera.MV_CC_OpenDevice_NET(); if (MyCamera.MV_OK != nRet) { m_MyCamera.MV_CC_DestroyDevice_NET(); return; } // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera) if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE) { int nPacketSize = m_MyCamera.MV_CC_GetOptimalPacketSize_NET(); if (nPacketSize > 0) { nRet = m_MyCamera.MV_CC_SetIntValue_NET("GevSCPSPacketSize", (uint)nPacketSize); if (nRet != MyCamera.MV_OK) { } } else { } } // ch:设置采集连续模式 | en:Set Continues Aquisition Mode m_MyCamera.MV_CC_SetEnumValue_NET("AcquisitionMode", (uint)MyCamera.MV_CAM_ACQUISITION_MODE.MV_ACQ_MODE_CONTINUOUS); m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF); // ch:获取参数 | en:Get parameters MyCamera.MVCC_FLOATVALUE stParam = new MyCamera.MVCC_FLOATVALUE(); nRet = m_MyCamera.MV_CC_GetFloatValue_NET("ExposureTime", ref stParam); if (MyCamera.MV_OK == nRet) { mapOfCameraDevice[deviceIpAdd].ExposureTime = stParam.fCurValue; } nRet = m_MyCamera.MV_CC_GetFloatValue_NET("Gain", ref stParam); if (MyCamera.MV_OK == nRet) { mapOfCameraDevice[deviceIpAdd].Gain = stParam.fCurValue; } nRet = m_MyCamera.MV_CC_GetFloatValue_NET("ResultingFrameRate", ref stParam); if (MyCamera.MV_OK == nRet) { mapOfCameraDevice[deviceIpAdd].ResultingFrameRate = stParam.fCurValue; } // ch:控件操作 | en:Control operation } //关闭设备 public void CloseDevice() { // ch:取流标志位清零 | en:Reset flow flag bit if (m_bGrabbing == true) { m_bGrabbing = false; m_hReceiveThread.Join(); } if (m_BufForDriver != IntPtr.Zero) { Marshal.Release(m_BufForDriver); } // ch:关闭设备 | en:Close Device m_MyCamera.MV_CC_CloseDevice_NET(); m_MyCamera.MV_CC_DestroyDevice_NET(); } //设置为连续模式,关闭触发方式 public void SetContinuesMode() { m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF); currentCameraInfor.isOpenTriggerMode = false; } public void OpenTiggerMode() { m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON); currentCameraInfor.isOpenTriggerMode = true; } public void SetTiggerMode(int tiggerMode) { m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON); currentCameraInfor.isOpenTriggerMode = true; // ch:触发源选择:0 - Line0; | en:Trigger source select:0 - Line0; // 1 - Line1; // 2 - Line2; // 3 - Line3; // 4 - Counter; // 7 - Software; m_MyCamera.MV_CC_SetEnumValue_NET("TriggerSource",(uint) tiggerMode); } public void CaptureFile(ProcessBussess proc) { this.callback = proc; m_bGrabbing = true; m_hReceiveThread = new Thread(ReceiveThreadProcess); m_hReceiveThread.Start(); m_stFrameInfo.nFrameLen = 0;//取流之前先清除帧长度 m_stFrameInfo.enPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Undefined; // ch:开始采集 | en:Start Grabbing int nRet = m_MyCamera.MV_CC_StartGrabbing_NET(); if (MyCamera.MV_OK != nRet) { m_bGrabbing = false; m_hReceiveThread.Join(); return; } } public void ReceiveThreadProcess() { MyCamera.MV_FRAME_OUT stFrameInfo = new MyCamera.MV_FRAME_OUT(); MyCamera.MV_DISPLAY_FRAME_INFO stDisplayInfo = new MyCamera.MV_DISPLAY_FRAME_INFO(); int nRet = MyCamera.MV_OK; while (m_bGrabbing) { nRet = m_MyCamera.MV_CC_GetImageBuffer_NET(ref stFrameInfo, 1000);//获取一帧图片 并将图片信息保留在stFrameInfo中,超时时间设置为1秒 加的这一秒就是等待上一个线程抓取完毕 if (nRet == MyCamera.MV_OK) { lock (BufForDriverLock) //同步互斥操作 { if (m_BufForDriver == IntPtr.Zero || stFrameInfo.stFrameInfo.nFrameLen > m_nBufSizeForDriver) //帧长度大于0 { if (m_BufForDriver != IntPtr.Zero) { Marshal.Release(m_BufForDriver); m_BufForDriver = IntPtr.Zero; } m_BufForDriver = Marshal.AllocHGlobal((Int32)stFrameInfo.stFrameInfo.nFrameLen); if (m_BufForDriver == IntPtr.Zero) { return; } m_nBufSizeForDriver = stFrameInfo.stFrameInfo.nFrameLen; } m_stFrameInfo = stFrameInfo.stFrameInfo; CopyMemory(m_BufForDriver, stFrameInfo.pBufAddr, stFrameInfo.stFrameInfo.nFrameLen); } if (RemoveCustomPixelFormats(stFrameInfo.stFrameInfo.enPixelType)) { m_MyCamera.MV_CC_FreeImageBuffer_NET(ref stFrameInfo); continue; } stDisplayInfo.hWnd = hWnd_pictureBox; stDisplayInfo.pData = stFrameInfo.pBufAddr; stDisplayInfo.nDataLen = stFrameInfo.stFrameInfo.nFrameLen; stDisplayInfo.nWidth = stFrameInfo.stFrameInfo.nWidth; stDisplayInfo.nHeight = stFrameInfo.stFrameInfo.nHeight; stDisplayInfo.enPixelType = stFrameInfo.stFrameInfo.enPixelType;//像素格式 if (isDisplayOnPictureBox)//初始化完成之后 { m_MyCamera.MV_CC_DisplayOneFrame_NET(ref stDisplayInfo);//显示一帧图像 将这帧图像的信息保存在变量中 } Mat dst1 = new Mat(stDisplayInfo.nHeight, stDisplayInfo.nWidth,MatType.CV_8UC3, m_BufForDriver); callback(dst1); //回调函数 识别 /*Rect rectangle = new Rect(20, 20, 300, 300); Cv2.Rectangle(dst1, rectangle, new Scalar(0, 0, 255), 2, LineTypes.AntiAlias); //dst1.SaveImage("D:\\333.jpg"); */ m_MyCamera.MV_CC_FreeImageBuffer_NET(ref stFrameInfo); } else { if (currentCameraInfor.isOpenTriggerMode) { Thread.Sleep(5); } } } } // ch:去除自定义的像素格式 | en:Remove custom pixel formats private bool RemoveCustomPixelFormats(MyCamera.MvGvspPixelType enPixelFormat) { Int32 nResult = ((int)enPixelFormat) & (unchecked((Int32)0x80000000)); if (0x80000000 == nResult) { return true; } else { return false; } } public bool SetCameraParam() { m_MyCamera.MV_CC_SetEnumValue_NET("ExposureAuto", 0); int nRet = m_MyCamera.MV_CC_SetFloatValue_NET("ExposureTime", aSCameraParamInfor.ExposureTime); if (nRet != MyCamera.MV_OK) { return false; } m_MyCamera.MV_CC_SetEnumValue_NET("GainAuto", 0); nRet = m_MyCamera.MV_CC_SetFloatValue_NET("Gain", aSCameraParamInfor.Gain); if (nRet != MyCamera.MV_OK) { return false; } nRet = m_MyCamera.MV_CC_SetFloatValue_NET("AcquisitionFrameRate", aSCameraParamInfor.FrameRate); if (nRet != MyCamera.MV_OK) { return false; } return true; } public void SavePNG() { if (false == m_bGrabbing) { return; } if (RemoveCustomPixelFormats(m_stFrameInfo.enPixelType)) { return; } MyCamera.MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam = new MyCamera.MV_SAVE_IMG_TO_FILE_PARAM(); lock (BufForDriverLock) { if (m_stFrameInfo.nFrameLen == 0) { return; } stSaveFileParam.enImageType = MyCamera.MV_SAVE_IAMGE_TYPE.MV_Image_Png; stSaveFileParam.enPixelType = m_stFrameInfo.enPixelType; stSaveFileParam.pData = m_BufForDriver; stSaveFileParam.nDataLen = m_stFrameInfo.nFrameLen; stSaveFileParam.nHeight = m_stFrameInfo.nHeight; stSaveFileParam.nWidth = m_stFrameInfo.nWidth; stSaveFileParam.nQuality = 8; stSaveFileParam.iMethodValue = 2; if (picSavePath == "") { picSavePath = System.Environment.CurrentDirectory + "\\Image\\"; } stSaveFileParam.pImagePath = picSavePath + "Image_w" + stSaveFileParam.nWidth.ToString() + "_h" + stSaveFileParam.nHeight.ToString() + "_fn" + m_stFrameInfo.nFrameNum.ToString() + ".png"; int nRet = m_MyCamera.MV_CC_SaveImageToFile_NET(ref stSaveFileParam); if (MyCamera.MV_OK != nRet) { return; } } } public void SaveTiff() { if (false == m_bGrabbing) { return; } if (RemoveCustomPixelFormats(m_stFrameInfo.enPixelType)) { return; } MyCamera.MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam = new MyCamera.MV_SAVE_IMG_TO_FILE_PARAM(); lock (BufForDriverLock) { if (m_stFrameInfo.nFrameLen == 0) { return; } stSaveFileParam.enImageType = MyCamera.MV_SAVE_IAMGE_TYPE.MV_Image_Tif; stSaveFileParam.enPixelType = m_stFrameInfo.enPixelType; stSaveFileParam.pData = m_BufForDriver; stSaveFileParam.nDataLen = m_stFrameInfo.nFrameLen; stSaveFileParam.nHeight = m_stFrameInfo.nHeight; stSaveFileParam.nWidth = m_stFrameInfo.nWidth; stSaveFileParam.iMethodValue = 2; if (picSavePath == "") { picSavePath = System.Environment.CurrentDirectory + "\\Image\\"; } stSaveFileParam.pImagePath = picSavePath + "Image_w" + stSaveFileParam.nWidth.ToString() + "_h" + stSaveFileParam.nHeight.ToString() + "_fn" + m_stFrameInfo.nFrameNum.ToString() + ".tif"; int nRet = m_MyCamera.MV_CC_SaveImageToFile_NET(ref stSaveFileParam); if (MyCamera.MV_OK != nRet) { return; } } } public void SaveBMP() { if (false == m_bGrabbing) { return; } if (RemoveCustomPixelFormats(m_stFrameInfo.enPixelType)) { return; } MyCamera.MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam = new MyCamera.MV_SAVE_IMG_TO_FILE_PARAM(); lock (BufForDriverLock) { if (m_stFrameInfo.nFrameLen == 0) { return; } stSaveFileParam.enImageType = MyCamera.MV_SAVE_IAMGE_TYPE.MV_Image_Bmp; stSaveFileParam.enPixelType = m_stFrameInfo.enPixelType; stSaveFileParam.pData = m_BufForDriver; stSaveFileParam.nDataLen = m_stFrameInfo.nFrameLen; stSaveFileParam.nHeight = m_stFrameInfo.nHeight; stSaveFileParam.nWidth = m_stFrameInfo.nWidth; stSaveFileParam.iMethodValue = 2; if (picSavePath == "") { picSavePath = System.Environment.CurrentDirectory + "\\Image\\"; } stSaveFileParam.pImagePath = picSavePath + "Image_w" + stSaveFileParam.nWidth.ToString() + "_h" + stSaveFileParam.nHeight.ToString() + "_fn" + m_stFrameInfo.nFrameNum.ToString() + ".bmp"; int nRet = m_MyCamera.MV_CC_SaveImageToFile_NET(ref stSaveFileParam); if (MyCamera.MV_OK != nRet) { return; } } } public void SaveJPG(string path) { //if (false == m_bGrabbing) //{ // return; //} //if (RemoveCustomPixelFormats(m_stFrameInfo.enPixelType)) //{ // return; //} //MyCamera.MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam = new MyCamera.MV_SAVE_IMG_TO_FILE_PARAM(); //lock (BufForDriverLock) //{ // if (m_stFrameInfo.nFrameLen == 0) // { // return; // } // stSaveFileParam.enImageType = MyCamera.MV_SAVE_IAMGE_TYPE.MV_Image_Jpeg; // stSaveFileParam.enPixelType = m_stFrameInfo.enPixelType; // stSaveFileParam.pData = m_BufForDriver; // stSaveFileParam.nDataLen = m_stFrameInfo.nFrameLen; // stSaveFileParam.nHeight = m_stFrameInfo.nHeight; // stSaveFileParam.nWidth = m_stFrameInfo.nWidth; // stSaveFileParam.nQuality = 80; // stSaveFileParam.iMethodValue = 2; // if (picSavePath == "" || picSavePath==null) // { // picSavePath = System.Environment.CurrentDirectory + "\\Image\\"; // } // stSaveFileParam.pImagePath = picSavePath + "Image_w" + stSaveFileParam.nWidth.ToString() + "_h" + stSaveFileParam.nHeight.ToString() + "_fn" + m_stFrameInfo.nFrameNum.ToString() + ".jpg"; // int nRet = m_MyCamera.MV_CC_SaveImageToFile_NET(ref stSaveFileParam); // //Mat dst1 = new Mat(stSaveFileParam.nHeight, stSaveFileParam.nWidth,MatType.CV_8UC3, m_BufForDriver); // //dst1.SaveImage("D:\\111.jpg"); // if (MyCamera.MV_OK != nRet) // { // return; // } //} if (false == m_bGrabbing) { return; } if (RemoveCustomPixelFormats(m_stFrameInfo.enPixelType)) { return; } MyCamera.MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam = new MyCamera.MV_SAVE_IMG_TO_FILE_PARAM(); lock (BufForDriverLock) { if (m_stFrameInfo.nFrameLen == 0) { return; } stSaveFileParam.enImageType = MyCamera.MV_SAVE_IAMGE_TYPE.MV_Image_Jpeg; stSaveFileParam.enPixelType = m_stFrameInfo.enPixelType; stSaveFileParam.pData = m_BufForDriver; stSaveFileParam.nDataLen = m_stFrameInfo.nFrameLen; stSaveFileParam.nHeight = m_stFrameInfo.nHeight; stSaveFileParam.nWidth = m_stFrameInfo.nWidth; stSaveFileParam.nQuality = 80; stSaveFileParam.iMethodValue = 2; if (string.IsNullOrEmpty(path)) stSaveFileParam.pImagePath = System.Environment.CurrentDirectory + "\\Image\\" + picSavePath + "Image_w" + stSaveFileParam.nWidth.ToString() + "_h" + stSaveFileParam.nHeight.ToString() + "_fn" + m_stFrameInfo.nFrameNum.ToString() + ".jpg"; else { stSaveFileParam.pImagePath = path; } int nRet = m_MyCamera.MV_CC_SaveImageToFile_NET(ref stSaveFileParam); //Mat dst1 = new Mat(stSaveFileParam.nHeight, stSaveFileParam.nWidth,MatType.CV_8UC3, m_BufForDriver); //dst1.SaveImage("D:\\111.jpg"); if (MyCamera.MV_OK != nRet) { return; } } } static bool IsMonoPixelFormat(MyCamera.MvGvspPixelType enType) { switch (enType) { case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8: case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10: case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10_Packed: case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12: case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12_Packed: return true; default: return false; } } }}