海康相機SDK開發in VS2015+Qt5



1 開發環境

本章節介紹工業相機二次開發環境的安裝,安裝后各目錄所包含的文件,以及客戶端的展示效果。

1.1 安裝包獲取

從官網下載最新版本的MVS安裝包,支持Windows xp、Windows 7、Windows 8、Windows 10的32和64位系統。安裝過程默認即可。

官網下載鏈接:http://www.hikvision.com/cn/download_more_960.html

1.2 安裝目錄介紹

MVS安裝包由四個組件構成,分別是MVS客戶端、SDK開發包、驅動、GenICam。安裝過程大概1-3分鍾。我安裝在“D:\Program\MVS”路徑,目錄結構如下:

clip_image001[4]

1.3 效果展示

建議安裝成功后,連接相機,打開MVS客戶端,查看相機連接和圖像預覽的效果,確認環境正常后,再開始基於SDK的二次開發。如下:

clip_image003[4]

觀察三個指標:

1) 帶寬。正常值在100Mbps以上即認為正常;

2) 錯誤數。非0即表示有丟幀,不正常;

3) 丟包數。非0,不正常。參考第四章常見問題的解決方法。

clip_image005[4]

2 產品概述

本章介紹SDK在整個機器視覺系統中的層次定位,可實現的功能,基本的開發調用流程,以及常用的接口。

2.1 SDK定位

clip_image007[4]

2.2 基本接口調用流程

clip_image009[4]

2.3 參數配置

1) 相機所有開放的參數可參考MVS的屬性樹,只要在屬性樹中看得到的節點,都可以通過SDK來獲取和設置相應的值。

2) 每個節點分別屬於哪種數據類型,可參考如下控件形式:

3) 節點的關鍵字,可參考以下表格:

HikCameraNode面陣相機.xlsx

HikCameraNode線陣相機.xlsx

e.g:

設置寬度為2592 ->MV_CC_SetIntValue(handle, “Width”, 2592);

4) 詳細的函數說明,可以參考SDK 使用手冊。

工業相機SDK 使用手冊.chm

3 具體海康相機SDK開發

這里我采用的是型號為MV-CA050-10GC的海康工業相機,開發平台是VS2015,界面是在Qt5框架上開發的。

3.1 配置Qt5開發環境和項目創建

3.1.1 配置Qt5開發環境

在VS2015中配置Qt5開發環境,具體可以參考我發布的文章《VS2015_Qt5_Halcon混合編程》第一章。

3.1.2 創建Qt Application項目

在VS2015中創建一個Qt Application項目

clip_image011[4]

clip_image013[4]

雙擊“hkcamera.ui”,如下圖所示添加一個Lable用於顯示,四個Push Button用於按鈕控制

clip_image015[4]

3.2 配置海康相機SDK開發環境

3.2.1 添加附加包含目錄

項目 --- 屬性 --- 屬性頁 --- C/C++ --- 常規--- 附加包含目錄添加如下路徑:

E:\code\Libraries\HKSDK\Includes

clip_image017[4]

3.2.2 添加附加庫目錄

項目 --- 屬性 --- 屬性頁 --- 鏈接器 --- 常規 --- 附加庫目錄,添加如下路徑

E:\code\Libraries\HKSDK\Libraries\win64

clip_image019[4]

3.2.3 添加附加依賴項

項目 --- 屬性 --- 屬性頁 --- 鏈接器 --- 輸入 --- 附加依賴項,添加

MvCameraControl.lib

clip_image020[4]

3.3 SDK開發

clip_image022[4]

3.3.1 枚舉設備

可通過函數MV_CC_EnumDevices(IN unsigned int nTLayerType, IN&OUT

MV_CC_DEVICE_INFO_LIST* pstDevList)來枚舉相機。

nTLayerType:用戶輸入的傳輸層類型(也就是相機類型),一般有MV_GIGE_DEVICE,MV_USB_DEVICE分別對應GigE和U3V相機。 pstDevList:枚舉到的設備都存儲到這個結構體中了,供之后使用。

