当前位置:   article > 正文

C/C++实战——基于Qt框架和visual studio的海康相机SDK二次开发_海康工业相机二次开发

海康工业相机二次开发

        最近在进行机器视觉系统的搭建,积累了经验,想和大家讨论、互相学习。相机是图像的来源。为了搭建视觉系统,完成图像分析、机器视觉任务,需要编程控制相机按工作所需的曝光、增益和帧率同步采集和存储图像。工业相机通常支持C/C++程序进行控制,提供了SDK(Software Development Kit)。这些SDK包括对相机进行基本控制的函数,利用这些函数,就可以控制相机参数(如曝光时间、增益、帧率、像素格式),满足视觉系统任务需求。

1.1相机选型

        工业相机分为彩色工业(CCD芯片)相机和黑白工业相机(CMOS芯片)。芯片的三个关键参数是:靶面尺寸(感光芯片的尺寸)、分辨率(图像宽高)和像元大小。在同样的镜头焦距和同样的靶面尺寸下,分辨率越高,图像的物面分辨率就越高。相同的价格下黑白工业相机的分辨率更高、噪声更小图像处理更简单,适合用于视觉测量、工业视觉等任务场景;彩色相机能获取的信息则更为丰富,适用于人脸检测、物体识别等任务。

        另外选择相机还需要注意其数据传输方式,一般有Gige和usb3.0两种。Gige数据传输更稳定,usb3.0传输速度更快。

1.2相机SDK

        我参与的项目采用的是海康MV-023-10UM相机,相机分辨率230万,像元尺寸5.86um。首先下载海康SDK。https://www.hikrobotics.com/cn/machinevision/service/download?module=0。下载机器视觉工业相机客户端MVS。建议默认安装路径安装。安装好后打开下面的文件夹,这里就是海康相机的软件开发包了。

         Includes文件夹下就是SDK的头文件,包括了用户可以使用的函数和变量的声明。Libraries文件夹下是静态链接库,内容应该是头文件里声明的函数的定义。Samples目录下就是二次开发示例。

我用的是VS2015,打开这个sample进行解读。因为在C盘下,所以需要给权限运行。如图选择使用不同的凭据重新启动此应用程序。 

 跑起来后可以看到一个的界面。可以打和初始化相机。

 

        这是一个基于MFC框架的界面程序,需要了解的实际上只有三组文件(或者说三路文件)。一是MvCameraControl.h和MvErrorDefine.h和PixelType.h和CameraParams.h这几个文件就是前面的SDK的include里的头文件;二是MvCamera.h和MvCamera.cpp这是海康对SDK里的API和变量进行的二次封装,方便用户使用;三是BasicDemoDlg.h和BasicDemo.cpp,这是这个界面应用程序的主体。其他的则是一些资源文件和系统文件。因此,只要了解了海康二次开发的内容和界面程序的内容,就可以照猫画虎进行二次开发了。可以将这个demo划为3个层次

        这里还需要借助海康给出的二次开发文档,这里面有相机工作的方式和流程,以及SDK里API的说明。

 首先看MvCamera.h

  1. /************************************************************************/
  2. /* 以C++接口为基础,对常用函数进行二次封装,方便用户使用 */
  3. /************************************************************************/
  4. class CMvCamera
  5. {
  6. public:
  7. CMvCamera();
  8. ~CMvCamera();
  9. // ch:获取SDK版本号 | en:Get SDK Version
  10. static int GetSDKVersion();
  11. // ch:枚举设备 | en:Enumerate Device
  12. static int EnumDevices(unsigned int nTLayerType, MV_CC_DEVICE_INFO_LIST* pstDevList);
  13. // ch:判断设备是否可达 | en:Is the device accessible
  14. static bool IsDeviceAccessible(MV_CC_DEVICE_INFO* pstDevInfo, unsigned int nAccessMode);
  15. // ch:打开设备 | en:Open Device
  16. int Open(MV_CC_DEVICE_INFO* pstDeviceInfo);
  17. // ch:关闭设备 | en:Close Device
  18. int Close();
  19. // ch:判断相机是否处于连接状态 | en:Is The Device Connected
  20. bool IsDeviceConnected();
  21. /**********这里博主略了很多内容,细节大家可以看自己SDK里的文档****************/
  22. /**********这里博主略了很多内容,细节大家可以看自己SDK里的文档****************/
  23. /**********这里博主略了很多内容,细节大家可以看自己SDK里的文档****************/
  24. /**********这里博主略了很多内容,细节大家可以看自己SDK里的文档****************/
  25. /**********这里博主略了很多内容,细节大家可以看自己SDK里的文档****************/
  26. // ch:保存图片 | en:save image
  27. int SaveImage(MV_SAVE_IMAGE_PARAM_EX* pstParam);
  28. // ch:保存图片为文件 | en:Save the image as a file
  29. int SaveImageToFile(MV_SAVE_IMG_TO_FILE_PARAM* pstParam);
  30. private:
  31. void* m_hDevHandle;
  32. };

        可以看到,这个类里只有一个句柄和一系列和相机控制相关的函数。那么可以推测,相机(一个或许多个)可以通过实例化这个CMvCamera类的对象就可以对相机进行控制了。

