从零到一:在Ubuntu上集成OpenCV与海康GigE相机SDK进行图像采集

张开发
2026/5/9 16:06:50 15 分钟阅读
从零到一:在Ubuntu上集成OpenCV与海康GigE相机SDK进行图像采集
1. 环境准备搭建Ubuntu开发环境在开始集成海康GigE相机之前我们需要先准备好开发环境。我推荐使用Ubuntu 22.04 LTS版本这个版本长期支持且稳定性较好。记得第一次配置环境时我因为漏装了几个依赖包导致后续编译OpenCV时各种报错折腾了大半天才解决。首先更新系统软件包sudo apt update sudo apt upgrade -y安装基础开发工具链sudo apt install -y build-essential cmake git pkg-config安装图像处理相关依赖sudo apt install -y libjpeg-dev libpng-dev libtiff-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libxvidcore-dev libx264-dev安装Python3开发环境可选但建议安装sudo apt install -y python3-dev python3-numpy对于海康相机SDK我们还需要安装网络相关依赖sudo apt install -y libgtk-3-dev libcanberra-gtk3-module libatlas-base-dev gfortran我建议在/opt目录下创建工作空间这样便于管理sudo mkdir /opt/opencv_ws sudo chown -R $USER:$USER /opt/opencv_ws cd /opt/opencv_ws2. 安装OpenCV与contrib模块海康相机采集的图像最终要通过OpenCV处理所以我们需要完整安装OpenCV。我选择从源码编译安装这样可以灵活控制模块和版本。实测下来OpenCV 4.6.0版本与海康SDK兼容性最好。首先下载源码git clone https://github.com/opencv/opencv.git -b 4.6.0 git clone https://github.com/opencv/opencv_contrib.git -b 4.6.0创建编译目录并配置cd opencv mkdir build cd build cmake -D CMAKE_BUILD_TYPERELEASE \ -D CMAKE_INSTALL_PREFIX/usr/local \ -D OPENCV_EXTRA_MODULES_PATH../../opencv_contrib/modules \ -D WITH_TBBON \ -D WITH_V4LON \ -D WITH_QTOFF \ -D WITH_OPENGLON \ -D WITH_CUDAOFF \ -D OPENCV_ENABLE_NONFREEON \ -D BUILD_EXAMPLESON ..开始编译根据CPU核心数调整-j参数make -j8安装并配置环境sudo make install sudo ldconfig验证安装是否成功pkg-config --modversion opencv43. 配置海康GigE相机环境海康相机需要通过专门的MVS软件进行管理和配置。我刚开始使用时发现相机无法识别后来发现是IP地址配置问题。这里分享下我的踩坑经验。首先从海康官网下载MVS软件wget https://www.hikrobotics.com/cn2/source/support/software/MVS_2.1.2_Linux64.tar.gz解压并安装tar -xzvf MVS_2.1.2_Linux64.tar.gz cd MVS_2.1.2_Linux64/deb sudo dpkg -i MVS-2.1.2_amd64.deb配置网络接口非常重要我建议创建一个专门的网络配置文件sudo nano /etc/netplan/99-hikvision.yaml添加以下内容根据实际网络环境调整network: version: 2 renderer: networkd ethernets: enp3s0: dhcp4: no addresses: [192.168.16.68/24] gateway4: 192.168.16.1 nameservers: addresses: [202.96.128.166] mtu: 9000应用网络配置sudo netplan apply启动MVS软件检查相机连接cd /opt/MVS/bin ./MVS在MVS界面中你应该能看到已连接的相机设备。如果没有显示检查网线连接和IP配置是否正确。4. 创建CMake项目集成SDK现在进入核心环节 - 将海康SDK集成到我们的项目中。我建议创建一个独立的相机控制类这样代码结构更清晰也方便复用。首先创建项目目录结构mkdir -p ~/projects/hik_camera/{include,src,build} cd ~/projects/hik_camera从MVS安装目录复制SDK文件cp -r /opt/MVS/include/ . cp -r /opt/MVS/lib/ .创建CMakeLists.txt文件cmake_minimum_required(VERSION 3.10) project(hik_camera_opencv) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(OpenCV REQUIRED) include_directories( ${OpenCV_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/include ) link_directories( ${CMAKE_SOURCE_DIR}/lib/64 ) add_executable(hik_camera_opencv src/main.cpp src/camera_class.cpp) target_link_libraries(hik_camera_opencv ${OpenCV_LIBS} MvCameraControl )创建相机控制类头文件include/camera_class.h#ifndef CAMERA_CLASS_H #define CAMERA_CLASS_H #include opencv2/opencv.hpp #include MvCameraControl.h class HikCamera { public: HikCamera(); ~HikCamera(); bool open(); void close(); bool grabFrame(cv::Mat frame); bool isOpened() const; void setExposureAuto(bool enable); void setGainAuto(bool enable); void setWhiteBalanceAuto(bool enable); private: void* handle_ nullptr; bool is_opened_ false; unsigned char* rgb_buffer_ nullptr; }; #endif5. 实现相机控制类相机控制类的实现是整个项目的核心我花了大量时间调试图像采集和格式转换部分。下面是经过实战验证的实现方案。src/camera_class.cpp#include camera_class.h #include iostream HikCamera::HikCamera() { rgb_buffer_ new unsigned char[1920*1080*3]; // 根据相机分辨率调整 } HikCamera::~HikCamera() { close(); delete[] rgb_buffer_; } bool HikCamera::open() { MV_CC_DEVICE_INFO_LIST stDeviceList; memset(stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); int nRet MV_CC_EnumDevices(MV_GIGE_DEVICE, stDeviceList); if (nRet ! MV_OK || stDeviceList.nDeviceNum 0) { std::cerr No devices found! std::endl; return false; } nRet MV_CC_CreateHandle(handle_, stDeviceList.pDeviceInfo[0]); if (nRet ! MV_OK) { std::cerr Create handle failed! std::endl; return false; } nRet MV_CC_OpenDevice(handle_); if (nRet ! MV_OK) { std::cerr Open device failed! std::endl; return false; } // 设置触发模式为连续采集 nRet MV_CC_SetEnumValue(handle_, TriggerMode, 0); if (nRet ! MV_OK) { std::cerr Set trigger mode failed! std::endl; } // 开始采集图像 nRet MV_CC_StartGrabbing(handle_); if (nRet ! MV_OK) { std::cerr Start grabbing failed! std::endl; return false; } is_opened_ true; return true; } void HikCamera::close() { if (handle_) { MV_CC_StopGrabbing(handle_); MV_CC_CloseDevice(handle_); MV_CC_DestroyHandle(handle_); handle_ nullptr; } is_opened_ false; } bool HikCamera::grabFrame(cv::Mat frame) { if (!is_opened_) return false; MV_FRAME_OUT stOutFrame; memset(stOutFrame, 0, sizeof(MV_FRAME_OUT)); int nRet MV_CC_GetImageBuffer(handle_, stOutFrame, 1000); if (nRet ! MV_OK) { std::cerr Get image buffer failed! std::endl; return false; } MV_CC_PIXEL_CONVERT_PARAM stConvertParam; memset(stConvertParam, 0, sizeof(MV_CC_PIXEL_CONVERT_PARAM)); stConvertParam.nWidth stOutFrame.stFrameInfo.nWidth; stConvertParam.nHeight stOutFrame.stFrameInfo.nHeight; stConvertParam.pSrcData stOutFrame.pBufAddr; stConvertParam.nSrcDataLen stOutFrame.stFrameInfo.nFrameLen; stConvertParam.enSrcPixelType stOutFrame.stFrameInfo.enPixelType; stConvertParam.enDstPixelType PixelType_Gvsp_RGB8_Packed; stConvertParam.pDstBuffer rgb_buffer_; stConvertParam.nDstBufferSize stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * 3; nRet MV_CC_ConvertPixelType(handle_, stConvertParam); if (nRet ! MV_OK) { std::cerr Convert pixel type failed! std::endl; MV_CC_FreeImageBuffer(handle_, stOutFrame); return false; } frame cv::Mat(stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nWidth, CV_8UC3, rgb_buffer_); cv::cvtColor(frame, frame, cv::COLOR_RGB2BGR); MV_CC_FreeImageBuffer(handle_, stOutFrame); return true; } // 其他设置函数实现...6. 主程序实现与测试有了相机控制类后主程序就变得非常简单了。下面是一个完整的采集示例包含基本的错误处理和参数调整功能。src/main.cpp#include iostream #include opencv2/opencv.hpp #include camera_class.h int main() { HikCamera camera; if (!camera.open()) { std::cerr Failed to open camera! std::endl; return -1; } cv::namedWindow(Hikvision Camera, cv::WINDOW_NORMAL); cv::Mat frame; while (true) { if (!camera.grabFrame(frame)) { std::cerr Failed to grab frame! std::endl; break; } cv::imshow(Hikvision Camera, frame); int key cv::waitKey(10); if (key 27) { // ESC退出 break; } else if (key a) { camera.setExposureAuto(true); } else if (key m) { camera.setExposureAuto(false); } } camera.close(); return 0; }编译并运行项目cd ~/projects/hik_camera/build cmake .. make ./hik_camera_opencv7. 性能优化与常见问题解决在实际使用中我发现几个影响性能的关键点和常见问题网络配置优化确保使用千兆网线Cat5e或以上禁用网络接口的节能模式sudo ethtool -s enp3s0 wol d调整MTU值为9000可以显著提高吞吐量采集帧率不稳定在相机类初始化时设置合适的Packet Sizeint nPacketSize MV_CC_GetOptimalPacketSize(handle_); if (nPacketSize 0) { MV_CC_SetIntValue(handle_, GevSCPSPacketSize, nPacketSize); }检查网络交换机配置确保没有QoS限制图像格式转换优化如果不需要彩色图像可以直接采集灰度图像减少转换开销预分配图像缓冲区避免频繁内存分配常见错误处理相机无法连接检查IP配置和防火墙设置图像撕裂降低采集帧率或优化网络环境SDK初始化失败确保LD_LIBRARY_PATH包含SDK库路径多线程采集 对于高性能应用建议使用单独的采集线程#include thread #include atomic std::atomicbool running(true); cv::Mat current_frame; void captureThread(HikCamera camera) { cv::Mat frame; while (running) { if (camera.grabFrame(frame)) { std::lock_guardstd::mutex lock(frame_mutex); frame.copyTo(current_frame); } } }8. 高级功能扩展基础功能实现后可以进一步扩展相机的高级功能参数保存与加载bool HikCamera::saveParameters(const std::string filename) { return MV_CC_FeatureSave(handle_, filename.c_str()) MV_OK; } bool HikCamera::loadParameters(const std::string filename) { return MV_CC_FeatureLoad(handle_, filename.c_str()) MV_OK; }硬件触发模式bool HikCamera::setTriggerMode(bool hardware_trigger) { int mode hardware_trigger ? 1 : 0; return MV_CC_SetEnumValue(handle_, TriggerMode, mode) MV_OK; }ROI设置bool HikCamera::setROI(int x, int y, int width, int height) { bool ret true; ret MV_CC_SetIntValue(handle_, Width, width) MV_OK; ret MV_CC_SetIntValue(handle_, Height, height) MV_OK; ret MV_CC_SetIntValue(handle_, OffsetX, x) MV_OK; ret MV_CC_SetIntValue(handle_, OffsetY, y) MV_OK; return ret; }事件回调机制void __stdcall eventCallback(unsigned int nMsgType, void* pUser) { // 处理相机事件 } bool HikCamera::registerEventCallback() { return MV_CC_RegisterEventCallBack(handle_, eventCallback, this) MV_OK; }与ROS集成 如果需要将相机接入ROS系统可以创建image_transport发布者#include ros/ros.h #include image_transport/image_transport.h #include cv_bridge/cv_bridge.h image_transport::Publisher pub; void publishFrame(const cv::Mat frame) { sensor_msgs::ImagePtr msg cv_bridge::CvImage( std_msgs::Header(), bgr8, frame).toImageMsg(); pub.publish(msg); }在实际项目中我发现将海康相机与OpenCV深度集成后可以构建非常强大的机器视觉系统。从最初的设备连接到最终的图像处理每个环节都需要仔细调试。特别是在工业环境中稳定性往往比性能更重要因此建议添加完善的重连机制和错误处理。

更多文章