也可以通過函數MV_CC_EnumerateTls來枚舉支持的設備類型(傳輸層類型)和函數MV_CC_EnumDevicesEx來枚舉子網內指定的傳輸協議和指定廠商的所有設備。

/************************************************************************/
/* 1.枚舉設備 MV_CC_EnumDevices/MV_CC_EnumerateTls/MV_CC_EnumDevicesEx */
/************************************************************************/
//枚舉子網內指定的傳輸協議對應的所有設備
unsigned int nTLayerType = MV_GIGE_DEVICE | MV_USB_DEVICE;
MV_CC_DEVICE_INFO_LIST m_stDevList = { 0 };
int nRet = MV_CC_EnumDevices(nTLayerType, &m_stDevList);
3.3.2 創建句柄

可通過函數MV_CC_CreateHandle(OUT void ** handle, IN const MV_CC_DEVICE_INFO* pstDevInfo)創建句柄。

handle:創建句柄成功后,該句柄返回到handle。pstDevInfo:用戶輸入的設備信息,枚舉設備時所獲取,這樣的話該設備就和該句柄綁定在一起了,以后只用句柄就完成所有任務。

也可以通過函數MV_CC_CreateHandleWithoutLog創建無日志的句柄。

/************************************************************************/
/* 2.創建句柄 MV_CC_CreateHandle/MV_CC_CreateHandleWithoutLog */
/************************************************************************/
//選擇查找到的第一台在線設備,創建設備句柄
int nDeviceIndex = 0;
MV_CC_DEVICE_INFO m_stDevInfo = { 0 };
memcpy(&m_stDevInfo, m_stDevList.pDeviceInfo[nDeviceIndex],sizeof(MV_CC_DEVICE_INFO));
nRet = MV_CC_CreateHandle(&m_handle, &m_stDevInfo);
3.3.3 打開設備

可通過函數MV_CC_OpenDevice(IN void* handle, IN unsigned int nAccessMode = MV_ACCESS_Exclusive, IN unsigned short nSwitchoverKey = 0)打開設備。

這個函數只需要輸入一個參數即可,就是上面創建成功的句柄handle,后兩個參數一般使用默認參數,返回成功后表示打開了對應相機。

/************************************************************************/
/* 3.打開設備 MV_CC_OpenDevice */
/************************************************************************/
//連接設備
//nRet = MV_CC_OpenDevice(m_handle, nAccessMode, nSwitchoverKey);
nRet = MV_CC_OpenDevice(m_handle);
3.3.4 開啟抓圖

可通過函數MV_CC_StartGrabbing(IN void* handle)開始抓圖。

此操作依然只輸入一個handle即可,但開啟抓圖並沒有圖像,只是有流數據傳輸而已。若需要取圖有兩種方式,一種注冊回調,另一種主動調用MV_CC_GetOneFrameTimeout來取圖。

/************************************************************************/
/* 4.開啟抓圖 MV_CC_StartGrabbing */
/************************************************************************/
//開始采集圖像
nRet = MV_CC_StartGrabbing(m_handle);
3.3.5 獲取一幀圖像

圖像數據采集有兩種方式,兩種方式不能復用:

1) 調用MV_CC_StartGrabbing開始采集,然后在應用層循環調用MV_CC_GetOneFrame獲取指定像素格式的幀數據,獲取幀數據時上層應用程序需要根據幀率控制好調用該接口的頻率。

通過函數MV_CC_GetOneFrameTimeout (IN void* handle, IN&OUT unsigned char * pData , IN unsigned int nDataSize, IN&OUT MV_FRAME_OUT_INFO_EX* pFrameInfo,int nMsec)獲取一幀。所獲取的幀屬於裸數據,數據保存在pData,並無圖像格式(具體數據格式可以提前設定)。pFrameInfo表示輸出幀的信息。

也可以通過函數MV_CC_GetOneFrame獲取一幀圖像數據;函數MV_CC_GetOneFrameEx獲取一幀圖像數據,支持獲取chunk信息;函數MV_CC_GetImageForRGB獲取一幀RGB24數據,查詢內存里面幀數據並且轉換成RGB24格式返回,支持設置超時時間;函數MV_CC_GetImageForBGR獲取一幀BGR24數據,查詢內存里面幀數據並且轉換成BGR24格式返回,支持設置超時時間。

