Robustified frame transformations. Fixed image projection. Added quick performance test comparing multithreading with single thread projection.

This commit is contained in:
Jerome 2023-05-01 18:16:13 +02:00
parent bd6948ecdc
commit 4c6de81a5f
10 changed files with 363 additions and 95 deletions

19
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,19 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "360 to perspective",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/cpp/obj/360_to_perspective.exe",
"cwd": "${workspaceFolder}/cpp",
"environment": [],
"args": [],
"stopAtEntry": false,
"externalConsole": false
}
]
}

21
.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,21 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "D:/Programmes/Octave/Octave-7.3.0/usr/bin/make.exe",
"args": [
"-f",
"${workspaceFolder}/cpp/Makefile"
],
"options": {
"cwd": "${workspaceFolder}/cpp"
},
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View file

@ -3,7 +3,8 @@
#include <Eigen/Dense> #include <Eigen/Dense>
#define DEG2RAD 0.017453292519943295 #define DEG2RAD 0.01745329251994329576923690768488612713442871888541725456097191440171009114603449443682241569634509482
#define RAD2DEG 57.29577951308232087679815481410517033240547246656432154916024386120284714832155263244096899585111094
/// @brief A camera class to perform frame transformations and projections. /// @brief A camera class to perform frame transformations and projections.
class Camera { class Camera {

View file

@ -4,6 +4,38 @@
#include <Eigen/Dense> #include <Eigen/Dense>
#include <cmath> #include <cmath>
#ifndef M_PI
#define M_PI 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068
#endif
namespace frame_conversions {
/// @brief Computes the modulo of a number with another number, ensuring that the results lies in the interval [0;b].
/// @param a The number to compute the modulo of.
/// @param b The modulo.
/// @return The modulo of a with b, ensuring that the result lies in the interval [0;b]
template <typename T>
inline T fmod_pos(T a, T b) {
return a - b*std::floor(a/b);
}
/// @brief Maps a value x from the range (a,b) to the range (c,d).
///
/// This function takes an input value x and maps it from the input range (a,b)
/// to the output range (c,d). The resulting mapped value is returned.
///
/// @param x The value to be mapped.
/// @param a The lower bound of the input range.
/// @param b The upper bound of the input range.
/// @param c The lower bound of the output range.
/// @param d The upper bound of the output range.
///
/// @return The mapped value of x in the output range.
///
/// @note Make sure that a != b to avoid a divide-by-zero error.
double map(double x, double a, double b, double c, double d);
} // namespace frame_conversions
/// @brief Converts a spherical vector [r, theta, phi] in ISO convention (theta=0 is Z+) to a cartesian vector. /// @brief Converts a spherical vector [r, theta, phi] in ISO convention (theta=0 is Z+) to a cartesian vector.
Eigen::Vector3d sph2cart(const Eigen::Vector3d & sph); Eigen::Vector3d sph2cart(const Eigen::Vector3d & sph);
@ -16,7 +48,7 @@ Eigen::Vector3d cart2sph(const Eigen::Vector3d & cart);
/// @param width Width of the equirectangular image. /// @param width Width of the equirectangular image.
/// @param height Height of the equirectangular image. /// @param height Height of the equirectangular image.
/// @return A spherical vector [r, theta, phi] in ISO convention (theta=0 is Z+). /// @return A spherical vector [r, theta, phi] in ISO convention (theta=0 is Z+).
Eigen::Vector3d equirectangular2sph(unsigned int i, unsigned int j, unsigned int width, unsigned int height); Eigen::Vector3d equirectangular2sph(int i, int j, unsigned int width, unsigned int height);
/// @brief Converts spherical coordinates to pixel coordinates of an equirectangular image. /// @brief Converts spherical coordinates to pixel coordinates of an equirectangular image.
/// @param p_sph A spherical vector [r, theta, phi] in ISO convention (theta=0 is Z+). /// @param p_sph A spherical vector [r, theta, phi] in ISO convention (theta=0 is Z+).

View file

@ -0,0 +1,135 @@
#ifndef h_test_framework
#define h_test_framework
#ifndef TOL_EQ_TEST
#define TOL_EQ_TEST 1e-9
#endif
static size_t num_tests = 0;
static size_t num_tests_ok = 0;
static size_t num_tests_ko = 0;
#define PRINT_VAR(x) std::cout << #x << "\t= " << (x) << std::endl
// EXPECT_EQ macro
#define EXPECT_EQ(actual, expected) EXPECT_EQ_IMPL(actual, expected, __FILE__, __LINE__)
#define EXPECT_EQ_IMPL(actual, expected, file, line) \
do { \
num_tests++; \
auto&& e = (expected); \
auto&& a = (actual); \
if (e != a) { \
std::cout << file << ":" << line << " : " << "Expected: " << (#expected) << " = " << e << ", actual: " << (#actual) << " = " << a << " " << std::endl; \
num_tests_ko++; \
} \
else { \
num_tests_ok++; \
} \
} while (false)
// EXPECT_DOUBLE_EQ macro
#define EXPECT_DOUBLE_EQ(actual, expected) EXPECT_DOUBLE_EQ_IMPL(actual, expected, __FILE__, __LINE__)
#define EXPECT_DOUBLE_EQ_IMPL(actual, expected, file, line) \
do { \
num_tests++; \
double e = (expected); \
double a = (actual); \
if (std::abs(e - a) > TOL_EQ_TEST) { \
std::cout << file << ":" << line << " : " << "Expected: " << (#expected) << " = " << e << ", actual: " << (#actual) << " = " << a << " " << std::endl; \
num_tests_ko++; \
} \
else { \
num_tests_ok++; \
} \
} while (false)
// EXPECT_LT macro
#define EXPECT_LT(actual, expected) EXPECT_LT_IMPL(actual, expected, __FILE__, __LINE__)
#define EXPECT_LT_IMPL(actual, expected, file, line) \
do { \
num_tests++; \
auto&& e = (expected); \
auto&& a = (actual); \
if (a < e) { \
num_tests_ok++; \
} \
else { \
std::cout << file << ":" << line << " : " << "Expected " << a << " < " << (#expected) << " = " << e << ", actual: " << (#actual) << std::endl; \
num_tests_ko++; \
} \
} while (false)
// EXPECT_LE macro
#define EXPECT_LE(actual, expected) EXPECT_LE_IMPL(actual, expected, __FILE__, __LINE__)
#define EXPECT_LE_IMPL(actual, expected, file, line) \
do { \
num_tests++; \
auto&& e = (expected); \
auto&& a = (actual); \
if (a <= e) { \
num_tests_ok++; \
} \
else { \
std::cout << file << ":" << line << " : " << "Expected " << a << " <= " << (#expected) << " = " << e << ", actual: " << (#actual) << std::endl; \
num_tests_ko++; \
} \
} while (false)
// EXPECT_GT macro
#define EXPECT_GT(actual, expected) EXPECT_GT_IMPL(actual, expected, __FILE__, __LINE__)
#define EXPECT_GT_IMPL(actual, expected, file, line) \
do { \
num_tests++; \
auto&& e = (expected); \
auto&& a = (actual); \
if (a > e) { \
num_tests_ok++; \
} \
else { \
std::cout << file << ":" << line << " : " << "Expected " << a << " > " << (#expected) << " = " << e << ", actual: " << (#actual) << std::endl; \
num_tests_ko++; \
} \
} while (false)
// EXPECT_GE macro
#define EXPECT_GE(actual, expected) EXPECT_GE_IMPL(actual, expected, __FILE__, __LINE__)
#define EXPECT_GE_IMPL(actual, expected, file, line) \
do { \
num_tests++; \
auto&& e = (expected); \
auto&& a = (actual); \
if (a >= e) { \
num_tests_ok++; \
} \
else { \
std::cout << file << ":" << line << " : " << "Expected " << a << " >= " << (#expected) << " = " << e << ", actual: " << (#actual) << std::endl; \
num_tests_ko++; \
} \
} while (false)
// EXPECT_VEC_EQ macro
#define EXPECT_VEC_EQ(actual, expected) EXPECT_VEC_EQ_IMPL(actual, expected, __FILE__, __LINE__)
#define EXPECT_VEC_EQ_IMPL(actual, expected, file, line) \
do { \
for(unsigned int i = 0 ; i < (expected).size() ; i++) { \
EXPECT_DOUBLE_EQ_IMPL((actual)[i], (expected)[i], file, line); \
} \
} while (false)
// EXPECT_MAT_EQ macro
#define EXPECT_MAT_EQ(actual, expected) EXPECT_MAT_EQ_IMPL(actual, expected, __FILE__, __LINE__)
#define EXPECT_MAT_EQ_IMPL(actual, expected, file, line) \
do { \
for(unsigned int i = 0 ; i < (expected).rows() ; i++) { \
for(unsigned int j = 0 ; j < (expected).rows() ; j++) { \
EXPECT_DOUBLE_EQ_IMPL((actual)(i,j), (expected)(i,j), file, line); \
} \
} \
} while (false)
#define PRINT_TESTS_SUMMARY() \
do { \
std::cout << "Tests summary: " << num_tests << " tests in total\n " << num_tests_ok << "/" << num_tests << " OK (" << ((100.0*(double)num_tests_ok)/num_tests) << " %)\n " << num_tests_ko << "/" << num_tests << " KO (" << ((100.0*(double)num_tests_ko)/num_tests) << " %)" << std::endl; \
} while (false)
#endif

View file

@ -1,8 +1,5 @@
#include <Camera.hpp> #include <Camera.hpp>
#define _USE_MATH_DEFINES
#include <cmath>
Camera::Camera() Camera::Camera()
: fwd(Eigen::Vector3d::UnitX()), up(Eigen::Vector3d::UnitZ()), R(Eigen::Matrix3d::Identity()), R_T(Eigen::Matrix3d::Identity()), FOV(90.0*DEG2RAD), tanFOV2(tan(FOV/2.0)) : fwd(Eigen::Vector3d::UnitX()), up(Eigen::Vector3d::UnitZ()), R(Eigen::Matrix3d::Identity()), R_T(Eigen::Matrix3d::Identity()), FOV(90.0*DEG2RAD), tanFOV2(tan(FOV/2.0))
{ {

View file

@ -109,7 +109,9 @@ Eigen::Vector3i Image::GetPixelInterp(Eigen::Vector2d const& pos, InterpMethod c
int j = (int)std::floor(pos[1]); int j = (int)std::floor(pos[1]);
double u = pos[0] - i; double u = pos[0] - i;
double v = pos[1] - j; double v = pos[1] - j;
Eigen::Vector3i rgb = ((1 - u) * (1 - v) * GetPixel(i, j).cast<double>() + u * (1 - v) * GetPixel(i + 1, j).cast<double>() + (1 - u) * v * GetPixel(i, j + 1).cast<double>() + u * v * GetPixel(i + 1, j + 1).cast<double>()).cast<int>(); int i_plus_1 = (i + 1) % height;
int j_plus_1 = (j + 1) % width;
Eigen::Vector3i rgb = ((1 - u) * (1 - v) * GetPixel(i, j).cast<double>() + u * (1 - v) * GetPixel(i_plus_1, j).cast<double>() + (1 - u) * v * GetPixel(i, j_plus_1).cast<double>() + u * v * GetPixel(i_plus_1, j_plus_1).cast<double>()).cast<int>();
return rgb; return rgb;
} }
else // By default, use nearest neighbor interpolation else // By default, use nearest neighbor interpolation

View file

@ -60,7 +60,7 @@ Eigen::Vector3i Slicer360ToPerspective::ProjectToCameraPixel(Camera const& camer
// Compute ray direction from sensor coordinates // Compute ray direction from sensor coordinates
Eigen::Vector3d ray_dir = camera.ComputeRayDirInInertialFrame(p_sensor); Eigen::Vector3d ray_dir = camera.ComputeRayDirInInertialFrame(p_sensor);
Eigen::Vector3d ray_dir_sph = cart2sph(ray_dir); Eigen::Vector3d ray_dir_sph = cart2sph(ray_dir);
Eigen::Vector2d p_equi = sph2equirectangular_d(ray_dir_sph, output_image_width, output_image_height); Eigen::Vector2d p_equi = sph2equirectangular_d(ray_dir_sph, panorama_image.GetWidth(), panorama_image.GetHeight());
return panorama_image.GetPixelInterp(p_equi, interpolation_method); return panorama_image.GetPixelInterp(p_equi, interpolation_method);
} }

View file

@ -1,6 +1,12 @@
#include <frame_conversions.hpp> #include <frame_conversions.hpp>
#define M_PI 3.14159265358979323846 namespace frame_conversions {
double map(double x, double a, double b, double c, double d) {
return c + (x - a) * (d - c) / (b - a);
}
}// namespace frame_conversions
Eigen::Vector3d sph2cart(const Eigen::Vector3d & sph) { Eigen::Vector3d sph2cart(const Eigen::Vector3d & sph) {
double r = sph(0); double r = sph(0);
@ -18,19 +24,20 @@ Eigen::Vector3d cart2sph(const Eigen::Vector3d & cart) {
double z = cart(2); double z = cart(2);
double r = sqrt(x*x + y*y + z*z); double r = sqrt(x*x + y*y + z*z);
double theta = acos(z/r); double theta = acos(z/r);
double phi = atan2(y,x); double phi = frame_conversions::fmod_pos(atan2(y,x), 2.0*M_PI);
return Eigen::Vector3d(r,theta,phi); return Eigen::Vector3d(r,theta,phi);
} }
Eigen::Vector3d equirectangular2sph(unsigned int i, unsigned int j, unsigned int width, unsigned int height) { Eigen::Vector3d equirectangular2sph(int i, int j, unsigned int width, unsigned int height) {
return Eigen::Vector3d(1.0, M_PI*(((double)i)/(height-1)), 2.0*M_PI*(((double)j)/width - 0.5)); return Eigen::Vector3d(1.0, std::max(0.0, std::min(M_PI*(((double)i)/(height-1)), M_PI)), frame_conversions::fmod_pos(2.0*M_PI*((double)j)/((double)width), 2.0*M_PI));
} }
Eigen::Vector2d sph2equirectangular_d(Eigen::Vector3d const& p_sph, unsigned int width, unsigned int height) { Eigen::Vector2d sph2equirectangular_d(Eigen::Vector3d const& p_sph, unsigned int width, unsigned int height) {
double theta = p_sph[1]; double theta = std::max(0.0, std::min(p_sph[1], M_PI));
double phi = p_sph[2]; double phi = frame_conversions::fmod_pos(p_sph[2], 2.0*M_PI);
double i = std::fmod(((height - 1)*theta)/M_PI, height); double i = ((height - 1)*theta)/M_PI;
double j = std::fmod((width*(M_PI + phi))/(2.0*M_PI), width); // double j = std::fmod((width*phi)/(2.0*M_PI), width);
double j = std::fmod(frame_conversions::map(phi, 0.0, 2.0*M_PI, 0.0, width), width);
return Eigen::Vector2d(i, j); return Eigen::Vector2d(i, j);
} }

View file

@ -2,80 +2,98 @@
#include <Camera.hpp> #include <Camera.hpp>
#include <Image.hpp> #include <Image.hpp>
#include <Slicer360ToPerspective.hpp> #include <Slicer360ToPerspective.hpp>
#include <frame_conversions.hpp>
#define _USE_MATH_DEFINES #include <test_framework.hpp>
#include <cmath>
constexpr double TOL_EQ_TEST = 1e-9;
constexpr double M_PI = 3.14159265358979323846;
static size_t num_tests = 0;
static size_t num_tests_ok = 0;
static size_t num_tests_ko = 0;
#define PRINT_VAR(x) std::cout << #x << "\t= " << (x) << std::endl
// EXPECT_EQ macro
#define EXPECT_EQ(actual, expected) EXPECT_EQ_IMPL(actual, expected, __FILE__, __LINE__)
#define EXPECT_EQ_IMPL(actual, expected, file, line) \
do { \
num_tests++; \
auto&& e = (expected); \
auto&& a = (actual); \
if (e != a) { \
std::cout << file << ":" << line << " : " << "Expected: " << (#expected) << " = " << e << ", actual: " << (#actual) << " = " << a << " " << std::endl; \
num_tests_ko++; \
} \
else { \
num_tests_ok++; \
} \
} while (false)
// EXPECT_DOUBLE_EQ macro
#define EXPECT_DOUBLE_EQ(actual, expected) EXPECT_DOUBLE_EQ_IMPL(actual, expected, __FILE__, __LINE__)
#define EXPECT_DOUBLE_EQ_IMPL(actual, expected, file, line) \
do { \
num_tests++; \
double e = (expected); \
double a = (actual); \
if (std::abs(e - a) > TOL_EQ_TEST) { \
std::cout << file << ":" << line << " : " << "Expected: " << (#expected) << " = " << e << ", actual: " << (#actual) << " = " << a << " " << std::endl; \
num_tests_ko++; \
} \
else { \
num_tests_ok++; \
} \
} while (false)
// EXPECT_VEC_EQ macro
#define EXPECT_VEC_EQ(actual, expected) EXPECT_VEC_EQ_IMPL(actual, expected, __FILE__, __LINE__)
#define EXPECT_VEC_EQ_IMPL(actual, expected, file, line) \
do { \
for(unsigned int i = 0 ; i < (expected).size() ; i++) { \
EXPECT_DOUBLE_EQ_IMPL((actual)[i], (expected)[i], file, line); \
} \
} while (false)
// EXPECT_MAT_EQ macro
#define EXPECT_MAT_EQ(actual, expected) EXPECT_MAT_EQ_IMPL(actual, expected, __FILE__, __LINE__)
#define EXPECT_MAT_EQ_IMPL(actual, expected, file, line) \
do { \
for(unsigned int i = 0 ; i < (expected).rows() ; i++) { \
for(unsigned int j = 0 ; j < (expected).rows() ; j++) { \
EXPECT_DOUBLE_EQ_IMPL((actual)(i,j), (expected)(i,j), file, line); \
} \
} \
} while (false)
#define PRINT_TESTS_SUMMARY() \
do { \
std::cout << "Tests summary: " << num_tests << " tests in total\n " << num_tests_ok << "/" << num_tests << " OK (" << ((100.0*(double)num_tests_ok)/num_tests) << " %)\n " << num_tests_ko << "/" << num_tests << " KO (" << ((100.0*(double)num_tests_ko)/num_tests) << " %)" << std::endl; \
} while (false)
int main() { int main() {
std::cout.precision(16); std::cout.precision(16);
std::cout << "Running Unit Tests\n"; std::cout << "Running Unit Tests\n";
if(0){// frame transforms
std::cout << "frame transforms\n";
// PRINT_VAR(sph2equirectangular_d(Eigen::Vector3d(1.0, 0., 0.), 100, 50).transpose());
// PRINT_VAR(sph2equirectangular_d(Eigen::Vector3d(1.0, 0., 1.), 100, 50).transpose());
// PRINT_VAR(sph2equirectangular_d(Eigen::Vector3d(1.0, M_PI/2.0, 0.), 100, 50).transpose());
// PRINT_VAR(sph2equirectangular_d(Eigen::Vector3d(1.0, M_PI/2.0, M_PI/2.0), 100, 50).transpose());
// PRINT_VAR(sph2equirectangular_d(Eigen::Vector3d(1.0, M_PI/2.0, M_PI), 100, 50).transpose());
// PRINT_VAR(sph2equirectangular_d(Eigen::Vector3d(1.0, M_PI/2.0, -M_PI/2.0), 100, 50).transpose());
// PRINT_VAR(sph2equirectangular_d(Eigen::Vector3d(1.0, -M_PI/2.0, 0.), 100, 50).transpose());
// PRINT_VAR(sph2equirectangular_d(Eigen::Vector3d(1.0, M_PI/2.0, -M_PI/2.0), 100, 50).transpose());
// PRINT_VAR(sph2equirectangular_d(Eigen::Vector3d(1.0, -M_PI/2.0, -M_PI), 100, 50).transpose());
// PRINT_VAR(sph2equirectangular_d(Eigen::Vector3d(1.0, M_PI/2.0, -2.5*M_PI), 100, 50).transpose());
std::cout << "Testing equirectangular coordinates.\n";
int i = 0;
int j = 0;
int width = 10;
int height = 5;
if(1) { std::cout << "\nTesting equirectangular2sph.\n";
for(i = -2*height ; i < 2*height ; i++) {
for(j = -2*width ; j < 2*width ; j++) {
Eigen::Vector3d p_sph = equirectangular2sph(i, j, width, height);
// std::cout << "i: " << i << ", j: " << j << ", p_sph: " << p_sph.transpose() << "\n";
EXPECT_DOUBLE_EQ(p_sph[0], 1.0);
EXPECT_LT(p_sph[1], M_PI+TOL_EQ_TEST);
EXPECT_GE(p_sph[1], 0.0);
EXPECT_LT(p_sph[2], 2.0*M_PI);
EXPECT_GE(p_sph[2], 0.0);
}
}
}
if(1) { std::cout << "\nTesting roundtrip.\n";
for(i = 0 ; i < height ; i++) {
for(j = 0 ; j < width ; j++) {
Eigen::Vector3d p_sph = equirectangular2sph(i, j, width, height);
Eigen::Vector2d p_equirectangular = sph2equirectangular_d(p_sph, width, height);
// std::cout << "i: " << i << ", j: " << j << ", p_sph: " << p_sph.transpose() << ", p_equirectangular: " << p_equirectangular.transpose() << ((p_equirectangular[0] > height) ? "I OUT OF BOUNDS " : "") << ((p_equirectangular[1] > width) ? "J OUT OF BOUNDS " : "") << "\n";
EXPECT_DOUBLE_EQ(p_equirectangular[0], i);
EXPECT_DOUBLE_EQ(p_equirectangular[1], j);
EXPECT_LT(p_equirectangular[0], height);
EXPECT_GE(p_equirectangular[0], 0.0);
EXPECT_LT(p_equirectangular[1], width);
EXPECT_GE(p_equirectangular[1], 0.0);
}
}
}
if(1) { std::cout << "\nTesting sph2equirectangular_d out of bounds.\n";
width = 3600;
height = 1801;
double theta = 0.0;
double phi = M_PI/4.0;
std::cout << "\nTesting sph2equirectangular_d out of bounds in THETA.\n";
for(theta = -4.0*M_PI ; theta < 4.0*M_PI ; theta += M_PI/10.0) {
auto p_equi = sph2equirectangular_d(Eigen::Vector3d(1.0, theta, phi), width, height).transpose();
// std::cout << "theta: " << theta << ", phi: " << phi << ", p_equirectangular: " << p_equi << "\n";
EXPECT_EQ(p_equi[0] < 0, false);
EXPECT_EQ(p_equi[0] >= height, false);
}
std::cout << "\nTesting sph2equirectangular_d out of bounds in PHI.\n";
theta = M_PI/2.0;
for(phi = -4.0*M_PI ; phi < 4.0*M_PI ; phi += M_PI/10.0) {
auto p_equi = sph2equirectangular_d(Eigen::Vector3d(1.0, theta, phi), width, height).transpose();
// std::cout << "theta: " << theta << ", phi: " << phi << ",\tp_equirectangular: " << p_equi << "\n";
EXPECT_EQ(p_equi[1] < 0, false);
EXPECT_EQ(p_equi[1] >= width, false);
}
}
if(1) { std::cout << "\nTesting fmod.\n";
for(double x = -4.0*M_PI ; x < 4.0*M_PI ; x += M_PI/10.0) {
// std::cout << x << "\t" << std::fmod(x, 2.0*M_PI) << "\t" << frame_conversions::fmod_pos(x, 2.0*M_PI) << "\n";
EXPECT_EQ(frame_conversions::fmod_pos(x, 2.0*M_PI) >= 0.0, true);
EXPECT_EQ(frame_conversions::fmod_pos(x, 2.0*M_PI) < 2.0*M_PI, true);
}
}
}
if(0){// Camera if(0){// Camera
std::cout << "Camera\n"; std::cout << "Camera\n";
{// Constructor, accessors and setters {// Constructor, accessors and setters
@ -315,20 +333,56 @@ int main() {
} }
} }
{ if(1){// Test Slicer360ToPerspective
std::cout << "Coverage analysis\n"; if(0) { std::cout << "Coverage analysis\n";
Slicer360ToPerspective slicer; Slicer360ToPerspective slicer;
double fov = 90.0*DEG2RAD; double fov = 90.0*DEG2RAD;
int width = 500; int width = 500;
slicer.cameras.push_back(Camera().SetFOV(fov).SetTarget(Eigen::Vector3d(1., 0., 0.))); slicer.cameras.push_back(Camera().SetFOV(fov).SetTarget(Eigen::Vector3d(1., 0., 0.)));
slicer.cameras.push_back(Camera().SetFOV(fov).SetTarget(Eigen::Vector3d(-1., -1., -1.))); slicer.cameras.push_back(Camera().SetFOV(fov).SetTarget(Eigen::Vector3d(-1., -1., -1.)));
slicer.cameras.push_back(Camera().SetFOV(fov/2).SetTarget(Eigen::Vector3d(1., 0.2, -0.3))); slicer.cameras.push_back(Camera().SetFOV(fov/2).SetTarget(Eigen::Vector3d(1., 0.2, -0.3)));
Image coverage_raw = slicer.ComputeCoverage(width, true); Image coverage_raw = slicer.ComputeCoverage(width, true);
Image coverage_col = slicer.ComputeCoverage(width, false); Image coverage_col = slicer.ComputeCoverage(width, false);
coverage_raw.Save("coverage_raw.png"); coverage_raw.Save("coverage_raw.png");
coverage_col.Save("coverage_col.png"); coverage_col.Save("coverage_col.png");
}
if(1){ std::cout << "Perspective Projection\n";
double Mpx = 1e6; // Image size in Mpx
double ratio = 16./9.; // Image ratio
int width = sqrt(Mpx)*sqrt(ratio);
int height = sqrt(Mpx)/sqrt(ratio);
Slicer360ToPerspective slicer = Slicer360ToPerspective(Image("../venise.jpg")).SetOutputImageSize(width, height).SetInterpolationMethod(Image::InterpMethod::BILINEAR);
Camera camera = Camera().SetFOV(90.0*DEG2RAD).SetTarget(Eigen::Vector3d(1., 0., 0.));
slicer.cameras.push_back(camera);
slicer.ProjectToCamera(camera, true).Save("venise_proj_01.png");
// measure performance
double t0, t1;
t0 = omp_get_wtime();
slicer.ProjectToCamera(camera, false);
t1 = omp_get_wtime();
std::cout << "Projection time single-threaded BILINEAR : " << t1-t0 << " s\n";
t0 = omp_get_wtime();
slicer.ProjectToCamera(camera, true);
t1 = omp_get_wtime();
std::cout << "Projection time multi-threaded BILINEAR : " << t1-t0 << " s\n";
slicer.SetInterpolationMethod(Image::InterpMethod::NEAREST);
t0 = omp_get_wtime();
slicer.ProjectToCamera(camera, false);
t1 = omp_get_wtime();
std::cout << "Projection time single-threaded NEAREST : " << t1-t0 << " s\n";
t0 = omp_get_wtime();
slicer.ProjectToCamera(camera, true);
t1 = omp_get_wtime();
std::cout << "Projection time multi-threaded NEAREST : " << t1-t0 << " s\n";
}
} }
PRINT_TESTS_SUMMARY(); PRINT_TESTS_SUMMARY();