1. 问题描述:
在ROS系统中,可以使用bag文件来保存和恢复系统的运行状态,比如录制雷达和相机话题的bag包,然后通过回放用来SLAM定位或者进行联合外参标定。但是在一些情况下bag包并不是那么方便,例如:
- 所运行的程序不能时时运行,需要每张图片按照顺序进行处理。此时虽然rosbag可以设置回放速度以及队列缓存,但是有时仍然不能很好的满足顺序处理的要求。
- 有些平台没有安装ROS,不能接收图像的topic数据,即不能很好地将程序进行移植。
此时就需要我们将bag数据转存为TUM通用数据集的格式,然后通过读取TUM数据的方式读取我们的数据。具体的,本博文实现的功能为:提取并保存bag包中的RGB图像和depth图像,使其可以以单目或者RGB-D的数据集的形式供使用。
2. TUM数据集格式介绍:
TUM是一个RGB-D的数据集,主页为https://vision.in.tum.de/data/datasets/rgbd-dataset/download 。每一个数据集包含以下文件:depth ,rgb ,depth.txt ,rgb.txt ,accelerometer.txt ,groundtruth.txt。要运行此数据集,只需要恢复前四个文件即可。
rgb和depth文件中保存了rgb和深度图片,如:

其中rgb.txt和depth.txt记录的每张图片的采集时间和图片名称(图片名称也是以时间来命名的)。

所以我们只需要编写一个ROS节点,获取并保存相应的相应的图片及时间戳就行了。
3. 程序编写:
首先在catkin_ws工作空间下创建功能包(ROS的基本知识在这里不再赘述)
cd ~/catkin_ws/src
catkin_create_pkg rosbag2tum roscpp std_msgs sensor_msgs cv_bridge
在rosbag2tum/src文件夹在创建新的cpp文件bagToTUM.cpp,编写节点程序。
- 首先,在程序开始前定义了一些全局变量,用来存储数据保存路径以及图像topic名称,这些也是需要根据自己实际情况进行修改的。
- 然后,定义保存路径的函数savePath(),输入参数为包存路径以及double格式的图像时间戳,用时间戳命名图像。
- 然后,定义了回调函数GrabRGB()和GrabDepth(),在接受到相关话题数据时运行,用来保存、显示图像。
- 最后,主函数main()中订阅了相关话题。
#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>
#include <ros/ros.h>
#include <iostream>
#include <chrono>
#include <string>
using namespace std;
//设置一些保存路径
string Path = "/home/qin/桌面/fr1/";
string pathRGB = Path + "rgb.txt";
string pathDepth = Path + "depth.txt";
string dataRGB = Path + "rgb/";
string dataDepth = Path + "depth/";
//设置图像topic名称
string depth_topic = "/camera/depth/image";
string rgb_topic = "/camera/rgb/image_color";
//保存图像路径至txt文件下
void savePath(string &path, string &type,double time){
ofstream of;
of.open(path, std::ios_base::app);
if(of.fail()){
ROS_INFO("Fail to opencv file!!");
}else{
of<<endl;
of<<std::fixed<< time <<" "<< type <<time<<".png";
of.close();
}
}
//RGB图像回调函数
void GrabRGB(const sensor_msgs::ImageConstPtr& msg)
{
cv_bridge::CvImageConstPtr cv_ptr;
try
{
//保存图像及路径
cv_ptr = cv_bridge::toCvShare(msg);
//颜色通道转换
cv::Mat bgrImage;
cvtColor(cv_ptr->image, bgrImage, CV_BGR2RGB);
//cv::imshow("RGB", bgrImage);
double time = cv_ptr->header.stamp.toSec();
string type = "rgb/";
savePath(pathRGB, type, time);
std::ostringstream osf;
osf<< dataRGB <<std::fixed <<time << ".png";//图像以时间戳命名
cv::imwrite(osf.str(), bgrImage);
}
catch (cv_bridge::Exception& e)
{
ROS_ERROR("cv_bridge exception: %s", e.what());
return;
}
//cv::waitKey(2);
}
//深度图像回调函数
void GrabDepth(const sensor_msgs::ImageConstPtr& msg)
{
cv_bridge::CvImageConstPtr cv_ptr;
try
{
cv_ptr = cv_bridge::toCvShare(msg);
//imshow("Depth", cv_ptr->image);
double time = cv_ptr->header.stamp.toSec();
string type = "depth/";
savePath(pathDepth, type, time);
std::ostringstream osf;
osf<< dataDepth <<std::fixed <<time << ".png";
cv::imwrite(osf.str(), cv_ptr->image);
}
catch (cv_bridge::Exception& e)
{
ROS_ERROR("cv_bridge exception: %s", e.what());
return;
}
//cv::waitKey(2);
}
//节点主函数
int main(int argc,char **argv)
{
ros::init(argc,argv,"bagToTUM");
ros::start();
ofstream of;
of.open(pathRGB, std::ios_base::app);
if(of.fail()){
ROS_INFO("Fail to opencv file!!");
}else{
of<<"#------------start a new dataset-----------------";
of.close();
}
of.open(pathDepth, std::ios_base::app);
if(of.fail()){
ROS_INFO("Fail to opencv file!!");
}else{
of<<"#------------start a new dataset-----------------";
of.close();
}
//订阅图像话题
ROS_INFO("bagToTUM is ready.");
ros::NodeHandle nodeHandler;
ros::Subscriber subRGB = nodeHandler.subscribe(rgb_topic, 5, &GrabRGB);
ros::Subscriber subDepth = nodeHandler.subscribe(depth_topic, 5, &GrabDepth);
ros::spin();
return 0;
}
接下来配置CMakeLists.txt。所依赖的第三方只有opencv库(这里为opencv 3.x),opencv库容易和ros的cv_bridge版本冲突,解决方案见我的csdn博文https://blog.csdn.net/qinqinxiansheng/article/details/120219388 。
cmake_minimum_required(VERSION 3.0.2)
project(rosbag2tum)
find_package(catkin REQUIRED COMPONENTS
cv_bridge
roscpp
sensor_msgs
std_msgs
)
find_package(OpenCV 3.0 QUIET)
if(NOT OpenCV_FOUND)
find_package(OpenCV 2.4.3 QUIET)
if(NOT OpenCV_FOUND)
message(FATAL_ERROR "OpenCV > 2.4.3 not found.")
endif()
endif()
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES rosbag2tum
CATKIN_DEPENDS cv_bridge roscpp sensor_msgs std_msgs
# DEPENDS system_lib
)
include_directories(
# include
${OpenCV_INCLUDE_DIRS}
${catkin_INCLUDE_DIRS}
)
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_executable(bag2tum src/bagToTUM.cpp)
target_link_libraries(bag2tum ${catkin_LIBRARIES} ${OpenCV_LIBS})
最后用catkin_make命令编译工作空间即可。
4. 使用说明及结果展示:
4.1 注意事项
运行时需要知道的是:
- 此程序可以用于单目。
- 运行前相关文件夹要有效,程序不会自己新建文件夹。
- 图像的路径保存在rgb.txt和depth.txt中,若文件路径下不含该txt文件会重新生成一个。
- rosbag数据不会完全还原原始数据,且用topic收发数据时可能会丢失前几帧图像。
- 不要重复执行程序,图像的路径保存在rgb.txt时是直接在文件尾部添加的,多次时不会覆盖之前的结果,导致越来越长。
- 如果检测不出来节点,直接运行运行catkin_ws/devel/lib/rosrgb2tum/bag2tum也可以。
4.2 使用方法
这里我们用TUM的fr1_xyz.bag制作图像格式的数据集,并用ORB-SLAM2验证我们制作的数据集可以正常使用。
a. 在src/bagToTUM.cpp中修改相关路径以及topic 名称,在要保存的路径下创建rgb,depth文件夹