/************************************************************************/
/* 5.獲取一幀圖像 MV_CC_GetOneFrameTimeout */
/************************************************************************/
//獲取一幀數據的大小
MVCC_INTVALUE stIntvalue = { 0 };
nRet = MV_CC_GetIntValue(m_handle, "PayloadSize", &stIntvalue);
//一幀數據大小+預留字節(用於SDK內部處理)
int nBufSize = stIntvalue.nCurValue + 2048;
unsigned int nTestFrameSize = 0;
unsigned char* pFrameBuf = NULL;
pFrameBuf = (unsigned char*)malloc(nBufSize);
MV_FRAME_OUT_INFO stInfo;
memset(&stInfo, 0, sizeof(MV_FRAME_OUT_INFO));
//上層應用程序需要根據幀率,控制好調用該接口的頻率
//此次代碼僅供參考,實際應用建議另建線程進行圖像幀采集和處理
//pFrameBuf是相機采集到的一幀原始圖像數據
nRet = MV_CC_GetOneFrame(m_handle, pFrameBuf, nBufSize, &stInfo);

2) 調用MV_CC_RegisterImageCallBack設置圖像數據回調函數,然后調用MV_CC_StartGrabbing開始采集,采集的圖像數據在設置的回調函數中返回。

在創建句柄后,通過注冊回調函數MV_CC_RegisterImageCallBack,然后通過回調函數ImageCallBack獲得一幀圖像數據。

先在源文件中注冊數據回調函數,

//注冊數據回調函數
nRet = MV_CC_RegisterImageCallBack(m_handle, ImageCallBack, NULL);
if (MV_OK != nRet)
{
    printf("error: RegisterImageCallBack fail [%x]\n", nRet);
    return;
}

再在頭文件中聲明回調函數,注意聲明回調函數為靜態函數

static void __stdcall ImageCallBack(unsigned char * pData, MV_FRAME_OUT_INFO * pFrameInfo, void * pUser);

最后在源文件中定義回調函數。

void __stdcall hkCamera::ImageCallBack(unsigned char * pData, MV_FRAME_OUT_INFO* pFrameInfo, void* pUser)
{
    if (pFrameInfo)
    {
        // 輸出時加上當前系統時間
        char   szInfo[128] = { 0 };
        SYSTEMTIME sys;
        GetLocalTime(&sys);
        sprintf_s(szInfo, 128, "[%d-%02d-%02d %02d:%02d:%02d:%04d] : GetOneFrame succeed, width[%d], height[%d]", sys.wYear, sys.wMonth,
            sys.wDay, sys.wHour, sys.wMinute, sys.wSecond, sys.wMilliseconds, pFrameInfo->nWidth, pFrameInfo->nHeight);
        printf("%s\n", szInfo);
    }
}
3.3.6 顯示圖像

可以通過函數MV_CC_Display(IN void* handle, IN void* hWnd)來實時顯示采集到的圖像。該函數需要在MV_CC_StartGrabbing之后調用,顯示采集到的圖像。如果相機當前采集圖像是JPEG壓縮的格式,則不支持調用該函數接口進行顯示。

/************************************************************************/
/* 6.顯示圖像                   MV_CC_Display                           */
/************************************************************************/
//獲取窗口句柄
HWND MainWndID = (HWND)this->ui.label->winId();
//顯示圖像
nRet = MV_CC_Display(m_handle, MainWndID);
3.3.7 保存圖像

可以通過函數MV_CC_SaveImage(IN&OUT MV_SAVE_IMAGE_PARAM* pSaveParam)將原始圖像數據轉換成圖片格式並保存在指定內存里,再通過函數fwrite寫入文件中。

