505 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			505 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using MvCamCtrl.NET;
 | ||
| using Sunny.UI;
 | ||
| using System;
 | ||
| using System.Collections.Generic;
 | ||
| using System.Drawing;
 | ||
| using System.Drawing.Imaging;
 | ||
| using System.IO;
 | ||
| using System.Linq;
 | ||
| using System.Runtime.InteropServices;
 | ||
| using System.Text;
 | ||
| using System.Threading;
 | ||
| using System.Threading.Tasks;
 | ||
| 
 | ||
| namespace Check.Main.Camera
 | ||
| {
 | ||
|     public class HikvisionCamera : IDisposable
 | ||
|     {
 | ||
|         [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
 | ||
|         private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
 | ||
|         // 事件委托
 | ||
|         public delegate void ImageAcquiredEventHandler(HikvisionCamera sender, Bitmap bmp);
 | ||
|         // public delegate void ImageAcquiredEventHandler(object sender, Bitmap bmp);
 | ||
|         public delegate void CameraMessageEventHandler(object sender, string message, int errorCode = 0);
 | ||
| 
 | ||
|         // 事件
 | ||
|         public event ImageAcquiredEventHandler ImageAcquired;
 | ||
|         public event CameraMessageEventHandler CameraMessage;
 | ||
| 
 | ||
|         // 私有成员变量
 | ||
|         private MyCamera m_MyCamera;
 | ||
|         private MyCamera.MV_CC_DEVICE_INFO_LIST m_stDeviceList;
 | ||
|         private bool m_bGrabbing = false;
 | ||
|         private Thread m_hReceiveThread = null;
 | ||
| 
 | ||
|         private Bitmap m_bitmap = null;
 | ||
|         private IntPtr m_ConvertDstBuf = IntPtr.Zero;
 | ||
|         private uint m_nConvertDstBufLen = 0;
 | ||
|         public string Name { get; set; }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 由外部逻辑分配的、唯一的相机软件编号。
 | ||
|         /// 主要用作图像路由和与逻辑处理器匹配的键。
 | ||
|         /// </summary>
 | ||
|         public int CameraIndex { get; set; }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 相机是否已经打开
 | ||
|         /// </summary>
 | ||
|         public bool IsOpen { get; private set; } = false;
 | ||
| 
 | ||
|         public TriggerModeType TriggerMode { get; internal set; } // internal set 保证只有 CameraManager 能设置它
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 相机是否正在采集
 | ||
|         /// </summary>
 | ||
|         public bool IsGrabbing => m_bGrabbing;
 | ||
| 
 | ||
|         public HikvisionCamera()
 | ||
|         {
 | ||
|             // 初始化SDK
 | ||
|             MyCamera.MV_CC_Initialize_NET();
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 查找所有可用的相机设备
 | ||
|         /// </summary>
 | ||
|         /// <returns>设备名称列表</returns>
 | ||
|         public List<string> FindDevices()
 | ||
|         {
 | ||
|             var deviceList = new List<string>();
 | ||
|             m_stDeviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();
 | ||
|             int nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_stDeviceList);
 | ||
|             if (nRet != MyCamera.MV_OK)
 | ||
|             {
 | ||
|                 OnCameraMessage("设备枚举失败!", nRet);
 | ||
|                 return deviceList;
 | ||
|             }
 | ||
| 
 | ||
|             for (int i = 0; i < m_stDeviceList.nDeviceNum; i++)
 | ||
|             {
 | ||
|                 var 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)
 | ||
|                 {
 | ||
|                     var gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO_EX)MyCamera.ByteToStruct(device.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO_EX));
 | ||
|                     if (!string.IsNullOrEmpty(Encoding.Default.GetString(gigeInfo.chUserDefinedName).TrimEnd('\0')))
 | ||
|                         deviceList.Add("GEV: " + gigeInfo.chUserDefinedName + " (" + gigeInfo.chSerialNumber + ")");
 | ||
|                     else
 | ||
|                         deviceList.Add("GEV: " + gigeInfo.chManufacturerName + " " + gigeInfo.chModelName + " (" + gigeInfo.chSerialNumber + ")");
 | ||
|                 }
 | ||
|                 else if (device.nTLayerType == MyCamera.MV_USB_DEVICE)
 | ||
|                 {
 | ||
|                     var usbInfo = (MyCamera.MV_USB3_DEVICE_INFO_EX)MyCamera.ByteToStruct(device.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO_EX));
 | ||
|                     if (!string.IsNullOrEmpty(Encoding.Default.GetString(usbInfo.chUserDefinedName).TrimEnd('\0')))
 | ||
|                         deviceList.Add("U3V: " + usbInfo.chUserDefinedName + " (" + usbInfo.chSerialNumber + ")");
 | ||
|                     else
 | ||
|                         deviceList.Add("U3V: " + usbInfo.chManufacturerName + " " + usbInfo.chModelName + " (" + usbInfo.chSerialNumber + ")");
 | ||
|                 }
 | ||
|             }
 | ||
