2023-04-30 10:52:23 +02:00
|
|
|
#include <iostream>
|
|
|
|
|
#include <Camera.hpp>
|
2023-04-30 14:42:10 +02:00
|
|
|
#include <Image.hpp>
|
2023-05-01 00:31:24 +02:00
|
|
|
#include <Slicer360ToPerspective.hpp>
|
2023-05-01 18:16:13 +02:00
|
|
|
#include <frame_conversions.hpp>
|
2023-05-03 22:58:34 +02:00
|
|
|
#include <parser.hpp>
|
|
|
|
|
#include <csv_parser.hpp>
|
2023-04-30 10:52:23 +02:00
|
|
|
|
2023-05-01 18:16:13 +02:00
|
|
|
#include <test_framework.hpp>
|
2023-04-30 10:52:23 +02:00
|
|
|
|
2023-05-03 22:11:30 +02:00
|
|
|
int main(int argc, char** argv) {
|
2023-04-30 10:52:23 +02:00
|
|
|
std::cout.precision(16);
|
|
|
|
|
std::cout << "Running Unit Tests\n";
|
|
|
|
|
|
2023-05-03 22:11:30 +02:00
|
|
|
if(0){// argument parser
|
|
|
|
|
std::cout << "argument parser\n";
|
|
|
|
|
|
|
|
|
|
// declare the parser
|
|
|
|
|
cmd_line_parser::parser parser(argc, argv);
|
|
|
|
|
|
|
|
|
|
// configure the parser
|
|
|
|
|
parser.add("perc", // name
|
|
|
|
|
"A percentage value.", // description
|
|
|
|
|
"-p", // shorthand
|
|
|
|
|
true, // required argument
|
|
|
|
|
false // not boolean option (default is false)
|
|
|
|
|
);
|
|
|
|
|
parser.add("input_filename", "An input file name.", "-i", true);
|
|
|
|
|
|
|
|
|
|
parser.add("output_filename", // name
|
|
|
|
|
"An output file name.", // description
|
|
|
|
|
"-o", // shorthand
|
|
|
|
|
false, false);
|
|
|
|
|
parser.add("num_trials", "Number of trials.", "-n", false, false);
|
|
|
|
|
|
|
|
|
|
parser.add("sorted", "Sort output.", "--sort", false,
|
|
|
|
|
true // boolean option: a value is not expected after the shorthand
|
|
|
|
|
);
|
|
|
|
|
parser.add("buffered", "Buffer input.", "--buffer", false, true);
|
|
|
|
|
|
|
|
|
|
parser.add("ram", "Amount of ram to use.", "--ram", false, false);
|
|
|
|
|
|
|
|
|
|
// parse command line and return on failure
|
|
|
|
|
bool success = parser.parse();
|
|
|
|
|
if (!success) return 1;
|
|
|
|
|
|
|
|
|
|
// now get some variables
|
|
|
|
|
auto perc = parser.get<float>("perc"); // deduced type is float
|
|
|
|
|
auto input_filename = // deduced type is std::string
|
|
|
|
|
parser.get<std::string>("input_filename");
|
|
|
|
|
auto sorted_output = parser.get<bool>("sorted"); // deduced type is bool
|
|
|
|
|
auto buffered_input = parser.get<bool>("buffered"); // deduced type is bool
|
|
|
|
|
|
|
|
|
|
size_t ram = 999; // some default value
|
|
|
|
|
if (parser.parsed("ram")) {
|
|
|
|
|
ram = parser.get<size_t>("ram"); // deduced type is size_t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << "perc: " << perc << std::endl;
|
|
|
|
|
std::cout << "input_filename: " << input_filename << std::endl;
|
|
|
|
|
std::cout << "sorted_output: " << sorted_output << std::endl;
|
|
|
|
|
std::cout << "buffered_input: " << buffered_input << std::endl;
|
|
|
|
|
std::cout << "ram: " << ram << std::endl;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
auto val = parser.get<int>("bar"); // fail: no name 'bar' was specified
|
|
|
|
|
(void)val; // shut up, compiler!
|
|
|
|
|
} catch (std::runtime_error const& e) {
|
|
|
|
|
std::cerr << e.what() << std::endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-01 18:16:13 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-03 22:11:30 +02:00
|
|
|
if(1){// Camera
|
2023-04-30 10:52:23 +02:00
|
|
|
std::cout << "Camera\n";
|
|
|
|
|
{// Constructor, accessors and setters
|
|
|
|
|
std::cout << "Constructor, accessors and setters\n";
|
|
|
|
|
|
|
|
|
|
// Create a camera object
|
|
|
|
|
Camera camera;
|
|
|
|
|
|
|
|
|
|
// Check constructor values using the getters
|
|
|
|
|
Eigen::Matrix3d default_R;// Rotation matrix with default fwd and up vectors.
|
|
|
|
|
default_R << 0, 0, -1,
|
|
|
|
|
-1, 0, 0,
|
|
|
|
|
0, 1, 0;
|
|
|
|
|
EXPECT_EQ(camera.GetForward(), Eigen::Vector3d(1, 0, 0));
|
|
|
|
|
EXPECT_EQ(camera.GetUp(), Eigen::Vector3d(0, 0, 1));
|
|
|
|
|
EXPECT_DOUBLE_EQ(camera.GetFOV(), 90.0*M_PI/180.0);
|
2023-04-30 11:47:40 +02:00
|
|
|
EXPECT_MAT_EQ(camera.GetR(), default_R);
|
|
|
|
|
EXPECT_MAT_EQ(camera.GetR_T(), default_R.transpose());
|
2023-04-30 10:52:23 +02:00
|
|
|
|
|
|
|
|
// Check target computation
|
|
|
|
|
Eigen::Vector3d target(1, 1, 1);
|
|
|
|
|
camera.SetTarget(target);
|
|
|
|
|
|
|
|
|
|
Eigen::Matrix3d target_R;
|
|
|
|
|
target_R << 0.7071067811865476, -0.4082482904638631, -0.5773502691896258,
|
|
|
|
|
-0.7071067811865476, -0.4082482904638631, -0.5773502691896258,
|
|
|
|
|
-0. , 0.8164965809277261, -0.5773502691896258;
|
|
|
|
|
|
|
|
|
|
EXPECT_VEC_EQ(camera.GetForward(), Eigen::Vector3d(0.57735027, 0.57735027, 0.57735027));
|
2023-04-30 11:47:40 +02:00
|
|
|
EXPECT_MAT_EQ(camera.GetR(), target_R);
|
2023-04-30 10:52:23 +02:00
|
|
|
|
|
|
|
|
// Set some values using the setters
|
|
|
|
|
Eigen::Vector3d fwd(1, 0, 0);
|
|
|
|
|
Eigen::Vector3d up(0, 1, 0);
|
|
|
|
|
Eigen::Matrix3d R;
|
|
|
|
|
R << 0, 0, 1,
|
|
|
|
|
1, 0, 0,
|
|
|
|
|
0, 1, 0;
|
|
|
|
|
double fov = M_PI / 3.0;
|
|
|
|
|
|
|
|
|
|
camera.SetForward(fwd)
|
|
|
|
|
.SetUp(up)
|
|
|
|
|
.SetR(R)
|
|
|
|
|
.SetFOV(fov);
|
|
|
|
|
|
|
|
|
|
// Check the values using the getters
|
|
|
|
|
EXPECT_EQ(camera.GetForward(), fwd);
|
|
|
|
|
EXPECT_EQ(camera.GetUp(), up);
|
|
|
|
|
EXPECT_EQ(camera.GetR(), R);
|
|
|
|
|
EXPECT_DOUBLE_EQ(camera.GetFOV(), fov);
|
2023-05-03 22:11:30 +02:00
|
|
|
}
|
|
|
|
|
{// Test point projections
|
|
|
|
|
std::cout << "Test point projections\n";
|
|
|
|
|
Camera camera;
|
2023-04-30 10:52:23 +02:00
|
|
|
camera.SetFOV(20*DEG2RAD);
|
2023-04-30 11:47:40 +02:00
|
|
|
camera.SetUp(Eigen::Vector3d(0, 0, 1));
|
2023-04-30 10:52:23 +02:00
|
|
|
camera.SetTarget(Eigen::Vector3d(1.,.5,.3));
|
2023-04-30 14:42:10 +02:00
|
|
|
|
2023-04-30 10:52:23 +02:00
|
|
|
EXPECT_VEC_EQ(camera.ComputeRayDirInCameraFrame(Eigen::Vector2d( 0.0, 0.0)), Eigen::Vector3d(0.0, 0.0, -1.0));
|
|
|
|
|
EXPECT_VEC_EQ(camera.ComputeRayDirInCameraFrame(Eigen::Vector2d( 1.0, 0.0)), Eigen::Vector3d(0.1736481776669304, 0, -0.9848077530122081));
|
|
|
|
|
EXPECT_VEC_EQ(camera.ComputeRayDirInCameraFrame(Eigen::Vector2d( 1.0, 1.0)), Eigen::Vector3d(0.1710878697460355, 0.1710878697460355, -0.970287525247814));
|
|
|
|
|
EXPECT_VEC_EQ(camera.ComputeRayDirInCameraFrame(Eigen::Vector2d( 1.0, -1.0)), Eigen::Vector3d(0.1710878697460355, -0.1710878697460355, -0.970287525247814));
|
|
|
|
|
EXPECT_VEC_EQ(camera.ComputeRayDirInCameraFrame(Eigen::Vector2d(-1.0, -1.0)), Eigen::Vector3d(-0.1710878697460355, -0.1710878697460355, -0.970287525247814));
|
|
|
|
|
|
2023-04-30 11:47:40 +02:00
|
|
|
EXPECT_VEC_EQ(camera.ComputeRayDirInInertialFrame(Eigen::Vector2d(0.0, 0.0)), Eigen::Vector3d(0.86386842558136, 0.43193421279068, 0.259160527674408));
|
|
|
|
|
EXPECT_VEC_EQ(camera.ComputeRayDirInInertialFrame(Eigen::Vector2d(1.0, 0.0)), Eigen::Vector3d(0.9284021489814165, 0.2700565097745997, 0.2552232969284919));
|
|
|
|
|
EXPECT_VEC_EQ(camera.ComputeRayDirInInertialFrame(Eigen::Vector2d(1.0, 1.0)), Eigen::Vector3d(0.8750553718495241, 0.2462456324858795, 0.416702753385336));
|
|
|
|
|
EXPECT_VEC_EQ(camera.ComputeRayDirInInertialFrame(Eigen::Vector2d(1.0, -1.0)), Eigen::Vector3d(0.9543717844957083, 0.2859038388089716, 0.08621770069290194));
|
|
|
|
|
EXPECT_VEC_EQ(camera.ComputeRayDirInInertialFrame(Eigen::Vector2d(-1.0, -1.0)), Eigen::Vector3d(0.8013461417446023, 0.5919551243111837, 0.08621770069290194));
|
|
|
|
|
|
|
|
|
|
EXPECT_VEC_EQ(camera.ProjectPointToSensorCameraFrame(camera.ComputeRayDirInCameraFrame(Eigen::Vector2d(0.0, 0.0))), Eigen::Vector3d(0.0, 0.0, -1.0/tan(camera.GetFOV()/2.0)));
|
|
|
|
|
EXPECT_VEC_EQ(camera.ProjectPointToSensorCameraFrame(camera.ComputeRayDirInCameraFrame(Eigen::Vector2d(1.0, 1.0))), Eigen::Vector3d(1.0, 1.0, -1.0/tan(camera.GetFOV()/2.0)));
|
|
|
|
|
EXPECT_VEC_EQ(camera.ProjectPointToSensorCameraFrame(camera.ComputeRayDirInCameraFrame(Eigen::Vector2d(-1.0, 0.5))), Eigen::Vector3d(-1.0, 0.5, -1.0/tan(camera.GetFOV()/2.0)));
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(camera.IsPointVisibleCameraFrame(Eigen::Vector3d( 0.0, 0.0, -1.0/tan(camera.GetFOV()/2.0))), true);
|
|
|
|
|
EXPECT_EQ(camera.IsPointVisibleCameraFrame(Eigen::Vector3d( 1.0, 0.0, -1.0/tan(camera.GetFOV()/2.0))), true);
|
|
|
|
|
EXPECT_EQ(camera.IsPointVisibleCameraFrame(Eigen::Vector3d( 1.0, 1.0, -1.0/tan(camera.GetFOV()/2.0))), true);
|
|
|
|
|
EXPECT_EQ(camera.IsPointVisibleCameraFrame(Eigen::Vector3d( 1.0, -1.0, -1.0/tan(camera.GetFOV()/2.0))), true);
|
|
|
|
|
EXPECT_EQ(camera.IsPointVisibleCameraFrame(Eigen::Vector3d(-1.0, -1.0, -1.0/tan(camera.GetFOV()/2.0))), true);
|
|
|
|
|
EXPECT_EQ(camera.IsPointVisibleCameraFrame(Eigen::Vector3d(-1.5, -0.5, -1.0/tan(camera.GetFOV()/2.0))), false);
|
|
|
|
|
EXPECT_EQ(camera.IsPointVisibleCameraFrame(Eigen::Vector3d( 1.5, -0.5, -1.0/tan(camera.GetFOV()/2.0))), false);
|
|
|
|
|
EXPECT_EQ(camera.IsPointVisibleCameraFrame(Eigen::Vector3d( 0.5, -1.5, -1.0/tan(camera.GetFOV()/2.0))), false);
|
|
|
|
|
EXPECT_EQ(camera.IsPointVisibleCameraFrame(Eigen::Vector3d( 0.5, 1.5, -1.0/tan(camera.GetFOV()/2.0))), false);
|
|
|
|
|
EXPECT_EQ(camera.IsPointVisibleCameraFrame(Eigen::Vector3d( 0.5, -0.5, +1.0/tan(camera.GetFOV()/2.0))), false);
|
2023-04-30 10:52:23 +02:00
|
|
|
|
|
|
|
|
// Check static methods
|
|
|
|
|
unsigned int width = 640, height = 480;
|
|
|
|
|
EXPECT_VEC_EQ(Camera::PixelToNormalizedCoordinates( 0, 0, width, height), Eigen::Vector2d(-1.0, 1.0));
|
|
|
|
|
EXPECT_VEC_EQ(Camera::PixelToNormalizedCoordinates(height-1, 0, width, height), Eigen::Vector2d(-1.0, -1.0));
|
|
|
|
|
EXPECT_VEC_EQ(Camera::PixelToNormalizedCoordinates(height-1, width-1, width, height), Eigen::Vector2d(1.0, -1.0));
|
|
|
|
|
EXPECT_VEC_EQ(Camera::PixelToNormalizedCoordinates( 0, width-1, width, height), Eigen::Vector2d(1.0, 1.0));
|
|
|
|
|
}
|
2023-05-03 22:11:30 +02:00
|
|
|
{// test Euler angles conversion
|
|
|
|
|
std::cout << "Euler angles\n";
|
|
|
|
|
PRINT_VAR(Camera().SetEulerAngles(0.0, 0.0, 0.0).GetR());
|
|
|
|
|
PRINT_VAR(Camera().SetEulerAngles(45.0, 0.0, 0.0).GetR());
|
|
|
|
|
PRINT_VAR(Camera().SetEulerAngles(45.0, 30.0, 0.0).GetR());
|
|
|
|
|
PRINT_VAR(Camera().SetEulerAngles(0.0, 0.0, 45.0).GetR());
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(Camera().SetEulerAngles(45.0, 0.0, 0.0).GetR(), Camera().SetEulerAngles(45.0*DEG2RAD, 0.0, 0.0, true).GetR());// input in radians vs degrees
|
|
|
|
|
}
|
2023-05-03 22:58:34 +02:00
|
|
|
{// Test Camera creation from CSV line
|
|
|
|
|
std::cout << "Creation from CSV line\n";
|
|
|
|
|
std::vector<double> yaw_pitch_roll_FOV = {45.0, 30.0, -15.0, 90.0};
|
|
|
|
|
Camera camera = Camera::FromYawPitchRollFOV(yaw_pitch_roll_FOV);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(camera.GetFOV(), yaw_pitch_roll_FOV[3]*DEG2RAD);
|
|
|
|
|
EXPECT_EQ(camera.GetR(), Camera().SetEulerAngles(yaw_pitch_roll_FOV[0], yaw_pitch_roll_FOV[1], yaw_pitch_roll_FOV[2]).GetR());
|
|
|
|
|
}
|
2023-04-30 10:52:23 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-01 00:31:24 +02:00
|
|
|
if(0){// Image
|
2023-04-30 14:42:10 +02:00
|
|
|
std::cout << "Image\n";
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Constructors\n";
|
|
|
|
|
Image image1(640, 480);
|
|
|
|
|
EXPECT_EQ(image1.GetWidth(), 640);
|
|
|
|
|
EXPECT_EQ(image1.GetHeight(), 480);
|
|
|
|
|
EXPECT_EQ(image1.GetDepth(), 1);
|
|
|
|
|
|
|
|
|
|
Image image2(1080, 720, 3);
|
|
|
|
|
EXPECT_EQ(image2.GetWidth(), 1080);
|
|
|
|
|
EXPECT_EQ(image2.GetHeight(), 720);
|
|
|
|
|
EXPECT_EQ(image2.GetDepth(), 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Pixel accessors\n";
|
|
|
|
|
Image image(20, 10, 3);
|
|
|
|
|
image.SetPixelValue(5, 7, 0, 69);
|
|
|
|
|
image.SetPixelValue(5, 7, 1, 100);
|
|
|
|
|
image.SetPixelValue(5, 7, 2, 42);
|
|
|
|
|
EXPECT_EQ(image.GetPixelValue(5, 7, 0), 69);
|
|
|
|
|
EXPECT_EQ(image.GetPixelValue(5, 7, 1), 100);
|
|
|
|
|
EXPECT_EQ(image.GetPixelValue(5, 7, 2), 42);
|
|
|
|
|
|
|
|
|
|
// Test reference accessor
|
|
|
|
|
image.PixelValue(5, 7, 0) = 13;
|
|
|
|
|
image.PixelValue(5, 7, 1) = 37;
|
|
|
|
|
image.PixelValue(5, 7, 2) = 74;
|
|
|
|
|
EXPECT_EQ(image.PixelValue(5, 7, 0), 13);
|
|
|
|
|
EXPECT_EQ(image.PixelValue(5, 7, 1), 37);
|
|
|
|
|
EXPECT_EQ(image.PixelValue(5, 7, 2), 74);
|
|
|
|
|
|
|
|
|
|
// Test Vector accessors
|
|
|
|
|
EXPECT_EQ(image.GetPixel(5, 7), Eigen::Vector3i(13, 37, 74));
|
|
|
|
|
EXPECT_EQ(image.GetPixel(Eigen::Vector2i(5, 7)), Eigen::Vector3i(13, 37, 74));
|
|
|
|
|
|
|
|
|
|
image.SetPixel(5, 7, Eigen::Vector3i(1, 2, 3));
|
|
|
|
|
EXPECT_EQ(image.GetPixel(5, 7), Eigen::Vector3i(1, 2, 3));
|
|
|
|
|
|
|
|
|
|
image.SetPixel(Eigen::Vector2i(6, 8), Eigen::Vector3i(3, 2, 1));
|
|
|
|
|
EXPECT_EQ(image.GetPixel(Eigen::Vector2i(6, 8)), Eigen::Vector3i(3, 2, 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Test Fill(rgb)\n";
|
|
|
|
|
Image image(3, 2, 3);
|
|
|
|
|
image.Fill(Eigen::Vector3i(1, 2, 3));
|
2023-04-30 21:43:28 +02:00
|
|
|
for (int i = 0; i < image.GetHeight(); ++i) {
|
|
|
|
|
for (int j = 0; j < image.GetWidth(); ++j) {
|
|
|
|
|
EXPECT_EQ(image.GetPixel(i, j), Eigen::Vector3i(1, 2, 3));
|
2023-04-30 14:42:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Test Fill(uchar)\n";
|
|
|
|
|
Image image(3, 2, 3);
|
|
|
|
|
image.Fill(69);
|
2023-04-30 21:43:28 +02:00
|
|
|
for (int i = 0; i < image.GetHeight(); ++i) {
|
|
|
|
|
for (int j = 0; j < image.GetWidth(); ++j) {
|
|
|
|
|
EXPECT_EQ(image.GetPixel(i, j), Eigen::Vector3i(69, 69, 69));
|
2023-04-30 14:42:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Image loading\n";
|
|
|
|
|
Image image("../test_img_4x3.png");
|
|
|
|
|
EXPECT_EQ(image.GetWidth(), 4);
|
|
|
|
|
EXPECT_EQ(image.GetHeight(), 3);
|
|
|
|
|
EXPECT_EQ(image.GetDepth(), 3);
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < image.GetHeight(); ++i) {
|
|
|
|
|
for(int j = 0; j < image.GetWidth(); ++j) {
|
|
|
|
|
auto pixel = image.GetPixel(i, j);
|
|
|
|
|
std::cout << i << " " << j << " (" << pixel(0)/255.0 << ", " << pixel(1)/255.0 << ", " << pixel(2)/255.0 << ")\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(int i = 0 ; i < image.GetWidth()*image.GetHeight()*image.GetDepth() ; i++)
|
|
|
|
|
std::cout << i << "\t" << (int)(image[i]) << "\t" << (image[i]/255.0) << "\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Image saving\n";
|
|
|
|
|
Image image(512, 256, 3);
|
|
|
|
|
for(int i = 0; i < image.GetHeight(); ++i) {
|
|
|
|
|
for(int j = 0; j < image.GetWidth(); ++j) {
|
|
|
|
|
image.SetPixel(i, j, Eigen::Vector3i(127, i%256, j%256));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
image.Save("test_img_512x256.png");
|
|
|
|
|
image.Save("test_img_512x256.jpg");
|
|
|
|
|
image.Save("test_img_512x256.bmp");
|
|
|
|
|
image.Save("test_img_512x256.tga");
|
|
|
|
|
}
|
2023-04-30 21:43:28 +02:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Bilinear interpolation\n";
|
|
|
|
|
Image image1(3, 3, 3); // Source image
|
|
|
|
|
Image image2(512, 512, 3); // Interpolated image (nearest neighbor)
|
|
|
|
|
Image image3(512, 512, 3); // Interpolated image (bilinear)
|
|
|
|
|
image1.SetPixel(0, 0, Eigen::Vector3i(255, 127, 0));
|
|
|
|
|
image1.SetPixel(0, 1, Eigen::Vector3i(127, 192, 64));
|
|
|
|
|
image1.SetPixel(0, 2, Eigen::Vector3i(100, 100, 50));
|
|
|
|
|
image1.SetPixel(1, 0, Eigen::Vector3i(30, 0, 210));
|
|
|
|
|
image1.SetPixel(1, 1, Eigen::Vector3i(192, 64, 0));
|
|
|
|
|
image1.SetPixel(1, 2, Eigen::Vector3i(64, 150, 192));
|
|
|
|
|
image1.SetPixel(2, 0, Eigen::Vector3i(100, 50, 150));
|
|
|
|
|
image1.SetPixel(2, 1, Eigen::Vector3i(50, 120, 190));
|
|
|
|
|
image1.SetPixel(2, 2, Eigen::Vector3i(240, 50, 15));
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < image2.GetHeight(); ++i) {
|
|
|
|
|
for(int j = 0; j < image2.GetWidth(); ++j) {
|
|
|
|
|
Eigen::Vector2d pos_float((double)i/image2.GetHeight()*(image1.GetHeight()-1), (double)j/image2.GetWidth()*(image1.GetWidth()-1));
|
|
|
|
|
image2.SetPixel(i, j, image1.GetPixelInterp(pos_float, Image::InterpMethod::NEAREST));
|
|
|
|
|
image3.SetPixel(i, j, image1.GetPixelInterp(pos_float, Image::InterpMethod::BILINEAR));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
image1.Save("interp_src_3x3.png");
|
|
|
|
|
image2.Save("interp_nearest_512x512.png");
|
|
|
|
|
image3.Save("interp_bilinear_512x512.png");
|
|
|
|
|
}
|
2023-04-30 23:12:09 +02:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Grayscale conversion\n";
|
|
|
|
|
Image image("../venise.jpg");
|
|
|
|
|
Image image_gray = image.Grayscale();
|
|
|
|
|
Image image_luma = image.LumaREC709();
|
|
|
|
|
image_gray.Save("venise_gray.jpg");
|
|
|
|
|
image_luma.Save("venise_luma.jpg");
|
2023-04-30 23:40:07 +02:00
|
|
|
|
|
|
|
|
std::cout << "Colormap application\n";
|
2023-05-01 00:31:24 +02:00
|
|
|
Image image_parula = image_luma.Colorized(Image::Colormap::PARULA);
|
2023-04-30 23:40:07 +02:00
|
|
|
image_parula.Save("venise_parula.jpg");
|
2023-04-30 23:12:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Image normalization\n";
|
|
|
|
|
Image image("../venise.jpg");
|
|
|
|
|
image.HistogramNormalize(5);
|
|
|
|
|
image.Save("venise_normalized.jpg");
|
|
|
|
|
}
|
2023-04-30 14:42:10 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-03 22:11:30 +02:00
|
|
|
if(0){// Test Slicer360ToPerspective
|
2023-05-01 18:16:13 +02:00
|
|
|
if(0) { std::cout << "Coverage analysis\n";
|
|
|
|
|
Slicer360ToPerspective slicer;
|
|
|
|
|
|
|
|
|
|
double fov = 90.0*DEG2RAD;
|
|
|
|
|
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., -1., -1.)));
|
|
|
|
|
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_col = slicer.ComputeCoverage(width, false);
|
2023-05-01 00:31:24 +02:00
|
|
|
|
2023-05-01 18:16:13 +02:00
|
|
|
coverage_raw.Save("coverage_raw.png");
|
|
|
|
|
coverage_col.Save("coverage_col.png");
|
|
|
|
|
}
|
2023-05-01 00:31:24 +02:00
|
|
|
|
2023-05-01 18:16:13 +02:00
|
|
|
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";
|
|
|
|
|
}
|
2023-05-01 00:31:24 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-03 22:58:34 +02:00
|
|
|
if(1){// CSV parser
|
|
|
|
|
std::cout << "CSV parser\n";
|
|
|
|
|
{ std::cout << "Valid CSV file\n";
|
|
|
|
|
auto data = parse_csv_file("../data_test_valid.csv");
|
|
|
|
|
EXPECT_EQ(data.empty(), false);
|
|
|
|
|
for(auto& row : data) {
|
|
|
|
|
for(auto& cell : row) {
|
|
|
|
|
std::cout << cell << "\t";
|
|
|
|
|
}
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
{ std::cout << "Invalid CSV file (missing column on one row)\n";
|
|
|
|
|
auto data = parse_csv_file("../data_test_invalid.csv");
|
|
|
|
|
EXPECT_EQ(data.empty(), true);
|
|
|
|
|
}
|
|
|
|
|
{ std::cout << "Invalid CSV file (bad floating-point number)\n";
|
|
|
|
|
auto data = parse_csv_file("../data_test_invalid_floating_point.csv");
|
|
|
|
|
EXPECT_EQ(data.empty(), true);
|
|
|
|
|
}
|
|
|
|
|
{ std::cout << "Non-existent CSV file\n";
|
|
|
|
|
auto data = parse_csv_file("does_not_exist.csv");
|
|
|
|
|
EXPECT_EQ(data.empty(), true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-30 10:52:23 +02:00
|
|
|
PRINT_TESTS_SUMMARY();
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|