也可以通過函數MV_CC_SaveImageEx將原始圖像數據轉換成圖片格式並保存在指定內存中,可支持設置JPEG編碼質量。

    /************************************************************************/
    /* 7.保存圖像           MV_CC_SaveImage/MV_CC_SaveImageEx               */
    /************************************************************************/
    //圖片數據輸入輸出參數            
    MV_SAVE_IMAGE_PARAM stParam = { 0 };
    //源數據                 
    stParam.pData = pFrameBuf;                //原始圖像數據
    stParam.nDataLen = stInfo.nFrameLen;    //原始圖像數據長度
    stParam.enPixelType = stInfo.enPixelType;  //原始圖像數據的像素格式YUYV
    stParam.nWidth = stInfo.nWidth;       //圖像寬
    stParam.nHeight = stInfo.nHeight;      //圖像高   
    //目標數據
    /**
    enum _MV_SAVE_IAMGE_TYPE_{
    MV_Image_Undefined  = 0, //未定義
    MV_Image_Bmp        = 1, //BMP圖片
    MV_Image_Jpeg       = 2, //JPEG圖片
    MV_Image_Png        = 3, //PNG圖片,暫不支持
    MV_Image_Tif        = 4, //TIF圖片,暫不支持
    }MV_SAVE_IAMGE_TYPE
    **/
    stParam.enImageType = MV_Image_Bmp;            
    stParam.nBufferSize = nBufSize;                 //存儲節點的大小
    unsigned char* pImage = (unsigned char*)malloc(nBufSize);
    stParam.pImageBuffer = pImage;      //輸出數據緩沖區,存放轉換之后的圖片數據 
    nRet = MV_CC_SaveImage(&stParam);
    //將轉換之后圖片數據保存成文件
    FILE* fp = fopen("image", "wb");
    fwrite(pImage, 1, stParam.nImageLen, fp);
    fclose(fp);
    free(pImage);
3.3.8 停止抓圖

可通過函數 MV_CC_StopGrabbing(IN void* handle)來停止抓圖。

只輸入一個handle即可成功停止抓圖,便沒有數據流動了。

/************************************************************************/
/* 6. 停止抓圖  MV_CC_StopGrabbing                                      */
/************************************************************************/
//停止采集圖像 
nRet = MV_CC_StopGrabbing(m_handle);
3.3.9 關閉設備

可通過函數MV_CC_CloseDevice(IN void* handle)來關閉設備。

只輸入一個handle即可成功關閉設備。

/************************************************************************/
/* 7. 關閉設備  MV_CC_CloseDevice                                       */
/************************************************************************/
//關閉設備,釋放資源
nRet = MV_CC_CloseDevice(m_handle);
3.3.10 銷毀句柄

可通過函數MV_CC_DestroyHandle(IN void * handle)來銷毀句柄。

只輸入一個handle即可銷毀句柄。

/************************************************************************/
/* 8. 銷毀句柄 MV_CC_DestroyHandle */
/************************************************************************/
//銷毀句柄,釋放資源
nRet = MV_CC_DestroyHandle(m_handle);

4 混合編程

4.1 與Qt混合編程

可通過函數memcpy(OUT void* dst, IN void const* src, IN size_t size) 把資源內存(src所指向的內存區域)拷貝到目標內存(dest所指向的內存區域),從而將unsigned char*格式的圖像數據轉換為QImage格式的圖像數據,這里要注意輸入數據的格式,代碼中輸入的unsigned char* pFrameBuf數據格式分別為Mono8的灰度圖像和RGB8_Packed的彩色圖像。

/**************************unsigned char* 圖像轉換為QImage************************/
//新建一個灰度圖像image,對應灰度相機Mono8的灰度圖像
QImage * image = new QImage(stInfo.nWidth, stInfo.nHeight,QImage::Format_Indexed8);
//memcpy 函數用於 把資源內存(src所指向的內存區域)拷貝到目標內存(dest所指向的內存區域)
//bits()方法獲取圖像像素字節數據的首地址
memcpy((*image).bits(), pFrameBuf, stInfo.nWidth*stInfo.nHeight); /************************************************/
//新建一個彩色圖像image,對應彩色相機RGB8_Packed的彩色圖像
// QImage::Format_RGB888,存入格式為R, G, B 對應 0,1,2
QImage *image = new QImage(stInfo.nWidth, stInfo.nHeight,QImage::Format_RGB888);
memcpy((*image).bits(), pFrameBuf, stInfo.nWidth * stInfo.nHeight * 3);