|             return deviceList;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 根据索引打开相机
 | ||
|         /// </summary>
 | ||
|         /// <param name="index">设备索引</param>
 | ||
|         /// <returns>成功返回true,否则返回false</returns>
 | ||
|         public bool Open(CameraSettings camConfig)
 | ||
|         {
 | ||
|             string ipCam = "";
 | ||
|             string ipDevice = "";
 | ||
|             FindDevices();
 | ||
|             if (camConfig.IsEnabled == true)
 | ||
|             {
 | ||
|                 ipCam = camConfig.IPAddress;
 | ||
|                 ipDevice = camConfig.IPDeviceAddress;
 | ||
|             }
 | ||
|             try
 | ||
|             {
 | ||
|                 if (m_stDeviceList.nDeviceNum == 0 || string.IsNullOrEmpty(camConfig.IPAddress))
 | ||
|                 {
 | ||
|                     OnCameraMessage("没有找到设备或索引无效。", -1);
 | ||
|                     return false;
 | ||
|                 }
 | ||
| 
 | ||
|                 if (IsOpen)
 | ||
|                 {
 | ||
|                     OnCameraMessage("相机已经打开。", 0);
 | ||
|                     return true;
 | ||
|                 }
 | ||
|                 MyCamera.MV_CC_DEVICE_INFO device = new MyCamera.MV_CC_DEVICE_INFO();
 | ||
|                 device.nTLayerType = MyCamera.MV_GIGE_DEVICE;
 | ||
|                 MyCamera.MV_GIGE_DEVICE_INFO stGigEDev = new MyCamera.MV_GIGE_DEVICE_INFO();
 | ||
|                 var parts = ipCam.Split('.');
 | ||
|                 int nIp1 = Convert.ToInt32(parts[0]);
 | ||
|                 int nIp2 = Convert.ToInt32(parts[1]);
 | ||
|                 int nIp3 = Convert.ToInt32(parts[2]);
 | ||
|                 int nIp4 = Convert.ToInt32(parts[3]);
 | ||
|                 stGigEDev.nCurrentIp = (uint)((nIp1 << 24) | (nIp2 << 16) | (nIp3 << 8) | nIp4);
 | ||
| 
 | ||
|                 parts = ipDevice.Split('.');
 | ||
|                 nIp1 = Convert.ToInt32(parts[0]);
 | ||
|                 nIp2 = Convert.ToInt32(parts[1]);
 | ||
|                 nIp3 = Convert.ToInt32(parts[2]);
 | ||
|                 nIp4 = Convert.ToInt32(parts[3]);
 | ||
|                 stGigEDev.nNetExport = (uint)((nIp1 << 24) | (nIp2 << 16) | (nIp3 << 8) | nIp4);
 | ||
|                 IntPtr stGigeInfoPtr = Marshal.AllocHGlobal(216);
 | ||
|                 Marshal.StructureToPtr(stGigEDev, stGigeInfoPtr, false);
 | ||
|                 device.SpecialInfo.stGigEInfo = new Byte[540];
 | ||
|                 Marshal.Copy(stGigeInfoPtr, device.SpecialInfo.stGigEInfo, 0, 540);
 | ||
|                 //释放内存空间
 | ||
|                 Marshal.FreeHGlobal(stGigeInfoPtr);
 | ||
| 
 | ||
|                 //var device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[index], typeof(MyCamera.MV_CC_DEVICE_INFO));
 | ||
|                 m_MyCamera = new MyCamera();
 | ||
|                 int nRet = m_MyCamera.MV_CC_CreateDevice_NET(ref device);
 | ||
|                 if (nRet != MyCamera.MV_OK)
 | ||
|                 {
 | ||
|                     OnCameraMessage("创建设备句柄失败!", nRet);
 | ||
|                     return false;
 | ||
|                 }
 | ||
| 
 | ||
|                 nRet = m_MyCamera.MV_CC_OpenDevice_NET();
 | ||
|                 if (nRet != MyCamera.MV_OK)
 | ||
|                 {
 | ||
|                     OnCameraMessage("打开设备失败!", nRet);
 | ||
|                     m_MyCamera.MV_CC_DestroyDevice_NET();
 | ||
|                     return false;
 | ||
|                 }
 | ||
| 
 | ||
|                 // 探测网络最佳包大小(只对GigE相机有效)
 | ||
|                 if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
 | ||
|                 {
 | ||
|                     int nPacketSize = m_MyCamera.MV_CC_GetOptimalPacketSize_NET();
 | ||
|                     if (nPacketSize > 0)
 | ||
|                     {
 | ||
|                         m_MyCamera.MV_CC_SetIntValueEx_NET("GevSCPSPacketSize", nPacketSize);
 | ||
|                     }
 | ||
|                 }
 | ||
| 
 | ||
|                 // 默认设置为连续模式
 | ||
|                 //SetContinuousMode();
 | ||
|                 // 设置为硬触发模式(Line0)
 | ||
|                 SetTriggerMode(false);
 | ||
| 
 | ||
|                 IsOpen = true;
 | ||
|                 OnCameraMessage("相机打开成功。", 0);
 | ||
|                 return true;
 | ||
|             }
 | ||
|             catch (Exception ex)
 | ||
|             {
 | ||
|                 OnCameraMessage("相机打开失败。"+ ex.ToString(), 0);
 | ||
|                 return false;
 | ||
|             }
 | ||
|             
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 关闭相机
 | ||
|         /// </summary>
 | ||
|         public void Close()
 | ||
|         {
 | ||
|             if (!IsOpen) return;
 | ||
| 
 | ||
|             if (m_bGrabbing)
 | ||
|             {
 | ||
|                 StopGrabbing();
 | ||
|             }
 | ||
| 
 | ||
|             m_MyCamera.MV_CC_CloseDevice_NET();
 | ||
|             m_MyCamera.MV_CC_DestroyDevice_NET();
 | ||
| 
 | ||
|             IsOpen = false;
 | ||
|             m_MyCamera = null;
 | ||
|             OnCameraMessage("相机已关闭。", 0);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 开始采集图像
 | ||
|         /// </summary>
 | ||
|         /// <returns>成功返回true</returns>
 | ||
|         public bool StartGrabbing()
 | ||
|         {
 | ||
|             if (!IsOpen || m_bGrabbing)
 | ||
|             {
 | ||
|                 OnCameraMessage(m_bGrabbing ? "已经在采集中。" : "相机未打开。", -1);
 | ||
|                 return false;
 | ||
|             }
 | ||
| 
 | ||
|             // 取图前的必要操作
 | ||
|             if (NecessaryOperBeforeGrab() != MyCamera.MV_OK)
 | ||
|             {
 | ||
|                 return false;
 | ||
|             }
 | ||
| 
 | ||
|             int nRet = m_MyCamera.MV_CC_StartGrabbing_NET();
 | ||
|             if (nRet != MyCamera.MV_OK)
 | ||
|             {
 | ||
|                 OnCameraMessage("开始采集失败!", nRet);
 | ||
|                 return false;
 | ||
|             }
 | ||
| 
 | ||
|             m_bGrabbing = true;
 | ||
|             m_hReceiveThread = new Thread(ReceiveThreadProcess);
 | ||
|             m_hReceiveThread.IsBackground = true;
 | ||
|             m_hReceiveThread.Start();
 | ||
| 
 | ||
|             OnCameraMessage("开始采集。", 0);
 | ||
|             return true;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 停止采集图像
 | ||
|         /// </summary>
 | ||
|         public void StopGrabbing()
 | ||
|         {
 | ||
|             if (!m_bGrabbing) return;
 | ||
| 
 | ||
|             m_bGrabbing = false;
 | ||
|             if (m_hReceiveThread != null && m_hReceiveThread.IsAlive)
 | ||
|             {
 | ||
|                 m_hReceiveThread.Join(1000); // 等待线程退出
 | ||
|             }
 | ||
| 
 | ||
|             int nRet = m_MyCamera.MV_CC_StopGrabbing_NET();
 | ||
|             if (nRet != MyCamera.MV_OK)
 | ||
|             {
 | ||
|                 OnCameraMessage("停止采集失败!", nRet);
 | ||
|             }
 | ||
| 
 | ||
|             OnCameraMessage("停止采集。", 0);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 设置为连续采集模式
 | ||
|         /// </summary>
 | ||
|         public void SetContinuousMode()
 | ||
|         {
 | ||
|             if (!IsOpen) return;
 | ||
|             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);
 | ||
|             OnCameraMessage("已设置为连续模式。", 0);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 设置为触发模式
 | ||
|         /// </summary>
 | ||
|         /// <param name="isSoftwareTrigger">true为软触发, false为硬触发(Line0)</param>
 | ||
|         public void SetTriggerMode(bool isSoftwareTrigger)
 | ||
|         {
 | ||
|             if (!IsOpen) return;
 | ||
| 
 | ||
|             m_MyCamera.MV_CC_SetEnumValue_NET("AcquisitionMode", (uint)MyCamera.MV_CAM_ACQUISITION_MODE.MV_ACQ_MODE_CONTINUOUS); // 触发模式也需要在Continuous模式下
 | ||
|             m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON);
 | ||
| 
 | ||
|             if (isSoftwareTrigger)
 | ||
|             {
 | ||
|                 m_MyCamera.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_SOFTWARE);
 | ||
|                 OnCameraMessage("已设置为软触发模式。", 0);
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 m_MyCamera.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_LINE0);
 | ||
|                 OnCameraMessage("已设置为硬触发(Line0)模式。", 0);
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 执行一次软触发
 | ||
|         /// </summary>
 | ||
|         /// <returns>成功返回true</returns>
 | ||
|         public bool SoftwareTrigger()
 | ||
|         {
 | ||
|             if (!IsOpen || !m_bGrabbing)
 | ||
|             {
 | ||
|                 OnCameraMessage("请先打开相机并开始采集。", -1);
 | ||
|                 return false;
 | ||
|             }
 | ||
| 
 | ||
|             int nRet = m_MyCamera.MV_CC_SetCommandValue_NET("TriggerSoftware");
 | ||
|             if (nRet != MyCamera.MV_OK)
 | ||
|             {
 | ||
|                 OnCameraMessage("软触发失败!", nRet);
 | ||
|                 return false;
 | ||
|             }
 | ||
|             return true;
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 图像接收线程
 | ||
|         /// </summary>
 | ||
|         private void ReceiveThreadProcess()
 | ||
|         {
 | ||
|             MyCamera.MV_FRAME_OUT stFrameInfo = new MyCamera.MV_FRAME_OUT();
 | ||
|             var stConvertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();
 | ||
| 
 | ||
|             while (m_bGrabbing)
 | ||
|             {
 | ||
|                 int nRet = m_MyCamera.MV_CC_GetImageBuffer_NET(ref stFrameInfo, 1000);
 | ||
|                 if (nRet == MyCamera.MV_OK)
 | ||
|                 {
 | ||
|                     Bitmap bmpForEvent = null; // 在 try 外部声明
 | ||
|                     try
 | ||
|                     {
 | ||
|                         // 初始化转换参数
 | ||
|                         stConvertParam.nWidth = stFrameInfo.stFrameInfo.nWidth;
 | ||
|                         stConvertParam.nHeight = stFrameInfo.stFrameInfo.nHeight;
 | ||
|                         stConvertParam.pSrcData = stFrameInfo.pBufAddr;
 | ||
|                         stConvertParam.nSrcDataLen = stFrameInfo.stFrameInfo.nFrameLen;
 | ||
|                         stConvertParam.enSrcPixelType = stFrameInfo.stFrameInfo.enPixelType;
 | ||
|                         stConvertParam.pDstBuffer = m_ConvertDstBuf;
 | ||
|                         stConvertParam.nDstBufferSize = m_nConvertDstBufLen;
 | ||
| 
 | ||
|                         // 创建用于事件的Bitmap,它的格式与类成员m_bitmap一致
 | ||
|                         bmpForEvent = new Bitmap((int)stConvertParam.nWidth, (int)stConvertParam.nHeight, m_bitmap.PixelFormat);
 | ||
| 
 | ||
|                         // 判断并设置目标像素格式
 | ||
|                         if (bmpForEvent.PixelFormat == PixelFormat.Format8bppIndexed)
 | ||
|                         {
 | ||
|                             stConvertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8;
 | ||
|                         }
 | ||
|                         else
 | ||
|                         {
 | ||
|                             stConvertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_BGR8_Packed;
 | ||
|                         }
 | ||
| 
 | ||
|                         // 执行像素格式转换
 | ||
|                         nRet = m_MyCamera.MV_CC_ConvertPixelType_NET(ref stConvertParam);
 | ||
|                         if (nRet == MyCamera.MV_OK)
 | ||
|                         {
 | ||
|                             // 转换成功,锁定位图内存并拷贝数据
 | ||
|                             BitmapData bmpData = bmpForEvent.LockBits(new Rectangle(0, 0, stConvertParam.nWidth, stConvertParam.nHeight), ImageLockMode.WriteOnly, bmpForEvent.PixelFormat);
 | ||
| 
 | ||
|                             // 使用更健壮的逐行拷贝
 | ||
|                             int pixelSize = (bmpForEvent.PixelFormat == PixelFormat.Format8bppIndexed) ? 1 : 3;
 | ||
|                             for (int i = 0; i < bmpData.Height; ++i)
 | ||
|                             {
 | ||
|                                 IntPtr pDst = new IntPtr(bmpData.Scan0.ToInt64() + i * bmpData.Stride);
 | ||
|                                 IntPtr pSrc = new IntPtr(m_ConvertDstBuf.ToInt64() + i * stConvertParam.nWidth * pixelSize);
 | ||
|                                 CopyMemory(pDst, pSrc, (uint)(stConvertParam.nWidth * pixelSize));
 | ||
|                             }
 | ||
|                             bmpForEvent.UnlockBits(bmpData);
 | ||
| 
 | ||
|                             // 触发图像采集事件,转移 bmpForEvent 的所有权
 | ||
|                             ImageAcquired?.Invoke(this, bmpForEvent);
 | ||
| 
 | ||
|                             // 【关键】所有权已转移,将本地引用设为null,防止它在finally块中被错误地释放
 | ||
|                             bmpForEvent = null;
 | ||
|                         }
 | ||
|                         // 如果转换失败,不执行任何操作,让 finally 块来处理 bmpForEvent 的释放
 | ||
|                     }
 | ||
|                     finally
 | ||
|                     {
 | ||
|                         // 【关键】如果 bmpForEvent 不为 null,意味着它被成功创建,
 | ||
|                         // 但没有被成功传递给事件处理器(可能因为转换失败或发生其他异常),
 | ||
|                         // 在这里必须将其释放。
 | ||
|                         bmpForEvent?.Dispose();
 | ||
| 
 | ||
|                         // 无论成功与否,都必须释放相机SDK的内部图像缓存
 | ||
|                         m_MyCamera.MV_CC_FreeImageBuffer_NET(ref stFrameInfo);
 | ||
|                     }
 | ||
|                 }
 | ||
|                 else
 | ||
|                 {
 | ||
|                     // 超时,继续等待
 | ||
|                     Thread.Sleep(5);
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 取图前的内存和参数准备
 | ||
|         /// </summary>
 | ||
|         private int NecessaryOperBeforeGrab()
 | ||
|         {
 | ||
|             var stWidth = new MyCamera.MVCC_INTVALUE_EX();
 | ||
|             int nRet = m_MyCamera.MV_CC_GetIntValueEx_NET("Width", ref stWidth);
 | ||
|             if (nRet != MyCamera.MV_OK) { OnCameraMessage("获取宽度失败!", nRet); return nRet; }
 | ||
| 
 | ||
|             var stHeight = new MyCamera.MVCC_INTVALUE_EX();
 | ||
|             nRet = m_MyCamera.MV_CC_GetIntValueEx_NET("Height", ref stHeight);
 | ||
|             if (nRet != MyCamera.MV_OK) { OnCameraMessage("获取高度失败!", nRet); return nRet; }
 | ||
| 
 | ||
|             var stPixelFormat = new MyCamera.MVCC_ENUMVALUE();
 | ||
|             nRet = m_MyCamera.MV_CC_GetEnumValue_NET("PixelFormat", ref stPixelFormat);
 | ||
|             if (nRet != MyCamera.MV_OK) { OnCameraMessage("获取像素格式失败!", nRet); return nRet; }
 | ||
| 
 | ||
|             PixelFormat bitmapPixelFormat;
 | ||
|             // 判断当前像素格式是否为Mono
 | ||
|             bool isMono = IsMono(stPixelFormat.nCurValue);
 | ||
| 
 | ||
|             if (isMono)
 | ||
|             {
 | ||
|                 bitmapPixelFormat = PixelFormat.Format8bppIndexed;
 | ||
|                 m_nConvertDstBufLen = (uint)(stWidth.nCurValue * stHeight.nCurValue);
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 bitmapPixelFormat = PixelFormat.Format24bppRgb;
 | ||
|                 m_nConvertDstBufLen = (uint)(stWidth.nCurValue * stHeight.nCurValue * 3);
 | ||
|             }
 | ||
| 
 | ||
|             // 分配转换后的图像数据缓存
 | ||
|             if (m_ConvertDstBuf != IntPtr.Zero) Marshal.FreeHGlobal(m_ConvertDstBuf);
 | ||
|             m_ConvertDstBuf = Marshal.AllocHGlobal((int)m_nConvertDstBufLen);
 | ||
| 
 | ||
|             // 创建Bitmap对象
 | ||
|             if (m_bitmap != null) m_bitmap.Dispose();
 | ||
|             m_bitmap = new Bitmap((int)stWidth.nCurValue, (int)stHeight.nCurValue, bitmapPixelFormat);
 | ||
| 
 | ||
|             // 如果是Mono8格式,设置调色板
 | ||
|             if (isMono)
 | ||
|             {
 | ||
|                 ColorPalette palette = m_bitmap.Palette;
 | ||
|                 for (int i = 0; i < 256; i++) palette.Entries[i] = Color.FromArgb(i, i, i);
 | ||
|                 m_bitmap.Palette = palette;
 | ||
|             }
 | ||
| 
 | ||
|             return MyCamera.MV_OK;
 | ||
|         }
 | ||
| 
 | ||
|         private bool IsMono(uint enPixelType)
 | ||
|         {
 | ||
|             switch (enPixelType)
 | ||
|             {
 | ||
|                 case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8:
 | ||
|                 case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10:
 | ||
|                 case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:
 | ||
|                 case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12:
 | ||
|                 case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:
 | ||
|                     return true;
 | ||
|                 default:
 | ||
|                     return false;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 触发相机消息事件
 | ||
|         /// </summary>
 | ||
|         private void OnCameraMessage(string message, int errorCode = 0)
 | ||
|         {
 | ||
|             CameraMessage?.Invoke(this, message, errorCode);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 释放资源
 | ||
|         /// </summary>
 | ||
|         public void Dispose()
 | ||
|         {
 | ||
|             Close();
 | ||
|             if (m_ConvertDstBuf != IntPtr.Zero)
 | ||
|             {
 | ||
|                 Marshal.FreeHGlobal(m_ConvertDstBuf);
 | ||
|                 m_ConvertDstBuf = IntPtr.Zero;
 | ||
|             }
 | ||
|             if (m_bitmap != null)
 | ||
|             {
 | ||
|                 m_bitmap.Dispose();
 | ||
|                 m_bitmap = null;
 | ||
|             }
 | ||
|             MyCamera.MV_CC_Finalize_NET();
 | ||
|         }
 | ||
|     }
 | ||
| }
 |