b. 重新编译工作空间,运行roscore命令以及rgb2tum节点,此时回自动创建rgb.txt和depth.txt文件

c. rosbag play命令运行bag数据集,比如我的
rosbag play ./rgbd_dataset_freiburg1_xyz.bag -r 0.5
其中-r 0.5表示以0.5倍速播放,这是因为如果数据量过大,程序可能处理不过来,导致丢失数据。
d. 程序运行效果过程如图所示(程序中已经把显示imshow注释掉了,否则会很卡)

e. 呐,798张图片一张不少

f. 然后下载associate.py(https://download.csdn.net/download/qinqinxiansheng/38156469 )文件,对图像的时间戳进行对准
python associate.py rgb.txt depth.txt > associate.txt
生成的associate.txt文件如图所示:

g. 最后用ORB-SLAM2测试一下我们的数据集,完美。以我的为例,测试我们刚才新制作的数据集fr1_xyz
./Examples/RGB-D/rgbd_tum Vocabulary/ORBvoc.txt Examples/RGB-D/TUM1.yaml /home/robot/MrQin/MyTUM/fr1_xyz /home/robot/MrQin/MyTUM/fr1_xyz/associate.txt

恩,可以跑。如果你还不知道我为什么用TUM的rosbag的数据格式格式转成普通的图片格式,这样做的意义在哪里,那么再见。
评论(1)
您还未登录,请登录后发表或查看评论