/* * Copyright 2017 The Cartographer Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "cartographer/io/probability_grid_points_processor.h" #include "Eigen/Core" #include "absl/memory/memory.h" #include "cartographer/common/lua_parameter_dictionary.h" #include "cartographer/common/math.h" #include "cartographer/io/draw_trajectories.h" #include "cartographer/io/image.h" #include "cartographer/io/points_batch.h" #include "cartographer/mapping/probability_values.h" #include "glog/logging.h" namespace cartographer { namespace io { namespace { void DrawTrajectoriesIntoImage( const mapping::ProbabilityGrid& probability_grid, const Eigen::Array2i& offset, const std::vector& trajectories, cairo_surface_t* cairo_surface) { for (size_t i = 0; i < trajectories.size(); ++i) { DrawTrajectory( trajectories[i], GetColor(i), [&probability_grid, &offset](const transform::Rigid3d& pose) -> Eigen::Array2i { return probability_grid.limits().GetCellIndex( pose.cast().translation().head<2>()) - offset; }, cairo_surface); } } uint8 ProbabilityToColor(float probability_from_grid) { const float probability = 1.f - probability_from_grid; return ::cartographer::common::RoundToInt( 255 * ((probability - mapping::kMinProbability) / (mapping::kMaxProbability - mapping::kMinProbability))); } std::string FileExtensionFromOutputType( const ProbabilityGridPointsProcessor::OutputType& output_type) { if (output_type == ProbabilityGridPointsProcessor::OutputType::kPng) { return ".png"; } else if (output_type == ProbabilityGridPointsProcessor::OutputType::kPb) { return ".pb"; } LOG(FATAL) << "OutputType does not exist!"; } ProbabilityGridPointsProcessor::OutputType OutputTypeFromString( const std::string& output_type) { if (output_type == "png") { return ProbabilityGridPointsProcessor::OutputType::kPng; } else if (output_type == "pb") { return ProbabilityGridPointsProcessor::OutputType::kPb; } else { LOG(FATAL) << "OutputType " << output_type << " does not exist!"; } } } // namespace ProbabilityGridPointsProcessor::ProbabilityGridPointsProcessor( const double resolution, const mapping::proto::ProbabilityGridRangeDataInserterOptions2D& probability_grid_range_data_inserter_options, const DrawTrajectories& draw_trajectories, const OutputType& output_type, std::unique_ptr file_writer, const std::vector& trajectories, PointsProcessor* const next) : draw_trajectories_(draw_trajectories), output_type_(output_type), trajectories_(trajectories), file_writer_(std::move(file_writer)), next_(next), range_data_inserter_(probability_grid_range_data_inserter_options), probability_grid_( CreateProbabilityGrid(resolution, &conversion_tables_)) { LOG_IF(WARNING, output_type == OutputType::kPb && draw_trajectories_ == DrawTrajectories::kYes) << "Drawing the trajectories is not supported when writing the " "probability grid as protobuf."; } std::unique_ptr ProbabilityGridPointsProcessor::FromDictionary( const std::vector& trajectories, const FileWriterFactory& file_writer_factory, common::LuaParameterDictionary* const dictionary, PointsProcessor* const next) { const auto draw_trajectories = (!dictionary->HasKey("draw_trajectories") || dictionary->GetBool("draw_trajectories")) ? DrawTrajectories::kYes : DrawTrajectories::kNo; const auto output_type = dictionary->HasKey("output_type") ? OutputTypeFromString(dictionary->GetString("output_type")) : OutputType::kPng; return absl::make_unique( dictionary->GetDouble("resolution"), mapping::CreateProbabilityGridRangeDataInserterOptions2D( dictionary->GetDictionary("range_data_inserter").get()), draw_trajectories, output_type, file_writer_factory(dictionary->GetString("filename") + FileExtensionFromOutputType(output_type)), trajectories, next); } void ProbabilityGridPointsProcessor::Process( std::unique_ptr batch) { range_data_inserter_.Insert( {batch->origin, sensor::PointCloud(batch->points), {}}, &probability_grid_); next_->Process(std::move(batch)); } PointsProcessor::FlushResult ProbabilityGridPointsProcessor::Flush() { if (output_type_ == OutputType::kPng) { Eigen::Array2i offset; std::unique_ptr image = DrawProbabilityGrid(probability_grid_, &offset); if (image != nullptr) { if (draw_trajectories_ == ProbabilityGridPointsProcessor::DrawTrajectories::kYes) { DrawTrajectoriesIntoImage(probability_grid_, offset, trajectories_, image->GetCairoSurface().get()); } image->WritePng(file_writer_.get()); CHECK(file_writer_->Close()); } } else if (output_type_ == OutputType::kPb) { const auto probability_grid_proto = probability_grid_.ToProto(); std::string probability_grid_serialized; probability_grid_proto.SerializeToString(&probability_grid_serialized); file_writer_->Write(probability_grid_serialized.data(), probability_grid_serialized.size()); CHECK(file_writer_->Close()); } else { LOG(FATAL) << "Output Type " << FileExtensionFromOutputType(output_type_) << " is not supported."; } switch (next_->Flush()) { case FlushResult::kRestartStream: LOG(FATAL) << "ProbabilityGrid generation must be configured to occur " "after any stages that require multiple passes."; case FlushResult::kFinished: return FlushResult::kFinished; } LOG(FATAL); // The following unreachable return statement is needed to avoid a GCC bug // described at https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81508 return FlushResult::kFinished; } std::unique_ptr DrawProbabilityGrid( const mapping::ProbabilityGrid& probability_grid, Eigen::Array2i* offset) { mapping::CellLimits cell_limits; probability_grid.ComputeCroppedLimits(offset, &cell_limits); if (cell_limits.num_x_cells == 0 || cell_limits.num_y_cells == 0) { LOG(WARNING) << "Not writing output: empty probability grid"; return nullptr; } auto image = absl::make_unique(cell_limits.num_x_cells, cell_limits.num_y_cells); for (const Eigen::Array2i& xy_index : mapping::XYIndexRangeIterator(cell_limits)) { const Eigen::Array2i index = xy_index + *offset; constexpr uint8 kUnknownValue = 128; const uint8 value = probability_grid.IsKnown(index) ? ProbabilityToColor(probability_grid.GetProbability(index)) : kUnknownValue; image->SetPixel(xy_index.x(), xy_index.y(), {{value, value, value}}); } return image; } mapping::ProbabilityGrid CreateProbabilityGrid( const double resolution, mapping::ValueConversionTables* conversion_tables) { constexpr int kInitialProbabilityGridSize = 100; Eigen::Vector2d max = 0.5 * kInitialProbabilityGridSize * resolution * Eigen::Vector2d::Ones(); return mapping::ProbabilityGrid( mapping::MapLimits(resolution, max, mapping::CellLimits(kInitialProbabilityGridSize, kInitialProbabilityGridSize)), conversion_tables); } } // namespace io } // namespace cartographer