diff --git a/CMakeLists.txt b/CMakeLists.txt index a04ab1e..2494df5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,8 @@ find_package(geometry_msgs REQUIRED) find_package(nav_msgs REQUIRED) find_package(cv_bridge REQUIRED) find_package(image_transport REQUIRED) - +find_package(pcl_conversions REQUIRED) +find_package(PCL REQUIRED) find_package(Boost REQUIRED COMPONENTS system filesystem thread date_time program_options) @@ -41,12 +42,36 @@ ament_target_dependencies(extract_realsense_data_from_bag geometry_msgs nav_msgs cv_bridge - image_transport) -target_link_libraries(extract_realsense_data_from_bag ${OpenCV_LIBS} ${Boost_LIBRARIES}) + image_transport + pcl_conversions) +target_link_libraries(extract_realsense_data_from_bag + ${OpenCV_LIBS} + ${Boost_LIBRARIES} + ${PCL_LIBRARIES}) + +add_executable(extract_feynman_data_from_bag + src/extract_feynman_data_from_bag.cc) +ament_target_dependencies(extract_feynman_data_from_bag + rclcpp + rcpputils + rosbag2_cpp + std_msgs + sensor_msgs + geometry_msgs + nav_msgs + cv_bridge + image_transport + pcl_conversions) +target_link_libraries(extract_feynman_data_from_bag + ${OpenCV_LIBS} + ${Boost_LIBRARIES} + ${PCL_LIBRARIES}) # Install nodes install( - TARGETS extract_realsense_data_from_bag + TARGETS + extract_realsense_data_from_bag + extract_feynman_data_from_bag DESTINATION lib/${PROJECT_NAME}) ament_package() diff --git a/package.xml b/package.xml index 21d208c..7598c48 100644 --- a/package.xml +++ b/package.xml @@ -20,6 +20,7 @@ libopencv-dev cv_bridge image_transport + pcl_conversions ament_lint_auto ament_lint_common diff --git a/src/extract_feynman_data_from_bag.cc b/src/extract_feynman_data_from_bag.cc new file mode 100644 index 0000000..62bfeac --- /dev/null +++ b/src/extract_feynman_data_from_bag.cc @@ -0,0 +1,331 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) { + std::string in_bag, out_folder; + + //========= Handling Program options ========= + boost::program_options::options_description desc("Allowed options"); + desc.add_options() + ("help", "produce help message") + ("inbag,i", + boost::program_options::value( + &in_bag)->default_value(""), + "Input bag file") + ("outfolder,o", + boost::program_options::value( + &out_folder)->default_value(""), + "Output folder"); + + boost::program_options::positional_options_description pdesc; + pdesc.add("inbag", 1); + pdesc.add("outfolder", 2); + + boost::program_options::variables_map vm; + boost::program_options::store( + boost::program_options::command_line_parser( + argc, argv).options(desc).positional(pdesc).run(), + vm); + boost::program_options::notify(vm); + + if (vm.count("help") || in_bag.empty()) { + std::cout << desc << std::endl; + return 1; + } + + // Startup this node + rclcpp::init(argc, argv); + RCLCPP_INFO(rclcpp::get_logger(__func__), "Starting up"); + + if (out_folder.empty()) + out_folder = boost::filesystem::path(in_bag).parent_path().string(); + if (out_folder.back() != '/') + out_folder += "/"; + if (!boost::filesystem::exists(out_folder)) + boost::filesystem::create_directories(out_folder); + + if (!boost::filesystem::exists(out_folder + "color1")) + boost::filesystem::create_directories(out_folder + "color1"); + if (!boost::filesystem::exists(out_folder + "color2")) + boost::filesystem::create_directories(out_folder + "color2"); + if (!boost::filesystem::exists(out_folder + "infra1")) + boost::filesystem::create_directories(out_folder + "infra1"); + if (!boost::filesystem::exists(out_folder + "infra2")) + boost::filesystem::create_directories(out_folder + "infra2"); + if (!boost::filesystem::exists(out_folder + "depth")) + boost::filesystem::create_directories(out_folder + "depth"); + if (!boost::filesystem::exists(out_folder + "aligned_depth_to_color")) + boost::filesystem::create_directories(out_folder + "aligned_depth_to_color"); + if (!boost::filesystem::exists(out_folder + "pointcloud")) + boost::filesystem::create_directories(out_folder + "pointcloud"); + + std::string imu_topic = "/feynman_camera/default/imu0_single"; + std::string color1_topic = "/feynman_camera/default/rgb/image_rect_color"; + std::string color2_topic = "/feynman_camera/default/rgb2/image_rect_color"; + std::string infra1_topic = "/feynman_camera/default/leftir/image_rect"; + std::string infra2_topic = "/feynman_camera/default/rightir/image_rect"; + std::string depth_topic = "/feynman_camera/default/depth/image_raw"; + std::string aligned_d2c_topic = + "/feynman_camera/default/depthalignrgb/image_raw"; + std::string pointcloud_topic = "/feynman_camera/default/depth/dotcloud"; + + // Load rosbag here, and find messages we can play + rosbag2_cpp::Reader bag_reader; + bag_reader.open(in_bag); + + RCLCPP_INFO_STREAM( + rclcpp::get_logger(__func__), + "Extracting D400 RGB, Infra, Depth, and IMU data from " + + in_bag + " to " + out_folder); + + FILE* color1_file = fopen((out_folder + "color1.txt").c_str(), "wb"); + FILE* color2_file = fopen((out_folder + "color2.txt").c_str(), "wb"); + FILE* infra1_file = fopen((out_folder + "infra1.txt").c_str(), "wb"); + FILE* infra2_file = fopen((out_folder + "infra2.txt").c_str(), "wb"); + FILE* depth_file = fopen((out_folder + "depth.txt").c_str(), "wb"); + FILE* aligned_depth_to_color_file = fopen( + (out_folder + "aligned_depth_to_color.txt").c_str(), "wb"); + FILE* pointcloud_file = fopen((out_folder + "pointcloud.txt").c_str(), "wb"); + FILE* imu_file = fopen((out_folder + "imu.txt").c_str(), "wb"); + fprintf(imu_file, "#Time Gx Gy Gz Ax Ay Ax \n"); + + // Step through the rosbag and send to algo methods + while (bag_reader.has_next()) { + rosbag2_storage::SerializedBagMessageSharedPtr bag_message = + bag_reader.read_next(); + rclcpp::SerializedMessage serialized_msg(*bag_message->serialized_data); + + // Handle color1 message + if (bag_message->topic_name == color1_topic) { + sensor_msgs::msg::Image::SharedPtr image_msg = + std::make_shared(); + + rclcpp::Serialization serialization; + serialization.deserialize_message(&serialized_msg, image_msg.get()); + cv_bridge::CvImageConstPtr image_ptr; + try { + image_ptr = cv_bridge::toCvShare(image_msg, + sensor_msgs::image_encodings::BGR8); + } catch (cv_bridge::Exception &e) { + RCLCPP_ERROR(rclcpp::get_logger(__func__), + "cv_bridge exception: %s", e.what()); + } + + cv::Mat image = image_ptr->image; + if (image.empty()) continue; + char tmp[30] = {0}; + sprintf(tmp, "%.6f", rclcpp::Time(image_ptr->header.stamp).seconds()); + std::string time(tmp); + fprintf(color1_file, "%s %s\n", + time.c_str(), ("color1/" + time + ".png").c_str()); + fflush(color1_file); + cv::imwrite(out_folder + "color1/" + time + ".png", image); + } + + // Handle color1 message + if (bag_message->topic_name == color2_topic) { + sensor_msgs::msg::Image::SharedPtr image_msg = + std::make_shared(); + + rclcpp::Serialization serialization; + serialization.deserialize_message(&serialized_msg, image_msg.get()); + cv_bridge::CvImageConstPtr image_ptr; + try { + image_ptr = cv_bridge::toCvShare(image_msg, + sensor_msgs::image_encodings::BGR8); + } catch (cv_bridge::Exception &e) { + RCLCPP_ERROR(rclcpp::get_logger(__func__), + "cv_bridge exception: %s", e.what()); + } + + cv::Mat image = image_ptr->image; + if (image.empty()) continue; + char tmp[30] = {0}; + sprintf(tmp, "%.6f", rclcpp::Time(image_ptr->header.stamp).seconds()); + std::string time(tmp); + fprintf(color2_file, "%s %s\n", + time.c_str(), ("color2/" + time + ".png").c_str()); + fflush(color2_file); + cv::imwrite(out_folder + "color2/" + time + ".png", image); + } + + // Handle infra1 message + if (bag_message->topic_name == infra1_topic) { + sensor_msgs::msg::Image::SharedPtr image_msg = + std::make_shared(); + + rclcpp::Serialization serialization; + serialization.deserialize_message(&serialized_msg, image_msg.get()); + cv_bridge::CvImageConstPtr image_ptr; + try { + image_ptr = cv_bridge::toCvShare(image_msg, + sensor_msgs::image_encodings::MONO8); + } catch (cv_bridge::Exception &e) { + RCLCPP_ERROR(rclcpp::get_logger(__func__), + "cv_bridge exception: %s", e.what()); + } + + cv::Mat image = image_ptr->image; + if (image.empty()) continue; + char tmp[30] = {0}; + sprintf(tmp, "%.6f", rclcpp::Time(image_ptr->header.stamp).seconds()); + std::string time(tmp); + fprintf(infra1_file, "%s %s\n", + time.c_str(), ("infra1/" + time + ".png").c_str()); + fflush(infra1_file); + cv::imwrite(out_folder + "infra1/" + time + ".png", image); + } + + // Handle infra2 message + if (bag_message->topic_name == infra2_topic) { + sensor_msgs::msg::Image::SharedPtr image_msg = + std::make_shared(); + + rclcpp::Serialization serialization; + serialization.deserialize_message(&serialized_msg, image_msg.get()); + cv_bridge::CvImageConstPtr image_ptr; + try { + image_ptr = cv_bridge::toCvShare(image_msg, + sensor_msgs::image_encodings::MONO8); + } catch (cv_bridge::Exception &e) { + RCLCPP_ERROR(rclcpp::get_logger(__func__), + "cv_bridge exception: %s", e.what()); + } + + cv::Mat image = image_ptr->image; + if (image.empty()) continue; + char tmp[30] = {0}; + sprintf(tmp, "%.6f", rclcpp::Time(image_ptr->header.stamp).seconds()); + std::string time(tmp); + fprintf(infra2_file, "%s %s\n", + time.c_str(), ("infra2/" + time + ".png").c_str()); + fflush(infra2_file); + cv::imwrite(out_folder + "infra2/" + time + ".png", image); + } + + // Handle depth message + if (bag_message->topic_name == depth_topic) { + sensor_msgs::msg::Image::SharedPtr image_msg = + std::make_shared(); + + rclcpp::Serialization serialization; + serialization.deserialize_message(&serialized_msg, image_msg.get()); + cv_bridge::CvImageConstPtr image_ptr; + try { + image_ptr = cv_bridge::toCvShare(image_msg, + sensor_msgs::image_encodings::TYPE_16UC1); + } catch (cv_bridge::Exception &e) { + RCLCPP_ERROR(rclcpp::get_logger(__func__), + "cv_bridge exception: %s", e.what()); + } + + cv::Mat image = image_ptr->image; + if (image.empty()) continue; + char tmp[30] = {0}; + sprintf(tmp, "%.6f", rclcpp::Time(image_ptr->header.stamp).seconds()); + std::string time(tmp); + fprintf(depth_file, "%s %s\n", + time.c_str(), ("depth/" + time + ".png").c_str()); + fflush(depth_file); + cv::imwrite(out_folder + "depth/" + time + ".png", image); + } + + // Handle aligned_d2c_topic message + if (bag_message->topic_name == aligned_d2c_topic) { + sensor_msgs::msg::Image::SharedPtr image_msg = + std::make_shared(); + + rclcpp::Serialization serialization; + serialization.deserialize_message(&serialized_msg, image_msg.get()); + cv_bridge::CvImageConstPtr image_ptr; + try { + image_ptr = cv_bridge::toCvShare(image_msg, + sensor_msgs::image_encodings::TYPE_16UC1); + } catch (cv_bridge::Exception &e) { + RCLCPP_ERROR(rclcpp::get_logger(__func__), + "cv_bridge exception: %s", e.what()); + } + + cv::Mat image = image_ptr->image; + if (image.empty()) continue; + char tmp[30] = {0}; + sprintf(tmp, "%.6f", rclcpp::Time(image_ptr->header.stamp).seconds()); + std::string time(tmp); + fprintf(aligned_depth_to_color_file, "%s %s\n", + time.c_str(), ("aligned_depth_to_color/" + time + ".png").c_str()); + fflush(aligned_depth_to_color_file); + cv::imwrite(out_folder + "aligned_depth_to_color/" + time + ".png", image); + } + + // Handle pointcloud message + if (bag_message->topic_name == pointcloud_topic) { + sensor_msgs::msg::PointCloud2::SharedPtr point_cloud_2_msg = + std::make_shared(); + + rclcpp::Serialization serialization; + serialization.deserialize_message(&serialized_msg, point_cloud_2_msg.get()); + + if (point_cloud_2_msg->data.empty()) continue; + char tmp[30] = {0}; + sprintf(tmp, "%.6f", rclcpp::Time(point_cloud_2_msg->header.stamp).seconds()); + std::string time(tmp); + fprintf(pointcloud_file, "%s %s\n", + time.c_str(), ("pointcloud/" + time + ".png").c_str()); + fflush(pointcloud_file); + pcl::PointCloud cloud; + pcl::fromROSMsg(*point_cloud_2_msg, cloud); + pcl::io::savePCDFileBinary(out_folder + "pointcloud/" + time + ".pcd", cloud); + } + + // Handle IMU message + if (bag_message->topic_name == imu_topic) { + sensor_msgs::msg::Imu::SharedPtr imu_msg = + std::make_shared(); + + rclcpp::Serialization serialization; + serialization.deserialize_message(&serialized_msg, imu_msg.get()); + + fprintf(imu_file, "%.8f %.8f %.8f %.8f %.8f %.8f %.8f\n", + rclcpp::Time(imu_msg->header.stamp).seconds(), + imu_msg->angular_velocity.x, + imu_msg->angular_velocity.y, + imu_msg->angular_velocity.z, + imu_msg->linear_acceleration.x, + imu_msg->linear_acceleration.y, + imu_msg->linear_acceleration.z); + fflush(imu_file); + } + } + + bag_reader.close(); + + fclose(color1_file); + fclose(color2_file); + fclose(infra1_file); + fclose(infra2_file); + fclose(depth_file); + fclose(aligned_depth_to_color_file); + fclose(imu_file); + + rclcpp::shutdown(); + + return 0; +} + +