-
Notifications
You must be signed in to change notification settings - Fork 335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make voxel computation consistent across all source code #354
Conversation
…d::floor to have explicit control over type casting
Looks good, thanks! Do you have an estimate of how much it impacts the results on the common datasets? |
I am running some experiments now to see the impact on performance. I will report them soon. |
It looks like the results get slightly worse. However, the old voxel index computation was actually wrong/unsafe, especially if different voxelizations are used throughout the code. |
I'm unsure if the computation time is comparable, but I don't see why this should be much slower. @saurabh1002, can you verify again using a single thread? |
This is single threaded with |
The slight drop in frame rate is expected since by fixing the voxel computation, there are more voxels that need to be processed both in the input frame and the map. The earlier voxelization was leading to a larger voxel (double the |
Looks goot to me. |
Okay, that explanation makes sense and with the single-thread implementation, the effect does not seem to be big. Also, if you want to have a faster runtime from having larger voxels you should increase their size instead. A general idea that we could follow up would be to try a polar grid for downsampling, this will keep a higher density for close points and a smaller density for far-away points. But that's outside of this PR. Good job @saurabh1002 :) |
Pull this to fix the CI :)
Sorry for the dismissal; I used the power of |
Before I even go through the implementation and analyze results, etc. I tested the situation mentioned in the referenced ticket and randomly picked one point, but I don't see any difference. #include <Eigen/Core>
#include <iostream>
const double voxel_size = 1.0;
inline Eigen::Vector3i PointToVoxel(const Eigen::Vector3d &point) {
return (point / voxel_size).array().floor().cast<int>();
}
inline Eigen::Vector3i PointToVoxel2(const Eigen::Vector3d &point) {
return Eigen::Vector3i((point / voxel_size).cast<int>());
}
int main() {
const Eigen::Vector3d point{0.1, 0.3, 1.9};
std::cout << "Point = " << point.transpose() << std::endl;
std::cout << "Old Voxel = " << PointToVoxel2(point).transpose() << std::endl;
std::cout << "New Voxel = " << PointToVoxel(point).transpose() << std::endl;
return 0;
} Is there any easy isolation test for this? |
#include <Eigen/Core>
#include <iostream>
const double voxel_size = 1.0;
inline Eigen::Vector3i PointToVoxel(const Eigen::Vector3d &point)
{
return (point / voxel_size).array().floor().cast<int>();
}
inline Eigen::Vector3i PointToVoxel2(const Eigen::Vector3d &point)
{
return Eigen::Vector3i((point / voxel_size).cast<int>());
}
inline Eigen::Vector3i PointToVoxel3(const Eigen::Vector3d &point)
{
return Eigen::Vector3i(static_cast<int>(point.x() / voxel_size),
static_cast<int>(point.y() / voxel_size),
static_cast<int>(point.z() / voxel_size));
}
int main()
{
const Eigen::Vector3d point_1{0, 0, 0.99};
std::cout << "Point = " << point_1.transpose() << std::endl;
std::cout << "Eigen - Floor Array = " << PointToVoxel(point_1).transpose() << std::endl;
std::cout << "Eigen - cast int = " << PointToVoxel2(point_1).transpose() << std::endl;
std::cout << "std - static cast = " << PointToVoxel3(point_1).transpose() << std::endl;
std::cout << std::endl << std::endl;
const Eigen::Vector3d point_2{0, 0, -0.99};
std::cout << "Point = " << point_2.transpose() << std::endl;
std::cout << "Eigen - Floor Array = " << PointToVoxel(point_2).transpose() << std::endl;
std::cout << "Eigen - cast int = " << PointToVoxel2(point_2).transpose() << std::endl;
std::cout << "std - static cast = " << PointToVoxel3(point_2).transpose() << std::endl;
return 0;
} For a voxel size of 1.0, both |
This behaviour exists along each point coordinate which is in the range of |
@saurabh1002 Thanks, fantastic explanation!! I'm only concerned about performance, as this operation is roughly 8 times slower than we have now. Is there any significant advantage of this? Besides correctness? Edit about performance:I just quickly benchmarked this voxelization function, and it's on par with what we have now. Maybe the magic Eigen template is not working great, and I don't want to look into 🦖 inline Eigen::Vector3i PointToVoxel4(const Eigen::Vector3d& point) {
return {static_cast<int>(std::floor(point.x() / voxel_size)),
static_cast<int>(std::floor(point.y() / voxel_size)),
static_cast<int>(std::floor(point.z() / voxel_size))};
} BTW, this finally shed some light on these artifacts. We always knew it was there, but we always blamed the hashing library :) mainthis@tizianoGuadagnino what's your view on this? |
@nachovizzo what is that tool for benchmarking that u used? |
@saurabh1002 I pushed my proposal to avoid creating another PR. Feel free to revert google benchmark #include <benchmark/benchmark.h>
#include <Eigen/Core>
const double voxel_size = 1.0;
inline Eigen::Vector3i PointToVoxel(const Eigen::Vector3d& point) {
return (point / voxel_size).array().floor().cast<int>();
}
inline Eigen::Vector3i PointToVoxel4(const Eigen::Vector3d& point) {
return {static_cast<int>(std::floor(point.x() / voxel_size)),
static_cast<int>(std::floor(point.y() / voxel_size)),
static_cast<int>(std::floor(point.z() / voxel_size))};
}
inline Eigen::Vector3i PointToVoxel2(const Eigen::Vector3d& point) {
return Eigen::Vector3i((point / voxel_size).cast<int>());
}
inline Eigen::Vector3i PointToVoxel3(const Eigen::Vector3d& point) {
return Eigen::Vector3i(static_cast<int>(point.x() / voxel_size),
static_cast<int>(point.y() / voxel_size),
static_cast<int>(point.z() / voxel_size));
}
static void BM_PointToVoxel(benchmark::State& state) {
Eigen::Vector3d point(1.0, 2.0, 3.0);
for (auto _ : state) {
auto voxel = PointToVoxel(point);
benchmark::DoNotOptimize(voxel);
}
}
static void BM_PointToVoxel2(benchmark::State& state) {
Eigen::Vector3d point(1.0, 2.0, 3.0);
for (auto _ : state) {
auto voxel = PointToVoxel2(point);
benchmark::DoNotOptimize(voxel);
}
}
static void BM_PointToVoxel3(benchmark::State& state) {
Eigen::Vector3d point(1.0, 2.0, 3.0);
for (auto _ : state) {
auto voxel = PointToVoxel3(point);
benchmark::DoNotOptimize(voxel);
}
}
static void BM_PointToVoxel4(benchmark::State& state) {
Eigen::Vector3d point(1.0, 2.0, 3.0);
for (auto _ : state) {
auto voxel = PointToVoxel3(point);
benchmark::DoNotOptimize(voxel);
}
}
BENCHMARK(BM_PointToVoxel);
BENCHMARK(BM_PointToVoxel2);
BENCHMARK(BM_PointToVoxel3);
BENCHMARK(BM_PointToVoxel4);
BENCHMARK_MAIN(); |
@nachovizzo I am a bit lost on these benchmark results. Give me the data association between |
@tizianoGuadagnino is on the name of the functions but here we go |
Make voxel computation consistent across all source code (PRBonn#354)
Make voxel computation consistent across all source code (PRBonn#354)
Eigen::Vector3i
.std::floor
function fromnumerics
standard library to have control over the rounding off done while convertingEigen::Vector3d
toEigen::Vector3i
. As mentioned also in Unintendet behaviour in kiss_icp/cpp/kiss_icp/core/VoxelHashMap.cppAddPoints
? #191, it avoids the issue of having large voxels around the origin of each axes.