ROS2 C++中cv::VideoCapture打开本地视频失败问题求助
ROS2 C++节点中OpenCV打开本地视频失败的解决方法
问题概述
在ROS2 C节点中实现CamReader类,调用摄像头(cap.open(0))功能正常,但打开本地视频文件/home/pi/Meurtres_au_paradis.S01E01.mp4时失败,触发GStreamer相关错误。非ROS2环境下的C程序可正常播放该视频,需解决此问题以进行C++ DNN人脸识别(Python性能不足)。
相关代码
cam_reader.hpp
#include "rclcpp/rclcpp.hpp" #include "opencv2/opencv.hpp" #include "sensor_msgs/msg/image.hpp" #include "cv_bridge/cv_bridge.h" class CamReader : public rclcpp::Node { public: CamReader(); cv::VideoCapture cap; private: // Subscribers(未实现) // Publishers rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr image_publisher_; // Timers rclcpp::TimerBase::SharedPtr get_image_timer_; // Callbacks void get_image_callback_(); };
cam_reader.cpp(原代码)
#include <cam_reader/cam_reader.hpp> CamReader::CamReader () Node("cam_reader") // 语法错误:缺少初始化列表冒号 char videopath[] = "/home/pi/Meurtres_au_paradis.S01E01.mp4"; this->cap.open(*videopath); // 错误:传递char而非字符串指针 this->cap.open(0); // 覆盖了之前的视频打开操作 if (!this->cap.isOpened()) { RCLCPP_INFO(this->get_logger(), "Error opening camera"); } get_image_timer_ = this->create_wall_timer( std::chrono::milliseconds(2000), std::chrono::milliseconds(50), // 错误:create_wall_timer只接受一个周期参数 std::bind(&CamReader::get_image_callback_, this)); auto default_qos = rclcpp::QoS(rclcpp::SystemDefaultsQoS()); default_qos.reliable(); image_publisher_ = this->create_publisher<sensor_msgs::msg::Image>("/image", default_qos); } void CamReader::get_image_callback_() { cv::Mat frame; this->cap >> frame; cv_bridge::CvImage img_bridge; sensor_msgs::msg::Image cam_msg; std_msgs::msg::Header header; img_bridge = cv_bridge::CvImage(header, sensor_msgs::image_encodings::RGB8, frame); img_bridge.toImageMsg(cam_msg); image_publisher_->publish(cam_msg); } int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared()); // 错误:未指定类型 rclcpp::shutdown(); return 0; }
报错信息
WARN:0@0.203] global /home/pi/opencv/modules/videoio/src/cap_gstreamer.cpp (2401) handleMessage OpenCV | GStreamer warning: Embedded video playback halted; module v4l2src0 reported: Cannot identify device '/dev/video47'. WARN:0@0.203] global /home/pi/opencv/modules/videoio/src/cap_gstreamer.cpp (1356) open OpenCV | GStreamer warning: unable to start pipeline WARN:0@0.204] global /home/pi/opencv/modules/videoio/src/cap_gstreamer.cpp (862) isPipelinePlaying OpenCV | GStreamer warning: GStreamer: pipeline have not been created WARN:0@0.204] global /home/pi/opencv/modules/videoio/src/cap_v4l.cpp (902) open VIDEOIO(V4L2:/dev/video47): can't open camera by index [INFO] [1679388758.511573816] [cam_reader]: Error opening camera
解决方案
1. 修复代码中的语法错误
- 构造函数初始化:修正Node继承的初始化列表,改为
CamReader::CamReader() : Node("cam_reader") - 视频路径传递:直接传递字符串指针而非解引用char数组,即
this->cap.open(videopath),同时注释掉覆盖视频的this->cap.open(0) - 定时器参数:
create_wall_timer仅需一个周期参数,移除多余的std::chrono::milliseconds(2000) - main函数修正:
std::make_shared需指定类型,改为std::make_shared<CamReader>()
2. 强制指定OpenCV视频后端
ROS2环境下OpenCV可能默认优先使用GStreamer后端,但该后端对本地视频的兼容性不如FFmpeg。强制指定FFmpeg后端打开视频:
std::string videopath = "/home/pi/Meurtres_au_paradis.S01E01.mp4"; if (!this->cap.open(videopath, cv::CAP_FFMPEG)) { RCLCPP_ERROR(this->get_logger(), "Failed to open video file"); }
可通过cv::getBuildInformation()检查OpenCV是否编译了FFmpeg支持,确保输出中包含FFmpeg: YES。
3. 验证文件权限与路径
- 确认视频文件路径正确,可通过终端执行
ls /home/pi/Meurtres_au_paradis.S01E01.mp4验证 - 检查文件权限,确保当前用户有读取权限:
chmod +r /home/pi/Meurtres_au_paradis.S01E01.mp4
修改后的完整cam_reader.cpp示例
#include <cam_reader/cam_reader.hpp> CamReader::CamReader() : Node("cam_reader") { std::string videopath = "/home/pi/Meurtres_au_paradis.S01E01.mp4"; // 强制使用FFmpeg后端打开视频 if (!this->cap.open(videopath, cv::CAP_FFMPEG)) { RCLCPP_ERROR(this->get_logger(), "Failed to open video file: %s", videopath.c_str()); rclcpp::shutdown(); return; } // 创建定时器,周期50ms get_image_timer_ = this->create_wall_timer( std::chrono::milliseconds(50), std::bind(&CamReader::get_image_callback_, this)); auto default_qos = rclcpp::QoS(rclcpp::SystemDefaultsQoS()); default_qos.reliable(); image_publisher_ = this->create_publisher<sensor_msgs::msg::Image>("/image", default_qos); } void CamReader::get_image_callback_() { cv::Mat frame; if (!this->cap.read(frame)) { RCLCPP_WARN(this->get_logger(), "End of video stream"); get_image_timer_->cancel(); return; } cv_bridge::CvImage img_bridge; sensor_msgs::msg::Image cam_msg; std_msgs::msg::Header header; header.stamp = this->get_clock()->now(); // 添加时间戳 img_bridge = cv_bridge::CvImage(header, sensor_msgs::image_encodings::BGR8, frame); img_bridge.toImageMsg(cam_msg); image_publisher_->publish(cam_msg); } int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared<CamReader>()); rclcpp::shutdown(); return 0; }
内容的提问来源于stack exchange,提问作者petitbouchon
相关产品推荐
相关产品推荐

