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”路徑,目錄結構如下:
1.3 效果展示
建議安裝成功后,連接相機,打開MVS客戶端,查看相機連接和圖像預覽的效果,確認環境正常后,再開始基於SDK的二次開發。如下:
觀察三個指標:
1) 帶寬。正常值在100Mbps以上即認為正常;
2) 錯誤數。非0即表示有丟幀,不正常;
3) 丟包數。非0,不正常。參考第四章常見問題的解決方法。
2 產品概述
本章介紹SDK在整個機器視覺系統中的層次定位,可實現的功能,基本的開發調用流程,以及常用的接口。
2.1 SDK定位
2.2 基本接口調用流程
2.3 參數配置
1) 相機所有開放的參數可參考MVS的屬性樹,只要在屬性樹中看得到的節點,都可以通過SDK來獲取和設置相應的值。
2) 每個節點分別屬於哪種數據類型,可參考如下控件形式:
3) 節點的關鍵字,可參考以下表格:
e.g:
設置寬度為2592 ->MV_CC_SetIntValue(handle, “Width”, 2592);
4) 詳細的函數說明,可以參考SDK 使用手冊。
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項目
雙擊“hkcamera.ui”,如下圖所示添加一個Lable用於顯示,四個Push Button用於按鈕控制
3.2 配置海康相機SDK開發環境
3.2.1 添加附加包含目錄
項目 --- 屬性 --- 屬性頁 --- C/C++ --- 常規--- 附加包含目錄,添加如下路徑:
E:\code\Libraries\HKSDK\Includes
3.2.2 添加附加庫目錄
項目 --- 屬性 --- 屬性頁 --- 鏈接器 --- 常規 --- 附加庫目錄,添加如下路徑
E:\code\Libraries\HKSDK\Libraries\win64
3.2.3 添加附加依賴項
項目 --- 屬性 --- 屬性頁 --- 鏈接器 --- 輸入 --- 附加依賴項,添加
MvCameraControl.lib
3.3 SDK開發
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中,其中代碼有兩份:
7 參考文獻
代碼資料:Samuel_yin / IplImageToHImage.cpp
版權聲明:
本文首發於onefish51的博客(http://www.cnblogs.com/onefish51和https://blog.csdn.net/weixin_31075593),未經允許不得轉載,版權所有,侵權必究。