再看BasicDemoDlg.h

  1. class CBasicDemoDlg : public CDialog
  2. {
  3. // Construction
  4. public:
  5. CBasicDemoDlg(CWnd* pParent = NULL); // Standard constructor
  6. // Dialog Data
  7. enum { IDD = IDD_BasicDemo_DIALOG };
  8. protected:
  9. virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
  10. // Implementation
  11. protected:
  12. HICON m_hIcon;
  13. // Generated message map functions
  14. virtual BOOL OnInitDialog();
  15. afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
  16. afx_msg void OnPaint();
  17. afx_msg HCURSOR OnQueryDragIcon();
  18. DECLARE_MESSAGE_MAP()
  19. /*ch:控件对应变量 | en:Control corresponding variable*/
  20. private:
  21. BOOL m_bSoftWareTriggerCheck;
  22. double m_dExposureEdit;
  23. double m_dGainEdit;
  24. double m_dFrameRateEdit;
  25. CComboBox m_ctrlDeviceCombo; // ch:枚举到的设备 | en:Enumerated device
  26. int m_nDeviceCombo;
  27. private:
  28. /*ch:最开始时的窗口初始化 | en:Window initialization*/
  29. void DisplayWindowInitial();
  30. void EnableControls(BOOL bIsCameraReady);
  31. void ShowErrorMsg(CString csMessage, int nErrorNum);
  32. int SetTriggerMode(); // ch:设置触发模式 | en:Set Trigger Mode
  33. int GetTriggerMode();
  34. int GetExposureTime(); // ch:设置曝光时间 | en:Set Exposure Time
  35. int SetExposureTime();
  36. int GetGain(); // ch:设置增益 | en:Set Gain
  37. int SetGain();
  38. int GetFrameRate(); // ch:设置帧率 | en:Set Frame Rate
  39. int SetFrameRate();
  40. int GetTriggerSource(); // ch:设置触发源 | en:Set Trigger Source
  41. int SetTriggerSource();
  42. int CloseDevice(); // ch:关闭设备 | en:Close Device
  43. int SaveImage(MV_SAVE_IAMGE_TYPE enSaveImageType); // ch:保存图片 | en:Save Image
  44. // ch:去除自定义的像素格式 | en:Remove custom pixel formats
  45. bool RemoveCustomPixelFormats(enum MvGvspPixelType enPixelFormat);
  46. private:
  47. BOOL m_bOpenDevice; // ch:是否打开设备 | en:Whether to open device
  48. BOOL m_bStartGrabbing; // ch:是否开始抓图 | en:Whether to start grabbing
  49. int m_nTriggerMode; // ch:触发模式 | en:Trigger Mode
  50. int m_nTriggerSource; // ch:触发源 | en:Trigger Source
  51. CMvCamera* m_pcMyCamera; // ch:CMyCamera封装了常用接口 | en:CMyCamera packed commonly used interface
  52. HWND m_hwndDisplay; // ch:显示句柄 | en:Display Handle
  53. MV_CC_DEVICE_INFO_LIST m_stDevList;
  54. CRITICAL_SECTION m_hSaveImageMux;
  55. unsigned char* m_pSaveImageBuf;
  56. unsigned int m_nSaveImageBufSize;
  57. MV_FRAME_OUT_INFO_EX m_stImageInfo;
  58. void* m_hGrabThread; // ch:取流线程句柄 | en:Grab thread handle
  59. BOOL m_bThreadState;
  60. public:
  61. /*ch:初始化 | en:Initialization*/
  62. afx_msg void OnBnClickedEnumButton(); // ch:查找设备 | en:Find Devices
  63. afx_msg void OnBnClickedOpenButton(); // ch:打开设备 | en:Open Devices
  64. afx_msg void OnBnClickedCloseButton(); // ch:关闭设备 | en:Close Devices
  65. /*ch:图像采集 | en:Image Acquisition*/
  66. afx_msg void OnBnClickedContinusModeRadio(); // ch:连续模式 | en:Continus Mode
  67. afx_msg void OnBnClickedTriggerModeRadio(); // ch:触发模式 | en:Trigger Mode
  68. afx_msg void OnBnClickedStartGrabbingButton(); // ch:开始采集 | en:Start Grabbing
  69. afx_msg void OnBnClickedStopGrabbingButton(); // ch:结束采集 | en:Stop Grabbing
  70. afx_msg void OnBnClickedSoftwareTriggerCheck(); // ch:软触发 | en:Software Trigger
  71. afx_msg void OnBnClickedSoftwareOnceButton(); // ch:软触发一次 | en:Software Trigger Execute Once
  72. /*ch:图像保存 | en:Image Save*/
  73. afx_msg void OnBnClickedSaveBmpButton(); // ch:保存bmp | en:Save bmp
  74. afx_msg void OnBnClickedSaveJpgButton(); // ch:保存jpg | en:Save jpg
  75. afx_msg void OnBnClickedSaveTiffButton();
  76. afx_msg void OnBnClickedSavePngButton();
  77. /*ch:参数设置获取 | en:Parameters Get and Set*/
  78. afx_msg void OnBnClickedGetParameterButton(); // ch:获取参数 | en:Get Parameter
  79. afx_msg void OnBnClickedSetParameterButton(); // ch:设置参数 | en:Exit from upper right corner
  80. afx_msg void OnClose();
  81. virtual BOOL PreTranslateMessage(MSG* pMsg);
  82. int GrabThreadProcess();
  83. };

        这个文件里需要掌握的其实也只有三块:1.public函数:典型的是OnBnClicked开头的一系列函数。这些函数与界面上的按钮相对应,可以在资源文件里,点击按钮看到。比如开始采集,对应着OnBnClickedStartGrabbingButton函数。这类函数就是用户发出指令的。用户需要发出怎样指令,看到怎样的效果,这里就需要编写对应的函数。

 2.private函数:比如SetParams等等。这些是相机和程序交互的函数。

