37 const std::vector<std::string> channels,
38 const std::vector<std::string> child_frame_ids) {
41 _child_frame_ids = child_frame_ids;
43 _localization_method =
"novatel";
44 auto create_folder = [](
const std::string& folder) {
51 for (std::size_t i = 0; i < _channels.size(); ++i) {
52 const auto& channel = _channels[i];
53 const auto& child_frame_id = _child_frame_ids[i];
54 std::string folder = TransformChannelToFolder(channel);
55 if (IsLidar(channel)) {
56 _cyber_node->CreateReader<
PcMsg>(
58 std::bind(&MsgExporter::PointCloudMessageHandler,
this,
59 std::placeholders::_1, channel, child_frame_id, folder));
60 create_folder(folder);
61 std::cout <<
"Bind lidar channel: " << channel << std::endl;
62 }
else if (IsCamera(channel)) {
63 _cyber_node->CreateReader<
ImgMsg>(
65 std::bind(&MsgExporter::ImageMessageHandler,
this,
66 std::placeholders::_1, channel, child_frame_id, folder));
67 create_folder(folder);
68 std::cout <<
"Bind camera channel: " << channel << std::endl;
70 std::cout <<
"Unknown channel type: " << channel << std::endl;
75void MsgExporter::ImageMessageHandler(
76 const std::shared_ptr<const ImgMsg>& img_msg,
const std::string& channel,
77 const std::string& child_frame_id,
const std::string& folder) {
78 double timestamp = img_msg->measurement_time();
81 const unsigned char* data =
82 reinterpret_cast<const unsigned char*
>(img_msg->data().data());
83 const unsigned char* range_data =
nullptr;
87 std::size_t width = img_msg->width();
88 std::size_t height = img_msg->height();
90 if (QuerySensorToWorldPose(timestamp, child_frame_id, &pose)) {
91 SaveImage(data, range_data, width, height, timestamp, folder);
92 SavePose(pose, timestamp, folder);
94 std::cout <<
"Failed to query camera pose, child_frame_id "
95 << child_frame_id << std::endl;
99void MsgExporter::PointCloudMessageHandler(
100 const std::shared_ptr<const PcMsg>& cloud_msg,
const std::string& channel,
101 const std::string& child_frame_id,
const std::string& folder) {
102 double timestamp = cloud_msg->measurement_time();
105 pcl::PointCloud<PCLPointXYZIT> cloud;
106 if (cloud_msg->point_size() > 0) {
107 cloud.resize(cloud_msg->point_size());
108 for (
int i = 0; i < cloud_msg->point_size(); ++i) {
110 if (std::isnan(pt.
x()) || std::isnan(pt.
y()) || std::isnan(pt.
z())) {
113 if (std::fabs(pt.
x()) > 1e3 || std::fabs(pt.
y()) > 1e3 ||
114 std::fabs(pt.
z()) > 1e3) {
120 cloud[i].intensity =
static_cast<unsigned char>(pt.
intensity());
121 cloud[i].timestamp =
static_cast<double>(pt.
timestamp()) * 1e-9;
124 Eigen::Matrix4d pose;
125 if (QuerySensorToWorldPose(timestamp, child_frame_id, &pose)) {
126 SavePointCloud(cloud, timestamp, folder);
127 SavePose(pose, timestamp, folder);
129 std::cout <<
"Failed to query lidar pose, child_frame_id " << child_frame_id
130 <<
" but still export pointcloud." << std::endl;
131 SavePointCloud(cloud, timestamp, folder);
135bool MsgExporter::SavePointCloud(
136 const pcl::PointCloud<PCLPointXYZIT>& point_cloud,
double timestamp,
137 const std::string& folder) {
138 static char path[500];
139 snprintf(path,
sizeof(path),
"%s/%.6f.pcd", folder.c_str(), timestamp);
140 pcl::PCDWriter writer;
141 writer.writeBinaryCompressed(path, point_cloud);
145bool MsgExporter::SaveImage(
const unsigned char* color_image,
146 const unsigned char* range_image, std::size_t width,
147 std::size_t height,
double timestamp,
148 const std::string& folder) {
149 cv::Mat image(
static_cast<int>(height),
static_cast<int>(width), CV_8UC3,
150 const_cast<unsigned char*
>(color_image));
151 cv::Mat out(
static_cast<int>(height),
static_cast<int>(width), CV_8UC3);
152 for (
size_t y = 0; y <
height; y++) {
153 for (
size_t x = 0; x <
width; x++) {
154 out.at<cv::Vec3b>(y, x)[0] = image.at<cv::Vec3b>(y, x)[2];
155 out.at<cv::Vec3b>(y, x)[2] = image.at<cv::Vec3b>(y, x)[0];
156 out.at<cv::Vec3b>(y, x)[1] = image.at<cv::Vec3b>(y, x)[1];
159 static char path[500];
160 snprintf(path,
sizeof(path),
"%s/%.6f.jpg", folder.c_str(), timestamp);
161 cv::imwrite(path, out);
162 if (range_image !=
nullptr) {
163 snprintf(path,
sizeof(path),
"%s/%.6f.data", folder.c_str(), timestamp);
164 std::ofstream fout(path, std::ofstream::binary);
165 if (fout.is_open()) {
166 fout.write(
reinterpret_cast<const char*
>(range_image), width * height);
178bool MsgExporter::QuerySensorToWorldPose(
double timestamp,
179 const std::string& child_frame_id,
180 Eigen::Matrix4d* pose) {
181 Eigen::Matrix4d sensor2novatel_extrinsics = Eigen::Matrix4d::Identity();
182 Eigen::Matrix4d novatel2world_pose;
184 if (child_frame_id !=
"null" &&
185 !QueryPose(timestamp,
"novatel", child_frame_id,
186 &sensor2novatel_extrinsics)) {
191 if (!QueryPose(timestamp,
"world", _localization_method,
192 &novatel2world_pose)) {
195 *pose = novatel2world_pose * sensor2novatel_extrinsics;
199bool MsgExporter::QueryPose(
double timestamp,
const std::string& frame_id,
200 const std::string& child_frame_id,
201 Eigen::Matrix4d* pose) {
202 cyber::Time query_time(timestamp);
203 std::string err_string;
204 if (!tf2_buffer_->
canTransform(frame_id, child_frame_id, query_time, 0.05f,
206 AERROR <<
"Can not find transform. "
207 <<
" frame_id: " <<
frame_id <<
" child_frame_id: " << child_frame_id
208 <<
" Error info: " << err_string;
215 Eigen::Translation3d translation(
223 *pose = (translation * rotation).matrix();
224 }
catch (tf2::TransformException& ex) {
231bool MsgExporter::SavePose(
const Eigen::Matrix4d& pose,
double timestamp,
232 const std::string& folder) {
233 Eigen::Affine3d affine(pose);
234 Eigen::Quaterniond quat = (Eigen::Quaterniond)affine.linear();
235 static char path[500];
236 snprintf(path,
sizeof(path),
"%s/%.6f.pose", folder.c_str(), timestamp);
237 std::ofstream fout(path);
238 if (fout.is_open()) {
239 fout << 0 <<
" " << std::setprecision(14) <<
timestamp <<
" "
240 << affine.translation().transpose() <<
" " << quat.x() <<
" "
241 << quat.y() <<
" " << quat.z() <<
" " << quat.w() << std::endl;
247bool MsgExporter::IsStereoCamera(
const std::string& channel) {
248 const std::vector<std::string> strs = absl::StrSplit(channel,
'/');
249 return strs.size() > 2 && strs[2] ==
"smartereye";
252bool MsgExporter::IsCamera(
const std::string& channel) {
253 const std::vector<std::string> strs = absl::StrSplit(channel,
'/');
254 return strs.size() > 1 && strs.back() ==
"image";
257bool MsgExporter::IsLidar(
const std::string& channel) {
258 const std::vector<std::string> strs = absl::StrSplit(channel,
'/');
259 return strs.size() > 0 && strs.back() ==
"PointCloud2";
262std::string MsgExporter::TransformChannelToFolder(
const std::string& channel) {
263 const std::vector<std::string> strs = absl::StrSplit(channel,
'/');
264 std::string target =
"";
265 for (
auto& str : strs) {