//在此之前可以先浏览我编写的通用上位机类,更方便理解
https://blog.csdn.net/m0_51559565/article/details/134403745
最近完成一个关于海康采图的demo,记录并说明用法。
先上代码。
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using MvCamCtrl.NET;
using HalconDotNet;
using System.Windows.Forms;
using WeldingInspection.MyCamer;
namespace WeldingInspection
{
public partial class HKCamerSDK: MyBaseCamera
{
//创建相机集合,用于存放相机
MyCamera.MV_CC_DEVICE_INFO_LIST m_pDeviceList;
MyCamera.MV_CC_DEVICE_INFO device;
MyCamera HKMyCamera = new MyCamera();
MyCamera.cbOutputExdelegate ImageCallback;
MyCamera.cbExceptiondelegate pCallBackFunc;
HObject hPylonImage = new HObject();
///
/// 查找并开启相机
///
/// 开启第几个相机
/// 开启成功返回true,开启失败返回false,并在输出打印错误码
public override List<string> FindCamer()
{
int nRet;
List<string> CamerName = new List<string>();
// ch:创建设备列表 en:Create Device List
System.GC.Collect();
nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_pDeviceList);
if (0 != nRet)
{
Console.WriteLine("NOT find Camer! {0:x8}", nRet);
return null;
}
//在输出打印相机名称
for (int i = 0; i < m_pDeviceList.nDeviceNum; i++)
{
MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_pDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
//网口相机
if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
{
IntPtr buffer = Marshal.UnsafeAddrOfPinnedArrayElement(device.SpecialInfo.stGigEInfo, 0);
MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)Marshal.PtrToStructure(buffer, typeof(MyCamera.MV_GIGE_DEVICE_INFO));
if (gigeInfo.chUserDefinedName != "")
{
Console.WriteLine("GigE: " + gigeInfo.chUserDefinedName + " (" + gigeInfo.chSerialNumber + ")");
CamerName.Add(gigeInfo.chSerialNumber);
}
else
{
Console.WriteLine("GigE: " + gigeInfo.chManufacturerName + " " + gigeInfo.chModelName + " (" + gigeInfo.chSerialNumber + ")");
}
}
//usb接口相机
else if (device.nTLayerType == MyCamera.MV_USB_DEVICE)
{
IntPtr buffer = Marshal.UnsafeAddrOfPinnedArrayElement(device.SpecialInfo.stUsb3VInfo, 0);
MyCamera.MV_USB3_DEVICE_INFO usbInfo = (MyCamera.MV_USB3_DEVICE_INFO)Marshal.PtrToStructure(buffer, typeof(MyCamera.MV_USB3_DEVICE_INFO));
if (usbInfo.chUserDefinedName != "")
{
Console.WriteLine("USB: " + usbInfo.chUserDefinedName + " (" + usbInfo.chSerialNumber + ")");
}
else
{
Console.WriteLine("USB: " + usbInfo.chManufacturerName + " " + usbInfo.chModelName + " (" + usbInfo.chSerialNumber + ")");
}
}
}
if (m_pDeviceList.nDeviceNum == 0)
{
Console.WriteLine("未找到USB或者网口相机", 0);
return null;
}
return CamerName;
}
///
/// 根据相机序列号开启相机
///
///
///
public override bool OpenCamer(string CamerSerialization)
{
int nRet = 0;
for (int i = 0; i < m_pDeviceList.pDeviceInfo.Length; i++)
{
device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_pDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
IntPtr buffer = Marshal.UnsafeAddrOfPinnedArrayElement(device.SpecialInfo.stGigEInfo, 0);
MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)Marshal.PtrToStructure(buffer, typeof(MyCamera.MV_GIGE_DEVICE_INFO));
if (gigeInfo.chSerialNumber== CamerSerialization)
{
break;
}
}
// ch:打开设备 | en:Open device
if (null == HKMyCamera)
{
HKMyCamera = new MyCamera();
if (null == HKMyCamera)
{
return false;
}
}
//循环执行1000次,用于连接相机
for (int i = 0; i < 10; i++)
{
nRet = HKMyCamera.MV_CC_CreateDevice_NET(ref device);
if (MyCamera.MV_OK != nRet)
{
Thread.Sleep(1);
//输出错误信息
if (i == 9) Console.WriteLine("创建相机失败! {0:x8}", nRet);
continue;
}
nRet = HKMyCamera.MV_CC_OpenDevice_NET();
if (MyCamera.MV_OK != nRet)
{
HKMyCamera.MV_CC_DestroyDevice_NET();
Thread.Sleep(10);
if (i == 9) Console.WriteLine("打开相机失败! {0:x8}", nRet);
}
else
{
break;
}
}
// ch:探测网络最佳包大小(只对GigE相机有效)
if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
{
int nPacketSize = HKMyCamera.MV_CC_GetOptimalPacketSize_NET();
if (nPacketSize > 0)
{
nRet = HKMyCamera.MV_CC_SetIntValue_NET("GevSCPSPacketSize", (uint)nPacketSize);
if (nRet != MyCamera.MV_OK)
{
Console.WriteLine("Warning: Set Packet Size failed {0:x8}", nRet);
}
}
else
{
Console.WriteLine("Warning: Get Packet Size failed {0:x8}", nPacketSize);
}
}
//注册断线重连回调函数
nRet = HKMyCamera.MV_CC_RegisterExceptionCallBack_NET(pCallBackFunc, IntPtr.Zero);
if (MyCamera.MV_OK != nRet)
{
//ShowErrorMsg("Register expection callback failed!", nRet);
return false;
}
GC.KeepAlive(pCallBackFunc);
//注册图像回调函数
ImageCallback = new MyCamera.cbOutputExdelegate(ImageCallbackFunc);
nRet = HKMyCamera.MV_CC_RegisterImageCallBackEx_NET(ImageCallback, IntPtr.Zero);
if (MyCamera.MV_OK != nRet)
{
Console.WriteLine("Register image callback failed!");
return false;
}
//设置开启触发模式
HKMyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON);
//设置开启软触发模式
HKMyCamera.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_SOFTWARE);
//使用软触发命令
nRet = HKMyCamera.MV_CC_SetCommandValue_NET("TriggerSoftware");
if (MyCamera.MV_OK != nRet)
{
m_bGrabbing = false;
// ShowErrorMsg("Start Grabbing Fail!", nRet);
return false;
}
// ch:开始采集
nRet = HKMyCamera.MV_CC_StartGrabbing_NET();
if (MyCamera.MV_OK != nRet)
{
Console.WriteLine("开启相机采集失败! {0:x8}", nRet);
return false;
}
Console.WriteLine("开启相机成功");
return true;
}
///
/// 相机重连开启
///
///
///
public override bool ReconnectCamer(string CamerName)
{
CloseCamer( );
for (int j = 0; j < m_pDeviceList.pDeviceInfo.Length; j++)
{
device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_pDeviceList.pDeviceInfo[j], typeof(MyCamera.MV_CC_DEVICE_INFO));
IntPtr buffer = Marshal.UnsafeAddrOfPinnedArrayElement(device.SpecialInfo.stGigEInfo, 0);
MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)Marshal.PtrToStructure(buffer, typeof(MyCamera.MV_GIGE_DEVICE_INFO));
if (gigeInfo.chSerialNumber == CamerName)
{
break;
}
}
int i = 0;
// ch:打开设备 | en:Open Device
while (true)
{
int nRet = HKMyCamera.MV_CC_CreateDevice_NET(ref device);
i++;
//防止重连超时卡死
if (i > 10000)
{
Console.WriteLine("相机重连超时,请检查相机安装");
nRet = -1;
break;
}
if (MyCamera.MV_OK != nRet)
{
Thread.Sleep(5);
continue;
}
nRet = HKMyCamera.MV_CC_OpenDevice_NET();
if (MyCamera.MV_OK != nRet)
{
Thread.Sleep(5);
HKMyCamera.MV_CC_DestroyDevice_NET();
continue;
}
else
{
//nRet = InitCamera();
if (MyCamera.MV_OK != nRet)
{
Thread.Sleep(5);
HKMyCamera.MV_CC_DestroyDevice_NET();
continue;
}
break;
}
}
return true;
}
///
/// 清空相机
///
public override bool CloseCamer()
{
// ch:停止采集 | en:Stop Grabbing
HKMyCamera.MV_CC_StopGrabbing_NET();
// ch:关闭设备 | en:Close Device
HKMyCamera.MV_CC_CloseDevice_NET();
HKMyCamera.MV_CC_DestroyDevice_NET();
return true;
}
//回调图像传出变量
void ImageCallbackFunc(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pUser)
{
Halcon_Image(pData, ref pFrameInfo, pUser);
}
public void Halcon_Image(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pUser)
{
int nRet;
HObject img = new HObject();
IntPtr pImageBuf = IntPtr.Zero;
int nImageBufSize = 0;
IntPtr pTemp = IntPtr.Zero;
if (IsColorPixelFormat(pFrameInfo.enPixelType))
{
if (pFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
{
pTemp = pData;
}
else
{
if (IntPtr.Zero == pImageBuf || nImageBufSize < (pFrameInfo.nWidth * pFrameInfo.nHeight * 3))
{
if (pImageBuf != IntPtr.Zero)
{
Marshal.FreeHGlobal(pImageBuf);
pImageBuf = IntPtr.Zero;
}
pImageBuf = Marshal.AllocHGlobal((int)pFrameInfo.nWidth * pFrameInfo.nHeight * 3);
if (IntPtr.Zero == pImageBuf)
{
Console.WriteLine("图像采集为空");
return;
}
nImageBufSize = pFrameInfo.nWidth * pFrameInfo.nHeight * 3;
}
MyCamera.MV_PIXEL_CONVERT_PARAM stPixelConvertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();
stPixelConvertParam.pSrcData = pData;//源数据
stPixelConvertParam.nWidth = pFrameInfo.nWidth;//图像宽度
stPixelConvertParam.nHeight = pFrameInfo.nHeight;//图像高度
stPixelConvertParam.enSrcPixelType = pFrameInfo.enPixelType;//源数据的格式
stPixelConvertParam.nSrcDataLen = pFrameInfo.nFrameLen;
stPixelConvertParam.nDstBufferSize = (uint)nImageBufSize;
stPixelConvertParam.pDstBuffer = pImageBuf;//转换后的数据
stPixelConvertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed;
nRet = HKMyCamera.MV_CC_ConvertPixelType_NET(ref stPixelConvertParam);//格式转换
if (MyCamera.MV_OK != nRet)
{
Console.WriteLine("图像转换异常");
return;
}
pTemp = stPixelConvertParam.pDstBuffer;
}
try
{
HOperatorSet.GenImageInterleaved(out hPylonImage, (HTuple)pTemp, (HTuple)"rgb", (HTuple)pFrameInfo.nWidth, (HTuple)pFrameInfo.nHeight, -1, "byte", 0, 0, 0, 0, -1, 0);
}
catch (System.Exception ex)
{
Console.WriteLine(ex.ToString());
return;
}
}
else if (IsMonoPixelFormat(pFrameInfo.enPixelType))
{
if (pFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
{
pTemp = pData;
}
else
{
if (IntPtr.Zero == pImageBuf || nImageBufSize < (pFrameInfo.nWidth * pFrameInfo.nHeight))
{
if (pImageBuf != IntPtr.Zero)
{
Marshal.FreeHGlobal(pImageBuf);
pImageBuf = IntPtr.Zero;
}
pImageBuf = Marshal.AllocHGlobal((int)pFrameInfo.nWidth * pFrameInfo.nHeight);
if (IntPtr.Zero == pImageBuf)
{
Console.WriteLine("图像采集为空");
return;
}
nImageBufSize = pFrameInfo.nWidth * pFrameInfo.nHeight;
}
MyCamera.MV_PIXEL_CONVERT_PARAM stPixelConvertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();
stPixelConvertParam.pSrcData = pData;//源数据
stPixelConvertParam.nWidth = pFrameInfo.nWidth;//图像宽度
stPixelConvertParam.nHeight = pFrameInfo.nHeight;//图像高度
stPixelConvertParam.enSrcPixelType = pFrameInfo.enPixelType;//源数据的格式
stPixelConvertParam.nSrcDataLen = pFrameInfo.nFrameLen;
stPixelConvertParam.nDstBufferSize = (uint)nImageBufSize;
stPixelConvertParam.pDstBuffer = pImageBuf;//转换后的数据
stPixelConvertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8;
nRet = HKMyCamera.MV_CC_ConvertPixelType_NET(ref stPixelConvertParam);//格式转换
if (MyCamera.MV_OK != nRet)
{
Console.WriteLine("图像转换异常");
return;
}
pTemp = stPixelConvertParam.pDstBuffer; ;
}
try
{
HOperatorSet.GenImage1Extern(out hPylonImage, "byte", pFrameInfo.nWidth, pFrameInfo.nHeight, pTemp, IntPtr.Zero);
}
catch (System.Exception ex)
{
MessageBox.Show(ex.ToString());
return;
}
}
else
{
Console.WriteLine("采集图像格式错误,不为彩色和黑白图像");
return;
}
try
{
GetImage(hPylonImage);//调用相机基类的函数,用于传出变量
hPylonImage.Dispose();
m_bGrabbing = false;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return;
}
}
///
/// 设置相机曝光
///
///
///
public override bool SetExposureTime(int ExposureTime)
{
try
{
HKMyCamera.MV_CC_SetEnumValue_NET("ExposureAuto", 0);
int nRet = HKMyCamera.MV_CC_SetFloatValue_NET("ExposureTime", ExposureTime);
if (nRet != MyCamera.MV_OK)
{
return false;
}
return true;
}
catch (Exception)
{
return false;
}
}
///
/// 设置Gige相机心跳时间
///
///
public void SetHeartBeatTime(uint value)
{
try
{
//判断是否是网口相机
if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
{
HKMyCamera.MV_CC_SetHeartBeatTimeout_NET(value);
}
}
catch (Exception)
{
}
}
bool m_bGrabbing = false;
///
/// 单张采集
///
public override bool OneGrap()
{
try
{
if (m_bGrabbing)
{
Console.WriteLine("Camera is now Continue Grabing Images!");
return false;
}
else
{
// ch:标志位置位true | en:Set position bit true
m_bGrabbing = true;
//设置开启触发模式
HKMyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON);
//设置开启软触发模式
HKMyCamera.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_SOFTWARE);
int nRet;
//使用软触发命令
nRet = HKMyCamera.MV_CC_SetCommandValue_NET("TriggerSoftware");
if (MyCamera.MV_OK != nRet)
{
m_bGrabbing = false;
return false;
}
// HeventSlim.Wait();
return true;
}
}
catch (Exception)
{
return false;
}
}
///
/// 开启硬触发
///
///
public override bool EncoderGrap()
{
try
{
if (m_bGrabbing)
{
Console.WriteLine("Camera is now Continue Grabing Images!");
return false;
}
else
{
// ch:标志位置位true | en:Set position bit true
m_bGrabbing = true;
HKMyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON);
//设置使用硬触发模式
int nRet = HKMyCamera.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_LINE0);
if (MyCamera.MV_OK != nRet)
{
m_bGrabbing = false;
return false;
}
return true;
}
}
catch (Exception)
{
return false;
}
}
///
/// 判断是否为彩色图像
///
///
///
private bool IsColorPixelFormat(MyCamera.MvGvspPixelType enType)
{
switch (enType)
{
case MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BGR8_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_RGBA8_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BGRA8_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_YUV422_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_YUV422_YUYV_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR8:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG8:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB8:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB10:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB10_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG10:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG10_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG10:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG10_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR10:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR10_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB12:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB12_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG12:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG12_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG12:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG12_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR12:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR12_Packed:
return true;
default:
return false;
}
}
///
/// 判断图像是否是黑白图像
///
///
///
private 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;
}
}
}
}
整体过程其实比较简单,由于海康相机没有像一些品牌相机一样可以直接使用序列号开启相机,所以我们需要通过对相机信息进行索引,根据索引到的相机序列号来开启相机。
如何显示图像:可以访问我文章开头的博客,通过编写相机通用类即可,程序中是有继承通用类,通过访问相机通用的获取图像数据,从而传输图像数据。具体可以查看相机通用类。