23 #include "cairo/cairo.h" 42 using PixelDataMatrix =
43 Eigen::Matrix<PixelData, Eigen::Dynamic, Eigen::Dynamic>;
45 double Mix(
const double a,
const double b,
const double t) {
46 return a * (1. - t) + t * b;
49 cairo_status_t CairoWriteCallback(
void*
const closure,
50 const unsigned char* data,
51 const unsigned int length) {
52 if (static_cast<FileWriter*>(closure)->Write(
53 reinterpret_cast<const char*>(data), length)) {
54 return CAIRO_STATUS_SUCCESS;
56 return CAIRO_STATUS_WRITE_ERROR;
60 void WritePng(
const PixelDataMatrix& mat, FileWriter*
const file_writer) {
62 cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, mat.cols());
63 CHECK_EQ(stride % 4, 0);
64 std::vector<uint32_t> pixels(stride / 4 * mat.rows(), 0.);
66 float max = std::numeric_limits<float>::min();
67 for (
int y = 0; y < mat.rows(); ++y) {
68 for (
int x = 0; x < mat.cols(); ++x) {
69 const PixelData& cell = mat(y, x);
70 if (cell.num_occupied_cells_in_column == 0.) {
73 max = std::max<float>(max, std::log(cell.num_occupied_cells_in_column));
77 for (
int y = 0; y < mat.rows(); ++y) {
78 for (
int x = 0; x < mat.cols(); ++x) {
79 const PixelData& cell = mat(y, x);
80 if (cell.num_occupied_cells_in_column == 0.) {
81 pixels[y * stride / 4 + x] =
82 (255 << 24) | (255 << 16) | (255 << 8) | 255;
89 const float saturation =
90 std::log(cell.num_occupied_cells_in_column) / max;
91 double mean_r_in_column = (cell.mean_r / 255.);
92 double mean_g_in_column = (cell.mean_g / 255.);
93 double mean_b_in_column = (cell.mean_b / 255.);
95 double mix_r = Mix(1., mean_r_in_column, saturation);
96 double mix_g = Mix(1., mean_g_in_column, saturation);
97 double mix_b = Mix(1., mean_b_in_column, saturation);
102 pixels[y * stride / 4 + x] = (255 << 24) | (r << 16) | (g << 8) | b;
112 cairo_image_surface_create_for_data(
113 reinterpret_cast<unsigned char*>(pixels.data()), CAIRO_FORMAT_ARGB32,
114 mat.cols(), mat.rows(), stride),
115 cairo_surface_destroy);
116 CHECK_EQ(cairo_surface_status(surface.get()), CAIRO_STATUS_SUCCESS);
117 CHECK_EQ(cairo_surface_write_to_png_stream(surface.get(), &CairoWriteCallback,
119 CAIRO_STATUS_SUCCESS);
120 CHECK(file_writer->Close());
124 const std::vector<mapping::Timespan>& timespans) {
125 for (
const mapping::Timespan& timespan : timespans) {
126 if (timespan.start <= time && time <= timespan.end) {
137 const std::vector<mapping::Floor>& floors,
const string& output_filename,
140 file_writer_factory_(file_writer_factory),
142 output_filename_(output_filename),
143 transform_(transform) {
144 for (
size_t i = 0; i < (
floors_.empty() ? 1 : floors.size()); ++i) {
151 const mapping::proto::Trajectory& trajectory,
155 std::vector<mapping::Floor> floors;
156 if (dictionary->
HasKey(
"separate_floors") &&
157 dictionary->
GetBool(
"separate_floors")) {
161 return common::make_unique<XRayPointsProcessor>(
165 floors, dictionary->
GetString(
"filename"), file_writer_factory, next);
171 LOG(WARNING) <<
"Not writing output: bounding box is empty.";
176 const auto voxel_index_to_pixel = [
this](
const Eigen::Array3i& index) {
186 PixelDataMatrix image = PixelDataMatrix(ysize, xsize);
188 !it.Done(); it.Next()) {
189 const Eigen::Array3i cell_index = it.GetCellIndex();
190 const Eigen::Array2i pixel = voxel_index_to_pixel(cell_index);
191 PixelData& pixel_data = image(pixel.y(), pixel.x());
192 const auto& column_data = aggregation.
column_data.at(
193 std::make_pair(cell_index[1], cell_index[2]));
194 pixel_data.mean_r = column_data.sum_r / column_data.count;
195 pixel_data.mean_g = column_data.sum_g / column_data.count;
196 pixel_data.mean_b = column_data.sum_b / column_data.count;
197 ++pixel_data.num_occupied_cells_in_column;
199 WritePng(image, file_writer);
205 constexpr
Color kDefaultColor = {{0, 0, 0}};
206 for (
size_t i = 0; i < batch.
points.size(); ++i) {
207 const Eigen::Vector3f camera_point = transform * batch.
points[i];
208 const Eigen::Array3i cell_index =
213 aggregation->
column_data[std::make_pair(cell_index[1], cell_index[2])];
215 batch.
colors.empty() ? kDefaultColor : batch.
colors.at(i);
216 column_data.
sum_r += color[0];
217 column_data.
sum_g += color[1];
218 column_data.
sum_b += color[2];
228 for (
size_t i = 0; i <
floors_.size(); ++i) {
229 if (!ContainedIn(batch->time,
floors_[i].timespans)) {
244 for (
size_t i = 0; i <
floors_.size(); ++i) {
254 LOG(FATAL) <<
"X-Ray generation must be configured to occur after any " 255 "stages that require multiple passes.";
FlushResult Flush() override
virtual void Process(std::unique_ptr< PointsBatch > points_batch)=0
const transform::Rigid3f transform_
bool GetBool(const string &key)
mapping_3d::HybridGridBase< bool > voxels
Eigen::Array3i GetCellIndex(const Eigen::Vector3f &point) const
Eigen::AlignedBox3i bounding_box_
std::function< std::unique_ptr< FileWriter >(const string &filename)> FileWriterFactory
int RoundToInt(const float x)
const string output_filename_
std::map< std::pair< int, int >, ColumnData > column_data
double GetDouble(const string &key)
UniversalTimeScaleClock::time_point Time
ValueType * mutable_value(const Eigen::Array3i &index)
std::unique_ptr< cairo_surface_t, void(*)(cairo_surface_t *)> UniqueSurfacePtr
void Insert(const PointsBatch &batch, const transform::Rigid3f &transform, Aggregation *aggregation)
static std::unique_ptr< XRayPointsProcessor > FromDictionary(const mapping::proto::Trajectory &trajectory, FileWriterFactory file_writer_factory, common::LuaParameterDictionary *dictionary, PointsProcessor *next)
std::array< uint8_t, 3 > Color
std::vector< mapping::Floor > floors_
XRayPointsProcessor(double voxel_size, const transform::Rigid3f &transform, const std::vector< mapping::Floor > &floors, const string &output_filename, FileWriterFactory file_writer_factory, PointsProcessor *next)
std::vector< Color > colors
std::vector< Aggregation > aggregations_
typename Grid< ValueType >::Iterator Iterator
virtual FlushResult Flush()=0
bool HasKey(const string &key) const
size_t num_occupied_cells_in_column
string GetString(const string &key)
void WriteVoxels(const Aggregation &aggregation, FileWriter *const file_writer)
std::vector< Floor > DetectFloors(const proto::Trajectory &trajectory)
PointsProcessor *const next_
FileWriterFactory file_writer_factory_
std::unique_ptr< LuaParameterDictionary > GetDictionary(const string &key)
std::vector< Eigen::Vector3f > points
void Process(std::unique_ptr< PointsBatch > batch) override