HK相机模块封装

本篇文章主要介绍如何使用HK官方提供的Demo(此处使用的是v1版本的Demo),来封装自己的打开相机方法。
下载MVS后,在对应的Samples目录下就能找到对应的Demo文件。

下文中的代码大部分都是对HK提供的Demo中的方法进行了逻辑上的改动,代码本身都是能直接从Demo中找到的。
image

文章内容源于:https://blog.csdn.net/qq_41375318/article/details/148051216 此处添加了一些额外说明,用于学习使用。

封装变量

封装变量
 /// <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(); 

启动相机方法封装

相机启动步骤

  1. 获取所有相机
  2. 根据相机序列号,看该序列号对应的相机ID是否存在,确定该相机是否存在
  3. 获取相机信息
  4. 建立设备对象
  5. 根据相机信息创建相机
  6. 打开设备
  7. 探测网络最佳包大小,并进行设置(针对网口相机,并且此步可忽略。默认情况下使用巨型帧)
  8. 设置为连续采集模式
  9. 注册异常回调函数(非必须,也可注册一些用于自己开发的项目的传参回调(向UI传图片、向其它类中传图片))

代码实现

此处的代码都能在对应的案例中找到。

打开相机
/// <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;
}
其他函数
// 枚举海康相机(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;
}

相机采图方法封装

相机采图流程

  1. 采集标志位置位,设为true,表示开始采集
  2. 启动取像线程(核心)
  3. 取流之前先清除帧长度
  4. 开始采集
  5. 如果采集失败,提示错误信息
点击查看代码
/// <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;
}

取像线程流程

  1. 获取单帧图像数据的有效负载大小(单位为字节),即从相机传输到客户端的每一帧图像数据(不包含协议头、尾等额外开销)的实际大小
  2. 获取图像高
  3. 获取图像宽
  4. 根据图像大小设置图像缓存
  5. 循环监听,触发相机的图像采集信号
  6. 获取一帧图像数据(核心)等待,软触发或者硬触发的信号
  7. 如果是彩色图像格式,则直接使用数据,给到pTemp
  8. 获取rgb三个通道的数据
  9. rgb三通道的指针数据转bitmap
  10. 进行显示
  11. 如果是黑白相机图像
  12. 灰度指针数据转bitmap
  13. 进行图像显示
取图线程
// 取像线程(核心)
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;
    // 根据图像大小设置图像缓存
    //这里用于列坐标的内存对齐。
    UInt32 nSupWidth = (nWidth + (UInt32)3) & 0xfffffffc;//5120
    // 根据图像大小设置图像缓存
    m_pDataForRed = new byte[nSupWidth * nHeight];
    m_pDataForGreen = new byte[nSupWidth * nHeight];
    m_pDataForBlue = new byte[nSupWidth * nHeight];
    //此处的判断 是为了与后面做对应,我们统一把所有图像都转换为了3通道。防止由于当前nPayloadSize为单通道导致m_nBufSizeForDriver不足
    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();
    //用来记录RGB三个区域的图像数据
    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)
        {
            // 彩色相机(对于所有的彩色图像,此处统一转换为RGB3通道图像)
            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
                {
                    //根据前面得到的指针地址,此处将完整的RGB图像数据 划分为三个通道分别存放到三个数组中
                    //这里用Marshal的CopyMemoer方法应该会更快,但我还没试
                    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);
                //显示采集图像(防止多线程竞争访问UI资源)
                lock (BufForImageLock)
                {
                    //从RGB数据中创建Bitmap
                    m_bitmap = CreateBitmapFromRGBPointers(RedPtr, GreenPtr, BluePtr,stFrameInfo.nWidth, stFrameInfo.nHeight);
                    pictureBox1.Image = m_bitmap;
                }
            }
            // 黑白图像(同理)
            else if (IsMonoData(stFrameInfo.enPixelType))
            {
                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="enGvspPixelType"></param>
/// <returns></returns>
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;
    }
}

/// <summary>
/// 判断是否为彩色图像
/// </summary>
/// <param name="enGvspPixelType"></param>
/// <returns></returns>
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;
    }
}

/// <summary>
/// 转换为RGB格式
/// </summary>
/// <param name="obj"></param>
/// <param name="pSrc"></param>
/// <param name="nHeight"></param>
/// <param name="nWidth"></param>
/// <param name="nPixelType"></param>
/// <param name="pDst"></param>
/// <returns></returns>
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;//源数据的格式
    //源数据长度=宽*高*每像素字节数,这与HK中定义的PixelType有关(高 16 位通常存储 “每像素的位数")
    stPixelConvertParam.nSrcDataLen = (uint)(nWidth * nHeight * ((((uint)nPixelType) >> 16) & 0x00ff) >> 3);

    //与上面同理,这里计算的是目标像素类型的总字节数=宽*高*RGB三通道类型每像素字节数(这里实际上就是*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;
}

/// <summary>
///  转换为Mono8格式
/// </summary>
/// <param name="obj"></param>
/// <param name="pInData"></param>
/// <param name="pOutData"></param>
/// <param name="nHeight"></param>
/// <param name="nWidth"></param>
/// <param name="nPixelType"></param>
/// <returns></returns>
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);

    //Mono8用不了这么大的空间 ,因为其是单通道的。此处的空间我们是按照一个3通道数据申请的,这也与上面的if判断对应起来了。
    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);
    //stPixelConvertParam.nDstBufferSize = (uint)(nWidth * nHeight * 1);
    nRet = device.MV_CC_ConvertPixelType_NET(ref stPixelConvertParam);//格式转换
    if (MyCamera.MV_OK != nRet)
    {
        return -1;
    }
    return nRet;
}
RGB/灰度图 转Bitmap

/// <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;

</details>
posted @ 2025-08-17 22:28  Ytytyty  阅读(29)  评论(0)    收藏  举报