海康威视相机开发(一)

您所在的位置:网站首页 海康威视的图片 海康威视相机开发(一)

海康威视相机开发(一)

#海康威视相机开发(一)| 来源: 网络整理| 查看: 265

海康威视相机 + opencv初体验 简单实现打开相机的连接,打开,控制相机的帧率,曝光时间等,使用opencv实时处理图片并输出

​​

平台

windows10 + vs2019 + MV-CA050-10GM + HIKCAMERA SDK版本v3.3.0 + OpenCV3.3.1

环境配置 下载

HIKCAMERA SDK下载:https://www.hikrobotics.com/service/download/0/0

海康威视相机开发(一)_程序人生

下载安装后,会有一个客户端和SDK

海康威视相机开发(一)_#include_02

我们开发所需要的SDK就在Development文件夹里边,里边有我们所需的头文件、库文件、文档、教程demo等等,是非常好学习资料。

OpenCV3.3.1下载:

配置

配置比较简单,将我们所需的可执行文件、库文件、头文件、附加依赖项包含进来就可以了

可执行文件

海康威视相机开发(一)_#include_03

这一步也可以在环境变量里添加到PATH里面,主要是为了让OpenCV正常运行。出现错误,重启一次就OK。

头文件

海康威视相机开发(一)_经验分享_04

库文件

海康威视相机开发(一)_经验分享_05

附加依赖项

海康威视相机开发(一)_#include_06

开发流程

Development文件夹里边里边的开发指南写的相当完善:

设备连接

对设备进行操作,实现图像采集、参数配置等功能,需要先连接设备(打开设备),具体流程如下图所示。

海康威视相机开发(一)_经验分享_07

详细步骤

(可选)调用 MV_CC_EnumDevices() 枚举子网内指定传输协议对应的所有设备。 可通过nTLayerType在结构 MV_CC_DEVICE_INFO() 中获取设备信息。(可选)在打开指定设备前,调用 MV_CC_IsDeviceAccessible() 检查指定设备是否可访问。调用 MV_CC_CreateHandle() 创建设备句柄。调用 MV_CC_OpenDevice() 打开设备。(可选)调用 MV_CC_GetAllMatchInfo()以获取设备信息。调用 MV_CC_CloseDevice() 关闭设备。调用 MV_CC_DestroyHandle() 销毁句柄并释放资源。 主动取流流程

SDK提供主动获取图像的接口,用户可以在开启取流后直接调用此接口获取图像,也可以使用异步方式(线程、定时器等)获取图像。

主动获取图像有两种方式(两种方式不能同时使用):

方式一:调用 MV_CC_StartGrabbing() 开始采集,需要自己开启一个buffer,然后在应用层循环调用 MV_CC_GetOneFrameTimeout() 获取指定像素格式的帧数据,获取帧数据时上层应用程序需要根据帧率控制好调用该接口的频率。方式二:调用 MV_CC_StartGrabbing() 开始采集,然后在应用层调用 MV_CC_GetImageBuffer() 获取指定像素格式的帧数据,然后调用 MV_CC_FreeImageBuffer() 释放buffer,获取帧数据时上层应用程序需要根据帧率控制好调用该接口的频率。海康威视相机开发(一)_程序人生_08 详细步骤(可选)调用 MV_CC_EnumDevices() 枚举子网内指定传输协议对应的所有设备。 可通过nTLayerType在结构 MV_CC_DEVICE_INFO() 中获取设备信息。(可选)打开指定设备前,调用 MV_CC_IsDeviceAccessible() 检查指定设备是否可访问。调用 MV_CC_CreateHandle() 创建设备句柄。调用 MV_CC_OpenDevice() 打开设备。(可选)执行以下一个或多个操作以获取/设置相机不同类型的参数。 • 获取/设置Int类型节点值 调用 MV_CC_GetIntValue() / MV_CC_SetIntValue() • 获取/设置Float类型节点值 调用 MV_CC_GetFloatValue() / MV_CC_SetFloatValue() • 获取/设置Enum类型节点值 调用 MV_CC_GetEnumValue() / MV_CC_SetEnumValue() • 获取/设置Bool类型节点值 调用 MV_CC_GetBoolValue() / MV_CC_SetBoolValue() • 获取/设置String类型节点值 调用 MV_CC_GetStringValue() / MV_CC_SetStringValue() • 设置Command类型节点值 调用 MV_CC_SetCommandValue()图像采集: • (可选)调用 MV_CC_SetImageNodeNum() 设置图像缓存节点个数。当获取的图像数超过这个设定值,最早的图像数据会被自动丢弃。 • 调用 MV_CC_StartGrabbing() 开始取流。 • 对于原始图像数据,可调用 MV_CC_ConvertPixelType() 转换图像的像素格式,也可调用 MV_CC_SaveImage() 转换成JPEG或BMP格式的图片,并保存成图片文件。 • 在应用程序层中重复调用 MV_CC_GetOneFrameTimeout() 来获取图片数据。调用 MV_CC_StopGrabbing() 停止采集。调用 MV_CC_CloseDevice() 关闭设备。调用 MV_CC_DestroyHandle() 销毁句柄并释放资源。