3.private变量:包括相机对象和相机参数。比如  CMvCamera*              m_pcMyCamera; 这就是要实例化的相机对象。

        进一步将程序跑起来,连上相机,结合开发文档。可以了解到整个程序的流程应如下

1.3 相机预览多线程

        整个程序中比较复杂的结构是相机图像的预览。直观地:就是相机不断取流的时候,显示器不断地展示相机拍摄到的图片。首先,程序肯定有循环,不断地取得相机数据。第二,程序需要保持与用户交互,不断显示图片,还能允许用户发送控制指令。第二点就是界面程序的本身任务,界面程序在一个线程里保持与用户交互;因此,还需要一个线程去不断获取相机的图像数据。

        

进行详细的代码解读(沿着打开预览的按钮的函数一直看下去):

在BasicDemoDlg.h声明了线程对象:

  void*                   m_hGrabThread;              // ch:取流线程句柄 | en:Grab threadhandle

 在BasicDemoDlg.h声明了取流函数:

  int GrabThreadProcess();

观察其定义,确实是循环在取流:

  1. int CBasicDemoDlg::GrabThreadProcess()
  2. {
  3. MV_FRAME_OUT stImageInfo = {0};
  4. MV_DISPLAY_FRAME_INFO stDisplayInfo = {0};
  5. int nRet = MV_OK;
  6. while(m_bThreadState)
  7. {
  8. nRet = m_pcMyCamera->GetImageBuffer(&stImageInfo, 1000);
  9. if (nRet == MV_OK)
  10. {
  11. /*********/
  12. }
  13. else
  14. {
  15. if (MV_TRIGGER_MODE_ON == m_nTriggerMode)
  16. {
  17. Sleep(5);
  18. }
  19. }
  20. }
  21. return MV_OK;
  22. }

还需要一个函数,决定线程的启动:

  1. unsigned int __stdcall GrabThread(void* pUser)
  2. {
  3. if (pUser)
  4. {
  5. CBasicDemoDlg* pCam = (CBasicDemoDlg*)pUser;
  6. pCam->GrabThreadProcess();
  7. return 0;
  8. }
  9. return -1;
  10. }

 观察上面函数GrabThread是如何被调用的:

  m_hGrabThread = (void*)_beginthreadex( NULL , 0 , GrabThread , this, 0 , &nThreadID );

注册回调函数的形式启动多线程。线程执行GrabThread,函数调用的是this,而this在线程里被同类的另一对象指向。也就是说,通过子线程里界面对象指针指向主线程的界面对象,从而实现子线程和主线程共同控制相机,完成不同的任务 。

        还需注意合适的位置加锁:

 2.基于Qt双相机控制

        采用Qt开发界面程序更加简单方便,因此这里用Qt进行双目相机控制界面程序开发。

IDE · GitCode

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/709854
推荐阅读
相关标签
  

闽ICP备14008679号