Repository navigation
Conversation
With downsample_all_data_ (the default), applyFilter averaged every field by copying field_sizes_[d] raw bytes into one float of an Eigen::VectorXf and copying them back out. Since PointCloudLibrary#4133 field_sizes_ allows 8 bytes for FLOAT64, INT64 and UINT64 fields, so such a field was written into its float and the next one, and past the end of the vector when it was the last field (e.g. a double timestamp after x/y/z). The copy back read past the end in the same way, and the average of the raw bytes was meaningless for these fields as well as for signed and 32-bit integers. A field named "rgb" or "rgba" was also taken as a packed 4-byte color whatever its size, so a 1-byte field of that name was read and written as 4 bytes. Each field is now read as a double according to its datatype, averaged in an Eigen::VectorXd and written back as its datatype, with integers rounded to the nearest value and saturated to their range. The rgb special case applies only to 4-byte rgb/rgba fields, and packs the color as std::uint32_t so that an alpha of 128 or more no longer overflows a signed int.
f513879 to
6e099d3
Compare
|
Thanks for the pull request! Regarding |
VoxelGrid<PCLPointCloud2> now reads the fields with pcl::point_field_as and writes them with storeFieldValue, moved to common/io.h and common/impl/io.hpp (with storeFieldValueAs) so that other classes can use it too. point_field_as gains the INT8 case it lacked, and reinterpret_and_cast copies the value with memcpy instead of dereferencing a cast pointer, as the fields of a PCLPointCloud2 need not be aligned (e.g. a double right after a 1 byte field).
|
Thanks for the review! Done in the new commit:
I kept it as a separate commit so the change is easy to see; I can squash it, or rename the functions if you prefer other names. |
| { | ||
| float r = centroid[centroid_size-4], g = centroid[centroid_size-3], b = centroid[centroid_size-2], a = centroid[centroid_size-1]; | ||
| int rgb = (static_cast<int> (a) << 24) | (static_cast<int> (r) << 16) | (static_cast<int> (g) << 8) | static_cast<int> (b); | ||
| double r = centroid[centroid_size-4], g = centroid[centroid_size-3], b = centroid[centroid_size-2], a = centroid[centroid_size-1]; |
There was a problem hiding this comment.
Here, a std::round would probably make sense (before casting to an integer)? For example, if we have 3 points with red values 255, 255, and 254 respectively, the best value to represent the centroid would be 255, not 254.
There was a problem hiding this comment.
Good point, done in b6d482e: each averaged channel (r, g, b and a) is now rounded with std::round before the cast. I also added a test, VoxelGrid.DownsampleAllDataRGBARounded, which checks your example (255, 255 and 254 give 255).
| * | ||
| * \param value value to store | ||
| * \param data data pointer | ||
| * \param type point field type |
There was a problem hiding this comment.
| * \param type point field type | |
| * \param type point field type (see pcl::PCLPointField::PointFieldTypes) |
The r/g/b/a averages of the rgb/rgba special case are rounded to the nearest value before they are packed again (e.g. 255, 255 and 254 give 255, not 254), and storeFieldValue's documentation names the PointFieldTypes its type takes.
With
downsample_all_data_(the default),VoxelGrid<PCLPointCloud2>::applyFilteraverages every field by copyingfield_sizes_[d]raw bytes into onefloatof anEigen::VectorXfand copying them back out:bool,std::{u}int64_tas possible point field types #4133,field_sizes_allows 8 bytes forFLOAT64,INT64andUINT64fields, so such a field was written into itsfloatand the next one, and past the end of the vector when it is the last field (for example adoubletimestamp after x, y, z). The copy back reads past the end in the same way. AddressSanitizer reports a heap-buffer-overflow write inapplyFilterfor such a cloud with the default settings.FLOAT32: signed integers, 32-bit integers and doubles came out wrong.rgborrgbawas taken as a packed 4-byte color whatever its size, so a 1-byte field of that name was read and written as 4 bytes, overwriting the fields after it.Each field is now read as a
doubleaccording to its datatype, averaged in anEigen::VectorXdand written back as its datatype, with integers rounded to the nearest value and saturated to their range. The rgb special case applies only to 4-bytergb/rgbafields, and packs the color asstd::uint32_t, so that an alpha of 128 or more no longer overflows a signedint. Clouds offloatfields and a packed rgb/rgba are averaged as before, now accumulated in double precision.The new tests
VoxelGrid.DownsampleAllDataFieldTypes(x, y, z, signed and unsigned integers and adoubleas the last field) andVoxelGrid.DownsampleAllDataSmallRGBField(a 1-bytergbfollowed by another field) check the averaged values. Without the fix, the first stops at the write past the end under AddressSanitizer and the second gets a wrong value for the field afterrgb. The wholetest_filterssuite passes.