下面尝试使用第一种方式进行主动获取图片

代码#include #include #include #include "windows.h"#include "MvCameraControl.h"#include "opencv2/imgproc/imgproc.hpp"#include "opencv2/highgui/highgui.hpp"#include "string.h"#include "time.h"HWND g_hwnd = NULL;bool g_bExit = false;unsigned int g_nPayloadSize = 0;// 以时间作为图片名 精确到毫秒std::string GetTimeAsFileName(){ SYSTEMTIME st = { 0 }; GetLocalTime(&st); //获取当前时间 可精确到ms char filename[100] = { 0 }; sprintf(filename, "pictures_src/%d%02d%02d_%02d%02d%02d%03d.bmp", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); return filename;}// ch:等待按键输入 | en:Wait for key pressvoid WaitForKeyPress(void){ while (!_kbhit()) { Sleep(10); } _getch();}bool PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo){ if (NULL == pstMVDevInfo) { printf("The Pointer of pstMVDevInfo is NULL!\n"); return false; } if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE) { int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24); int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16); int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8); int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff); // ch:打印当前相机ip和用户自定义名字 | en:print current ip and user defined name printf("CurrentIp: %d.%d.%d.%d\n", nIp1, nIp2, nIp3, nIp4); printf("UserDefinedName: %s\n\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName); printf("DeviceVersion: %s\n\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chDeviceVersion); printf("ManufacturerName: %s\n\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chManufacturerName); } else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE) { printf("UserDefinedName: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName); printf("Serial Number: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chSerialNumber); printf("Device Number: %d\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.nDeviceNumber); } else { printf("Not support.\n"); } return true;}int RGB2BGR(unsigned char* pRgbData, unsigned int nWidth, unsigned int nHeight){ if (NULL == pRgbData) { return MV_E_PARAMETER; } for (unsigned int j = 0; j < nHeight; j++) { for (unsigned int i = 0; i < nWidth; i++) { unsigned char red = pRgbData[j * (nWidth * 3) + i * 3]; pRgbData[j * (nWidth * 3) + i * 3] = pRgbData[j * (nWidth * 3) + i * 3 + 2]; pRgbData[j * (nWidth * 3) + i * 3 + 2] = red; } } return MV_OK;}// convert data stream in Mat formatbool Convert2Mat(MV_FRAME_OUT_INFO_EX* pstImageInfo, unsigned char* pData, cv::Mat& mat_img){ cv::Mat srcImage; if (pstImageInfo->enPixelType == PixelType_Gvsp_Mono8) { srcImage = cv::Mat(pstImageInfo->nHeight, pstImageInfo->nWidth, CV_8UC1, pData); } else if (pstImageInfo->enPixelType == PixelType_Gvsp_RGB8_Packed) { RGB2BGR(pData, pstImageInfo->nWidth, pstImageInfo->nHeight); srcImage = cv::Mat(pstImageInfo->nHeight, pstImageInfo->nWidth, CV_8UC3, pData); } else { printf("unsupported pixel format\n"); return false; } if (NULL == srcImage.data) { return false; } //save converted image in a local file std::string filename = GetTimeAsFileName(); try {#if defined (VC9_COMPILE) cvSaveImage(filename, &(IplImage(srcImage)));#else //cv::imwrite(filename, srcImage);#endif } catch (cv::Exception& ex) { fprintf(stderr, "Exception saving image to bmp format: %s\n", ex.what()); } mat_img = srcImage.clone(); srcImage.release(); return true;}LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){ switch (msg) { case WM_DESTROY: PostQuitMessage(0); g_hwnd = NULL; break; } return DefWindowProc(hWnd, msg, wParam, lParam);}static unsigned int __stdcall CreateRenderWindow(void* pUser){ HINSTANCE hInstance = ::GetModuleHandle(NULL); //获取应用程序的模块句柄 WNDCLASSEX wc; wc.cbSize = sizeof(wc); wc.style = CS_HREDRAW | CS_VREDRAW; //窗口的风格 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = ::LoadIcon(NULL, IDI_APPLICATION); //图标风格 wc.hIconSm = ::LoadIcon(NULL, IDI_APPLICATION); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); //背景色 wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); //鼠标风格 wc.lpfnWndProc = WndProc; //自定义消息处理函数 wc.lpszMenuName = NULL; wc.lpszClassName = "RenderWindow"; //该窗口类的名称 if (!RegisterClassEx(&wc)) { return 0; } DWORD style = WS_OVERLAPPEDWINDOW; DWORD styleEx = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; RECT rect = { 0, 0, 640, 480 }; AdjustWindowRectEx(&rect, style, false, styleEx); HWND hWnd = CreateWindowEx(styleEx, "RenderWindow", "Display", style, 0, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, NULL); if (hWnd == NULL) { return 0; } ::UpdateWindow(hWnd); ::ShowWindow(hWnd, SW_SHOW); g_hwnd = hWnd; MSG msg = { 0 }; while (msg.message != WM_QUIT) { if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return 0;}static unsigned int __stdcall WorkThread(void* pUser){ int nRet = MV_OK; MV_FRAME_OUT_INFO_EX stImageInfo = { 0 }; MV_DISPLAY_FRAME_INFO stDisplayInfo = { 0 }; unsigned char* pData = (unsigned char*)malloc(sizeof(unsigned char) * (g_nPayloadSize)); if (pData == NULL) { return 0; } unsigned int nDataSize = g_nPayloadSize; cv::namedWindow("test_img", cv::WINDOW_FREERATIO); cv::namedWindow("edge_img", cv::WINDOW_FREERATIO); for (int i = 0; ; i++) { cv::waitKey(0); nRet = MV_CC_GetOneFrameTimeout(pUser, pData, nDataSize, &stImageInfo, 1000); if (nRet == MV_OK) { printf("Get One Frame: Width[%d], Height[%d], nFrameNum[%d]\n", stImageInfo.nWidth, stImageInfo.nHeight, stImageInfo.nFrameNum); if (g_hwnd) { stDisplayInfo.hWnd = g_hwnd; stDisplayInfo.pData = pData; stDisplayInfo.nDataLen = stImageInfo.nFrameLen; stDisplayInfo.nWidth = stImageInfo.nWidth; stDisplayInfo.nHeight = stImageInfo.nHeight; stDisplayInfo.enPixelType = stImageInfo.enPixelType; MV_CC_DisplayOneFrame(pUser, &stDisplayInfo); // 数据去转换 bool bConvertRet = false; cv::Mat mat_img, threshold_img, edge, gray; bConvertRet = Convert2Mat(&stImageInfo, pData, mat_img); // print result if (bConvertRet) { printf("OpenCV format convert finished.\n"); cv::threshold(mat_img, threshold_img, 100, 255, cv::THRESH_BINARY); //cv::cvtColor(mat_img, gray, cv::COLOR_BGR2GRAY); cv::Canny(mat_img, edge, 100, 200, 3); cv::imshow("test_img", threshold_img); cv::imshow("edge_img", edge); /* free(pData); pData = NULL;*/ } else { printf("OpenCV format convert failed.\n"); free(pData); /* pData = NULL; break;*/ } } } else { printf("No data[0x%x]\n", nRet); } if (g_bExit) { break; } } cv::destroyAllWindows(); free(pData); return 0;}int main(){ int nRet = MV_OK; // void* handle = NULL; do { // 获取SDK版本信息 unsigned int sdk_V = MV_CC_GetSDKVersion(); printf("SDK version is [0x%x]\n", sdk_V); // 获取支持的传输层 int tls = MV_CC_EnumerateTls(); printf("surpport tls is: [%d]\n", tls); // 根据厂商名字枚举设备 /* MV_CC_DEVICE_INFO_LIST stDeviceList_ex = { 0 }; nRet = MV_CC_EnumDevicesEx(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList_ex, "hikvision"); if (MV_OK != nRet) { printf("Enum Devices with ManufacturerName fail! nRet [0x%x]\n", nRet); break; }*/ // ch:枚举设备 | en:Enum device MV_CC_DEVICE_INFO_LIST stDeviceList = { 0 }; nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList); if (MV_OK != nRet) { printf("Enum Devices fail! nRet [0x%x]\n", nRet); break; } if (stDeviceList.nDeviceNum > 0) { for (unsigned int i = 0; i < stDeviceList.nDeviceNum; i++) { printf("[device %d]:\n", i); MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i]; /* bool access = MV_CC_IsDeviceAccessible(pDeviceInfo, 1); if (!access) { printf("[device %d]:could not access... nRet:[0x%x]\n", i, nRet); }*/ if (NULL == pDeviceInfo) { break; } PrintDeviceInfo(pDeviceInfo); } } else { printf("Find No Devices!\n"); break; } printf("Please Input camera index:"); unsigned int nIndex = 0; scanf_s("%d", &nIndex); if (nIndex >= stDeviceList.nDeviceNum) { printf("Input error!\n"); break; } // ch:选择设备并创建句柄 | en:Select device and create handle nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]); if (MV_OK != nRet) { printf("Create Handle fail! nRet [0x%x]\n", nRet); break; } // ch:打开设备 | en:Open device nRet = MV_CC_OpenDevice(handle); if (MV_OK != nRet) { printf("Open Device fail! nRet [0x%x]\n", nRet); break; } // 设置相机常见参数 nRet = MV_CC_SetFrameRate(handle, 10.f); if (MV_OK != nRet) { printf("set FrameRate fail! nRet [0x%x]\n", nRet); break; } nRet = MV_CC_SetExposureTime(handle, 2000.f); if (MV_OK != nRet) { printf("set Exposure Time fail! nRet [0x%x]\n", nRet); break; } // 保存相机参数 printf("Start export the camera properties to the file\n"); printf("Wait......\n"); nRet = MV_CC_FeatureSave(handle, "FeatureFile.ini"); if (MV_OK != nRet) { printf("Save Feature fail! nRet [0x%x]\n", nRet); break; } printf("Finish export the camera properties to the file\n\n"); // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera) if (stDeviceList.pDeviceInfo[nIndex]->nTLayerType == MV_GIGE_DEVICE) { int nPacketSize = MV_CC_GetOptimalPacketSize(handle); if (nPacketSize > 0) { nRet = MV_CC_SetIntValue(handle, "GevSCPSPacketSize", nPacketSize); if (nRet != MV_OK) { printf("Warning: Set Packet Size fail nRet [0x%x]!", nRet); } } else { printf("Warning: Get Packet Size fail nRet [0x%x]!", nPacketSize); } } // ch:设置触发模式为off | en:Set trigger mode as off nRet = MV_CC_SetEnumValue(handle, "TriggerMode", 0); if (MV_OK != nRet) { printf("Set Trigger Mode fail! nRet [0x%x]\n", nRet); break; } // ch:获取数据包大小 | en:Get payload size MVCC_INTVALUE stParam = { 0 }; nRet = MV_CC_GetIntValue(handle, "PayloadSize", &stParam); if (MV_OK != nRet) { printf("Get PayloadSize fail! nRet [0x%x]\n", nRet); break; } g_nPayloadSize = stParam.nCurValue; unsigned int nThreadID = 0; void* hCreateWindow = (void*)_beginthreadex(NULL, 0, CreateRenderWindow, handle, 0, &nThreadID); if (NULL == hCreateWindow) { break; } // ch:开始取流 | en:Start grab image nRet = MV_CC_StartGrabbing(handle); if (MV_OK != nRet) { printf("Start Grabbing fail! nRet [0x%x]\n", nRet); break; } nThreadID = 0; void* hThreadHandle = (void*)_beginthreadex(NULL, 0, WorkThread, handle, 0, &nThreadID); if (NULL == hThreadHandle) { break; } printf("Press a key to stop grabbing.\n"); WaitForKeyPress(); g_bExit = true; WaitForSingleObject(hThreadHandle, INFINITE); CloseHandle(hThreadHandle); // ch:停止取流 | en:Stop grab image nRet = MV_CC_StopGrabbing(handle); if (MV_OK != nRet) { printf("Stop Grabbing fail! nRet [0x%x]\n", nRet); break; } // ch:关闭设备 | Close device nRet = MV_CC_CloseDevice(handle); if (MV_OK != nRet) { printf("ClosDevice fail! nRet [0x%x]\n", nRet); break; } // ch:销毁句柄 | Destroy handle nRet = MV_CC_DestroyHandle(handle); if (MV_OK != nRet) { printf("Destroy Handle fail! nRet [0x%x]\n", nRet); break; } } while (0); if (nRet != MV_OK) { if (handle != NULL) { MV_CC_DestroyHandle(handle); handle = NULL; } } printf("Press a key to exit.\n"); WaitForKeyPress(); return 0;} 代码效果

选择好设备序号,我目前只有一个相机,所以设备号为0,按下回车,把鼠标放在窗口上边,按一下enter或空格取一张图片,display显示原图,剩下两个窗口分别显示二值化图和通过canny提取的边缘轮廓图。

海康威视相机开发(一)_机器学习_09



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3