4.2 與Halcon混合編程

4.2.1 配置Halcon開發環境

在VS2015中配置Halcon開發環境,具體可以參考我發布的文章《VS2015_Qt5_Halcon混合編程》第二章。

4.2.2 數據格式轉換

通過Halcon中的函數GenImage3 (OUT HObject* ImageRGB, IN const HTuple& Type, IN const HTuple& Width, IN const HTuple& Height, IN const HTuple& PixelPointerRed, IN const HTuple& PixelPointerGreen, IN const HTuple& PixelPointerBlue),將SDK中獲得的unsigned char*格式的原始圖像數據轉換為HAlcon中使用的HObject格式的圖像數據,如果輸入圖像是灰度圖像則通過函數GenImage1轉換。

這里要注意獲取的那一幀原始圖像數據的格式,原始圖像數據格式不一樣,甚至是RGB三個分量的順序不同,轉換算法就得進行調整,下面代碼中原始圖像數據unsigned char* pFrameBuf的格式為RGB8_Packed。

轉換算法為:由於原始圖像數據pFrameBuf的格式是RGB8_Packed,存儲格式為RGB RGB RGB…,所以將pFrameBuf通過for循環進行拆分,分別存放入新建的三個顏色分量中,再通過函數GenImage3轉換為HObject格式的ho_Image。

/**************************unsigned char* 圖像轉換為HObject************************/
int hgt = stInfo.nHeight;
int wid = stInfo.nWidth;
unsigned char * dataRed = new unsigned char[wid * hgt];
unsigned char * dataGreen = new unsigned char[wid * hgt];
unsigned char * dataBlue = new unsigned char[wid * hgt];
unsigned char * data = new unsigned char[wid * hgt * 3];
memcpy(data, pFrameBuf, wid * hgt * 3);
for (int i = 0; i <wid * hgt; i++)
{
    dataRed[i] = (data[3 * i ]);
    dataGreen[i] = data[3 * i + 1];
    dataBlue[i] = data[3 * i +2];
}
GenImage3(&ho_Image, "byte", wid, hgt, (Hlong)(dataRed), (Hlong)(dataGreen), (Hlong)(dataBlue));
WriteImage(ho_Image, "bmp", 0, HTuple("E:/code/Photo/") + 1);
Sleep(500);
delete[] dataRed;
delete[] dataGreen;
delete[] dataBlue;
delete[] data;

5 遇到的問題

1) 沒注意從相機中獲取的一幀原始圖像的格式,以為默認就是RGB24格式的,導致后面的轉換出了bug,找了好久才發現默認的格式是YUYV的。解決辦法是:a.通過海康相機自帶的客戶端設置像素格式為RGB8_Packed,b.通過函數MV_CC_SetPixelFormat設置相機圖像的像素格式

//設置相機圖像的像素格式
unsigned int enValue = PixelType_Gvsp_RGB8_Packed;
nRet = MV_CC_SetPixelFormat(m_handle, enValue);
if (MV_OK != nRet)
{
    printf("error: SetPixelFormat fail [%x]\n", nRet);
    return;
}

2) 沒有注意例程中說明的“上層應用程序需要根據幀率,控制好調用該接口的頻率”,我的相機的采集速度是一秒20幀,結果我程序獲取的幀速超過了這個采集速度,所以程序在while循環獲取一幀圖像時,程序報錯。解決辦法有:a.采用回調函數采圖,b.不用循環采圖沒有問題,c.加一個Sleep函數也可以解決。

6 本文程序代碼

本文程序代碼和操作手冊已經被上傳到CSDN中,其中代碼有兩份:

海康相機SDK二次開發與Qt混合編程

海康相機SDK二次開發與Halcon混合編程

7 參考文獻

VS2015_Qt5_Halcon混合編程

BYTE類型轉換Hobject類型

代碼資料:Samuel_yin / IplImageToHImage.cpp

版權聲明:

本文首發於onefish51的博客(http://www.cnblogs.com/onefish51https://blog.csdn.net/weixin_31075593),未經允許不得轉載,版權所有,侵權必究。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM