WinForm之海康面阵相机SDK的使用-实现打开相机
38WinForm之海康面阵相机SDK的使用-实现打开相机
添加引用
// 打开相机流程:
// 1.获取所有相机
// 2.根据相机序列号,看该序列号对应的相机ID是否存在,确定该相机是否存在
// 3.获取相机信息
// 4.建立设备对象
// 5.根据相机信息创建相机
// 6.打开设备
// 7.探测网络最佳包大小,并进行设置(只对GigE相机有效)
// 8.设置为连续采集模式
// 9.注册异常回调函数
一打开相机
/// <summary>
/// 打开相机
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_Open_Click(object sender, EventArgs e)
{
OpenDevice();
}
相机相关变量
/// <summary>
/// 相机图像(bipmap格式)
/// </summary>
Bitmap m_bitmap = null;
/// <summary>
/// 相机是否已连接
/// </summary>
public bool IsConnect { get; private set; }
/// <summary>
/// 是否开始采集
/// </summary>
public bool isGrabbing { get; private set; }
/// <summary>
/// 是否为触发模式
/// </summary>
public bool isTriggerMode { get; private set; }
// 设备列表
private MyCamera.MV_CC_DEVICE_INFO_LIST m_stDeviceList;
// 相机对象
private MyCamera m_MyCamera = null;
// 取像线程
Thread m_hReceiveThread = null;
// 帧信息
MyCamera.MV_FRAME_OUT_INFO_EX m_stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX();
// 用于从驱动获取图像的缓存
IntPtr m_BufForDriver;
UInt32 m_nBufSizeForDriver = 0;
// R通道数据
byte[] m_pDataForRed = null;
// G通道数据
byte[] m_pDataForGreen = null;
// B通道数据
byte[] m_pDataForBlue = null;
// 读写图像时锁定
private Object BufForDriverLock = new Object();
private Object BufForImageLock = new Object();
枚举海康相机
// 枚举海康相机(GIGE,USB3)
public void EnumDevices()
{
// 枚举设备列表
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)
{
MessageBox.Show("枚举HIK相机设备失败!");
return;
}
}
获取相机对应的枚举索引
// 获取相机对应的枚举索引
private int GetDeviceIndex(string CameraID)
{
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));
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));
if (gigeInfo.chSerialNumber == CameraID)
return i;
}
else if (device.nTLayerType == MyCamera.MV_USB_DEVICE)
{
MyCamera.MV_USB3_DEVICE_INFO usb3Info = (MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO));
if (usb3Info.chSerialNumber == CameraID)
return i;
}
}
return -1;
}
异常,则关闭相机
/// <summary>
/// 异常,则关闭相机
/// </summary>
/// <param name="nMsgType"></param>
/// <param name="pUser"></param>
private void cbException(uint nMsgType, IntPtr pUser)
{
IsConnect = false;
if (nMsgType == MyCamera.MV_EXCEPTION_DEV_DISCONNECT)
{
// 先关闭设备
CloseDevice();
// 在尝试重新打开设备
if (OpenDevice())
{
MessageBox.Show("尝试重新连接设备失败!");
}
}
}
关闭设备
/// <summary>
/// 关闭设备
/// </summary>
public void CloseDevice()
{
// 取流标志位清零
if (isGrabbing == true)
{
isGrabbing = false;
m_hReceiveThread.Join();
}
if (m_BufForDriver != IntPtr.Zero)
{
Marshal.Release(m_BufForDriver);
}
// 关闭设备
m_MyCamera.MV_CC_CloseDevice_NET();
m_MyCamera.MV_CC_DestroyDevice_NET();
IsConnect = false;
}
打开相机
/// <summary>
/// 打开相机
/// </summary>
/// <returns></returns>
public bool OpenDevice()
{
// 枚举并搜索指定ID的相机是否存在
EnumDevices();
// 获取相机索引
int camIdx = GetDeviceIndex(txt_CameraSeriesNum.Text.Trim());
if (camIdx == -1)
{
MessageBox.Show("找不到该ID的相机!");
return false;
}
MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[camIdx],
typeof(MyCamera.MV_CC_DEVICE_INFO));
// 建立设备对象
if (null == m_MyCamera)
{
m_MyCamera = new MyCamera();
if (null == m_MyCamera)
{
MessageBox.Show("初始化相机对象失败");
return false;
}
}
// 创建设备
int nRet = m_MyCamera.MV_CC_CreateDevice_NET(ref device);
if (MyCamera.MV_OK != nRet)
{
MessageBox.Show($"创建设备失败,失败代码:{nRet}");
return false;
}
// 尝试打开设备
nRet = m_MyCamera.MV_CC_OpenDevice_NET();
if (MyCamera.MV_OK != nRet)
{
m_MyCamera.MV_CC_DestroyDevice_NET();
MessageBox.Show($"设备打开失败,失败代码:{nRet}");
return false;
}
// 探测网络最佳包大小(只对GigE相机有效)
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)
{
MessageBox.Show($"设置包大小失败,失败代码:{nRet}");
}
}
else
{
MessageBox.Show($"获取包大小失败,返回的包大小为:{nPacketSize}");
}
}
// 设置采集连续模式
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);
// 注册异常回调
m_MyCamera.MV_CC_RegisterExceptionCallBack_NET(cbException, IntPtr.Zero);
IsConnect = true;
return true;
}
二 关闭设备
private void btn_Close_Click(object sender, EventArgs e)
{
CloseDevice();
}
三 开始采集
辅助函数
// 判断是否为黑白图像
private Boolean IsMonoData(MyCamera.MvGvspPixelType enGvspPixelType)
{
switch (enGvspPixelType)
{
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;
}
}
// 判断是否为彩色图像
private Boolean IsColorData(MyCamera.MvGvspPixelType enGvspPixelType)
{
switch (enGvspPixelType)
{
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_BayerGR10:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG10:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB10:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG10:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR12:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG12:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB12:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG12:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR10_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG10_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB10_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG10_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR12_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG12_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB12_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG12_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_YUV422_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_YUV422_YUYV_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_YCBCR411_8_CBYYCRYY:
return true;
default:
return false;
}
}
// 转换为RGB格式
private Int32 ConvertToRGB(object obj, IntPtr pSrc, ushort nHeight, ushort nWidth, MyCamera.MvGvspPixelType nPixelType, IntPtr pDst)
{
if (IntPtr.Zero == pSrc || IntPtr.Zero == pDst)
{
return MyCamera.MV_E_PARAMETER;
}
int nRet = MyCamera.MV_OK;
MyCamera device = obj as MyCamera;
MyCamera.MV_PIXEL_CONVERT_PARAM stPixelConvertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();
stPixelConvertParam.pSrcData = pSrc;//源数据
if (IntPtr.Zero == stPixelConvertParam.pSrcData)
{
return -1;
}
stPixelConvertParam.nWidth = nWidth;//图像宽度
stPixelConvertParam.nHeight = nHeight;//图像高度
stPixelConvertParam.enSrcPixelType = nPixelType;//源数据的格式
stPixelConvertParam.nSrcDataLen = (uint)(nWidth * nHeight * ((((uint)nPixelType) >> 16) & 0x00ff) >> 3);
stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * ((((uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed) >> 16) & 0x00ff) >> 3);
stPixelConvertParam.pDstBuffer = pDst;//转换后的数据
stPixelConvertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed;
stPixelConvertParam.nDstBufferSize = (uint)nWidth * nHeight * 3;
nRet = device.MV_CC_ConvertPixelType_NET(ref stPixelConvertParam);//格式转换
if (MyCamera.MV_OK != nRet)
{
return -1;
}
return MyCamera.MV_OK;
}
// 转换为Mono8格式
private Int32 ConvertToMono8(object obj, IntPtr pInData, IntPtr pOutData, ushort nHeight, ushort nWidth, MyCamera.MvGvspPixelType nPixelType)
{
if (IntPtr.Zero == pInData || IntPtr.Zero == pOutData)
{
return MyCamera.MV_E_PARAMETER;
}
int nRet = MyCamera.MV_OK;
MyCamera device = obj as MyCamera;
MyCamera.MV_PIXEL_CONVERT_PARAM stPixelConvertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();
stPixelConvertParam.pSrcData = pInData;//源数据
if (IntPtr.Zero == stPixelConvertParam.pSrcData)
{
return -1;
}
stPixelConvertParam.nWidth = nWidth;//图像宽度
stPixelConvertParam.nHeight = nHeight;//图像高度
stPixelConvertParam.enSrcPixelType = nPixelType;//源数据的格式
stPixelConvertParam.nSrcDataLen = (uint)(nWidth * nHeight * ((((uint)nPixelType) >> 16) & 0x00ff) >> 3);
stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * ((((uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed) >> 16) & 0x00ff) >> 3);
stPixelConvertParam.pDstBuffer = pOutData;//转换后的数据
stPixelConvertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8;
stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * 3);
nRet = device.MV_CC_ConvertPixelType_NET(ref stPixelConvertParam);//格式转换
if (MyCamera.MV_OK != nRet)
{
return -1;
}
return nRet;
}
/// <summary>
/// 灰度数据intptr转bitmap图像
/// </summary>
/// <param name="grayData"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
public Bitmap CreateGrayscaleBitmapFromIntPtr(IntPtr grayData, int width, int height)
{
// 创建一个8bpp的灰度位图
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
// 设置灰度调色板
ColorPalette palette = bitmap.Palette;
for (int i = 0; i < 256; i++)
{
palette.Entries[i] = Color.FromArgb(i, i, i);
}
bitmap.Palette = palette;
// 锁定位图数据
BitmapData bitmapData = bitmap.LockBits(
new Rectangle(0, 0, width, height),
ImageLockMode.WriteOnly,
bitmap.PixelFormat);
try
{
// 将灰度数据复制到位图
byte[] pixelData = new byte[width * height];
Marshal.Copy(grayData, pixelData, 0, pixelData.Length);
// 将数据复制到位图内存中
Marshal.Copy(pixelData, 0, bitmapData.Scan0, pixelData.Length);
}
finally
{
bitmap.UnlockBits(bitmapData);
}
return bitmap;
}
/// <summary>
/// RGB三通道数据转bitmap
/// </summary>
/// <param name="rPtr"></param>
/// <param name="gPtr"></param>
/// <param name="bPtr"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
public Bitmap CreateBitmapFromRGBPointers(IntPtr rPtr, IntPtr gPtr, IntPtr bPtr, int width, int height)
{
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
BitmapData bitmapData = bitmap.LockBits(
new Rectangle(0, 0, width, height),
ImageLockMode.WriteOnly,
bitmap.PixelFormat);
try
{
int bytesPerPixel = 3;
int pixelCount = width * height;
byte[] pixelData = new byte[pixelCount * bytesPerPixel];
// 将指针数据复制到托管数组
Marshal.Copy(rPtr, pixelData, 0 * pixelCount, pixelCount); // Red
Marshal.Copy(gPtr, pixelData, 1 * pixelCount, pixelCount); // Green
Marshal.Copy(bPtr, pixelData, 2 * pixelCount, pixelCount); // Blue
// 重新排列为BGR格式
for (int i = 0; i < pixelCount; i++)
{
byte temp = pixelData[i * 3]; // R
pixelData[i * 3] = pixelData[i * 3 + 2]; // B
pixelData[i * 3 + 2] = temp; // R
// G remains in the middle
}
// 复制到位图
Marshal.Copy(pixelData, 0, bitmapData.Scan0, pixelData.Length);
}
finally
{
bitmap.UnlockBits(bitmapData);
}
return bitmap;
}
开始采集
/// <summary>
/// 开始采集
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_StartGrab_Click(object sender, EventArgs e)
{
StartGrab();
}
/// <summary>
/// 开始采集图像
/// </summary>
/// <returns></returns>
public bool StartGrab()
{
// 标志位置位true
isGrabbing = true;
m_hReceiveThread = new Thread(ReceiveThreadProcess);
m_hReceiveThread.Start();
// 取流之前先清除帧长度
m_stFrameInfo.nFrameLen = 0;
m_stFrameInfo.enPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8;
// 开始采集
int nRet = m_MyCamera.MV_CC_StartGrabbing_NET();
if (MyCamera.MV_OK != nRet)
{
isGrabbing = false;
m_hReceiveThread.Join();
MessageBox.Show($"连续采集失败,失败代码:{nRet}");
return false;
}
return true;
}
取图线程(核心)
// 取像线程(核心)
private void ReceiveThreadProcess()
{
// 确保释放保存了旧图像数据的bitmap实例,用新图像宽高等信息new一个新的bitmap实例
if (null != m_bitmap)
{
m_bitmap.Dispose();
m_bitmap = null;
}
MyCamera.MVCC_INTVALUE stParam = new MyCamera.MVCC_INTVALUE();
int nRet = m_MyCamera.MV_CC_GetIntValue_NET("PayloadSize", ref stParam);
if (MyCamera.MV_OK != nRet)
{
MessageBox.Show($"读取PayloadSize失败,失败代码:{nRet}");
return;
}
UInt32 nPayloadSize = stParam.nCurValue;
// 获取图像高
nRet = m_MyCamera.MV_CC_GetIntValue_NET("Height", ref stParam);
if (MyCamera.MV_OK != nRet)
{
MessageBox.Show($"获取图像高失败,失败代码:{nRet}");
return;
}
uint nHeight = stParam.nCurValue;
// 获取图像宽
nRet = m_MyCamera.MV_CC_GetIntValue_NET("Width", ref stParam);
if (MyCamera.MV_OK != nRet)
{
MessageBox.Show($"获取图像宽失败,失败代码:{nRet}");
return;
}
uint nWidth = stParam.nCurValue;
// 根据图像大小设置图像缓存
m_pDataForRed = new byte[nWidth * nHeight];
m_pDataForGreen = new byte[nWidth * nHeight];
m_pDataForBlue = new byte[nWidth * nHeight];
if (3 * nPayloadSize > m_nBufSizeForDriver)
{
if (m_BufForDriver != IntPtr.Zero)
{
Marshal.Release(m_BufForDriver);
}
m_nBufSizeForDriver = 3 * nPayloadSize;
m_BufForDriver = Marshal.AllocHGlobal((Int32)m_nBufSizeForDriver);
}
if (m_BufForDriver == IntPtr.Zero)
{
return;
}
IntPtr pImageBuffer = Marshal.AllocHGlobal((int)nPayloadSize * 3);
if (pImageBuffer == IntPtr.Zero)
{
MessageBox.Show($"申请图像缓存区失败!");
return;
}
MyCamera.MV_FRAME_OUT_INFO_EX stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX();
IntPtr RedPtr = IntPtr.Zero;
IntPtr GreenPtr = IntPtr.Zero;
IntPtr BluePtr = IntPtr.Zero;
IntPtr pTemp = IntPtr.Zero;
DateTime ProStartTime = DateTime.MinValue;
while (isGrabbing)
{
lock (BufForDriverLock)
{
nRet = m_MyCamera.MV_CC_GetOneFrameTimeout_NET(m_BufForDriver, m_nBufSizeForDriver, ref stFrameInfo, 1000);
if (nRet == MyCamera.MV_OK)
{
ProStartTime = DateTime.Now;
// MessageBox.Show("相机取图完成,开始处理...");
m_stFrameInfo = stFrameInfo;
}
}
if (nRet == MyCamera.MV_OK)
{
// 彩色相机
if (IsColorData(stFrameInfo.enPixelType))
{
if (stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
{
pTemp = m_BufForDriver;
}
else
{
nRet = ConvertToRGB(m_MyCamera, m_BufForDriver, stFrameInfo.nHeight, stFrameInfo.nWidth, stFrameInfo.enPixelType, pImageBuffer);
if (MyCamera.MV_OK != nRet)
{
return;
}
pTemp = pImageBuffer;
}
unsafe
{
byte* pBufForSaveImage = (byte*)pTemp;
UInt32 nSupWidth = (stFrameInfo.nWidth + (UInt32)3) & 0xfffffffc;//5120
for (int nRow = 0; nRow < stFrameInfo.nHeight; nRow++)
{
for (int col = 0; col < stFrameInfo.nWidth; col++)
{
m_pDataForRed[nRow * nSupWidth + col] = pBufForSaveImage[nRow * stFrameInfo.nWidth * 3 + (3 * col)];
m_pDataForGreen[nRow * nSupWidth + col] = pBufForSaveImage[nRow * stFrameInfo.nWidth * 3 + (3 * col + 1)];
m_pDataForBlue[nRow * nSupWidth + col] = pBufForSaveImage[nRow * stFrameInfo.nWidth * 3 + (3 * col + 2)];
}
}
}
RedPtr = Marshal.UnsafeAddrOfPinnedArrayElement(m_pDataForRed, 0);
GreenPtr = Marshal.UnsafeAddrOfPinnedArrayElement(m_pDataForGreen, 0);
BluePtr = Marshal.UnsafeAddrOfPinnedArrayElement(m_pDataForBlue, 0);
//显示采集图像
lock (BufForImageLock)
{
m_bitmap = CreateBitmapFromRGBPointers(RedPtr, GreenPtr, BluePtr,stFrameInfo.nWidth, stFrameInfo.nHeight);
pictureBox1.Image = m_bitmap;
}
}
else if (true) // 黑白图像
{
if (stFrameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
{
pTemp = m_BufForDriver;
}
else
{
nRet = ConvertToMono8(m_MyCamera, m_BufForDriver, pImageBuffer, stFrameInfo.nHeight, stFrameInfo.nWidth, stFrameInfo.enPixelType);
if (MyCamera.MV_OK != nRet)
{
return;
}
pTemp = pImageBuffer;
}
// 显示采集图像
lock (BufForImageLock)
{
m_bitmap = CreateGrayscaleBitmapFromIntPtr(pTemp, stFrameInfo.nWidth, stFrameInfo.nHeight);
pictureBox1.Image = m_bitmap;
}
}
else
{
continue;
}
}
else
{
if (isTriggerMode)
{
Thread.Sleep(5);
}
}
}
}
关闭相机
/// <summary>
/// 关闭相机
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_Close_Click(object sender, EventArgs e)
{
// 关闭相机
CloseDevice();
}
窗口关闭,也进行相机的关闭
/// <summary>
/// 关闭相机
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
CloseDevice();
}