Initial C++ version. Camera partially implemented.
This commit is contained in:
parent
269d3c2fe6
commit
f1a2c4860f
8 changed files with 726 additions and 0 deletions
26
cpp/Makefile
Normal file
26
cpp/Makefile
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
CXX = g++
|
||||
CXXFLAGS = -std=c++17 -Wall -Wextra -pedantic -I./include -g
|
||||
INCLUDES = -I./include -ID:/Users/Jerome/Documents/Ingenierie/Programmation/eigen-3.4.0
|
||||
|
||||
SRC_DIR = src
|
||||
OBJ_DIR = obj
|
||||
|
||||
SRCS = $(wildcard $(SRC_DIR)/*.cpp)
|
||||
OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(SRCS))
|
||||
EXEC = $(OBJ_DIR)/360_to_perspective
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(EXEC)
|
||||
|
||||
$(EXEC): $(OBJS)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||
|
||||
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp | $(OBJ_DIR)
|
||||
$(CXX) $(CXXFLAGS) $(INCLUDES) -c -o $@ $<
|
||||
|
||||
$(OBJ_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJ_DIR)
|
||||
77
cpp/include/Camera.hpp
Normal file
77
cpp/include/Camera.hpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#ifndef H_Camera
|
||||
#define H_Camera
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#define DEG2RAD 0.017453292519943295
|
||||
|
||||
/// @brief A camera class to perform frame transformations and projections.
|
||||
class Camera {
|
||||
public:
|
||||
// Constructors
|
||||
Camera();
|
||||
|
||||
// Accessors
|
||||
Eigen::Vector3d const& GetForward() const;
|
||||
Eigen::Vector3d const& GetUp() const;
|
||||
Eigen::Matrix3d const& GetR() const;
|
||||
Eigen::Matrix3d const& GetR_T() const;
|
||||
double GetFOV() const;
|
||||
|
||||
// Setters following the Builder pattern. See https://en.wikipedia.org/wiki/Builder_pattern
|
||||
Camera & SetForward(Eigen::Vector3d const& fwd_);
|
||||
Camera & SetUp(Eigen::Vector3d const& up_);
|
||||
Camera & SetR(Eigen::Matrix3d const& R_);
|
||||
Camera & SetFOV(double fov);
|
||||
|
||||
// Methods
|
||||
/// @brief Build the rotation matrix from the camera frame to the inertial frame from the fwd and up vectors.
|
||||
Camera & BuildRotationMatrix();
|
||||
|
||||
/// @brief Orients the camera so that the forward vector points at the target. The position of the camera is (0,0,0).
|
||||
Camera & SetTarget(Eigen::Vector3d const& target);
|
||||
|
||||
/// @brief Computes the ray direction in camera frame for a point (x,y) on the surface of the camera's sensor. p_norm is in normalized coordinates [-1;1].
|
||||
/// @param p_norm Normalized coordinates of the point on the sensor.
|
||||
/// @return The ray direction in camera frame, cartesian coordinates.
|
||||
Eigen::Vector3d ComputeRayDirInCameraFrame(Eigen::Vector2d const& p_norm) const;
|
||||
|
||||
/// @brief Computes the ray direction in inertial frame for a point (x,y) on the surface of the camera's sensor. p_norm is in normalized coordinates [-1;1].
|
||||
/// @param p_norm Normalized coordinates of the point on the sensor.
|
||||
/// @return The ray direction in inertial frame, cartesian coordinates.
|
||||
Eigen::Vector3d ComputeRayDirInInertialFrame(Eigen::Vector2d const& p_norm) const;
|
||||
|
||||
/// @brief Projects a point in camera frame (cartesian coordinates) to the sensor.
|
||||
/// @param p_inertial Cartesian coordinates of the point to project, in camera frame.
|
||||
/// @return Sensor coordinates of the projected point. The Z component is +/- tan(FOV/2). Negative Z means the point is in front of the camera.
|
||||
Eigen::Vector3d ProjectPointToSensorCameraFrame(Eigen::Vector3d const& p_camera) const;
|
||||
|
||||
/// @brief Projects a point in inertial frame (cartesian coordinates) to the sensor.
|
||||
/// @param p_inertial Cartesian coordinates of the point to project, in inertial frame.
|
||||
/// @return Sensor coordinates of the projected point. The Z component is +/- tan(FOV/2). Negative Z means the point is in front of the camera.
|
||||
Eigen::Vector3d ProjectPointToSensorInertialFrame(Eigen::Vector3d const& p_inertial) const;
|
||||
|
||||
/// @brief Checks if a point in camera frame is visible by the camera.
|
||||
/// @param p_inertial Cartesian coordinates of the point to check, in camera frame.
|
||||
/// @return True if the point is visible, false otherwise.
|
||||
bool IsPointVisibleCameraFrame(Eigen::Vector3d const& p_camera) const;
|
||||
|
||||
/// @brief Checks if a point in inertial frame is visible by the camera.
|
||||
/// @param p_inertial Cartesian coordinates of the point to check, in inertial frame.
|
||||
/// @return True if the point is visible, false otherwise.
|
||||
bool IsPointVisibleInertialFrame(Eigen::Vector3d const& p_inertial) const;
|
||||
|
||||
// Static methods
|
||||
/// @brief Converts pixel coordinates to normalized coordinates. i is the row (y), j is the column (x). Normalized coordinates range from -1 to 1.
|
||||
static Eigen::Vector2d PixelToNormalizedCoordinates(unsigned int i, unsigned int j, unsigned int width, unsigned int height);
|
||||
|
||||
private:
|
||||
Eigen::Vector3d fwd; //!< Forward vector in inertial frame
|
||||
Eigen::Vector3d up; //!< Up vector in inertial frame
|
||||
Eigen::Matrix3d R; //!< Rotation matrix from camera frame to inertial frame
|
||||
Eigen::Matrix3d R_T; //!< Rotation matrix from inertial frame to camera frame
|
||||
double FOV; //!< Field of view in radians
|
||||
double tanFOV2; //!< tan(FOV/2)
|
||||
};
|
||||
|
||||
#endif
|
||||
35
cpp/include/frame_conversions.hpp
Normal file
35
cpp/include/frame_conversions.hpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef H_frame_conversions
|
||||
#define H_frame_conversions
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include <cmath>
|
||||
|
||||
/// @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);
|
||||
|
||||
/// @brief Converts a cartesian vector to a spherical vector [r, theta, phi] in ISO convention (theta=0 is Z+).
|
||||
Eigen::Vector3d cart2sph(const Eigen::Vector3d & cart);
|
||||
|
||||
/// @brief Converts equirectangular coordinates to spherical coordinates.
|
||||
/// @param i Row in the equirectangular image.
|
||||
/// @param j Column in the equirectangular image.
|
||||
/// @param width Width 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+).
|
||||
Eigen::Vector3d equirectangular2sph(unsigned int i, unsigned int j, unsigned int width, unsigned int height);
|
||||
|
||||
/// @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 width Width of the equirectangular image.
|
||||
/// @param height Height of the equirectangular image.
|
||||
/// @return A 2D vector of floating point numbers [i, j] representing the pixel coordinates of the equirectangular image.
|
||||
Eigen::Vector2d sph2equirectangular_d(Eigen::Vector3d const& p_sph, unsigned int width, unsigned int height);
|
||||
|
||||
/// @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 width Width of the equirectangular image.
|
||||
/// @param height Height of the equirectangular image.
|
||||
/// @return A 2D vector of integers [i, j] representing the pixel coordinates of the equirectangular image.
|
||||
Eigen::Vector2i sph2equirectangular_i(Eigen::Vector3d const& p_sph, unsigned int width, unsigned int height);
|
||||
|
||||
#endif
|
||||
146
cpp/include/tinyimage.h
Normal file
146
cpp/include/tinyimage.h
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
TinyImage
|
||||
A tiny C/C++ image loading and saving library for graphics APIs.
|
||||
Author: Charles Dong
|
||||
*/
|
||||
|
||||
#ifndef TINYIMAGE_H_
|
||||
#define TINYIMAGE_H_
|
||||
|
||||
enum TinyImgColorType
|
||||
{
|
||||
TINYIMG_RGB,
|
||||
TINYIMG_RGBA
|
||||
};
|
||||
|
||||
enum TinyImgError
|
||||
{
|
||||
TINYIMG_OK,
|
||||
TINYIMG_FORMAT_UNSUPPORTED,
|
||||
TINYIMG_FILE_NOT_FOUND,
|
||||
TINYIMG_INVALID_ARGUMENT
|
||||
};
|
||||
|
||||
/* C-style interface begin */
|
||||
|
||||
/*
|
||||
@brief Loads an image from disk.
|
||||
|
||||
@param[filename] The name of the image file.
|
||||
@param[width] Pointer to an integer that stores the image width (should be not NULL).
|
||||
@param[height] Pointer to an integer that stores the image height (should be not NULL).
|
||||
@param[type] The color type of the final image data.
|
||||
|
||||
@return The image data in unsigned chars. If an error occurs, then NULL will be returned.
|
||||
*/
|
||||
unsigned char * tinyimg_load(const char * filename, int * width, int * height, TinyImgColorType type);
|
||||
|
||||
/*
|
||||
@brief Frees the image data.
|
||||
|
||||
@param[image] The image data.
|
||||
*/
|
||||
void tinyimg_free(unsigned char * image);
|
||||
|
||||
/*
|
||||
@brief Saves the given image to disk.
|
||||
|
||||
@param[filename] The name of the image file.
|
||||
@param[width] The image width.
|
||||
@param[height] The image height.
|
||||
@param[type] The color type of the image data.
|
||||
@param[image] The image data.
|
||||
|
||||
@return 1 if successful, 0 if fails.
|
||||
*/
|
||||
int tinyimg_save(const char * filename, int width, int height, TinyImgColorType type, const unsigned char * image);
|
||||
|
||||
/*
|
||||
@brief Gets the error code of the last error.
|
||||
|
||||
@return The error code. If no error occured, 0 will be returned.
|
||||
*/
|
||||
TinyImgError tinyimg_get_error();
|
||||
|
||||
/*
|
||||
@brief Gets the description of the last error.
|
||||
|
||||
@return The error string. If no error occured, NULL will be returned.
|
||||
*/
|
||||
const char * tinyimg_get_error_str();
|
||||
|
||||
/* C-style interface end */
|
||||
|
||||
/* C++ style interface begin */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#define TINYIMG_BEGIN namespace tinyimg {
|
||||
#define TINYIMG_END }
|
||||
|
||||
#include <vector>
|
||||
|
||||
TINYIMG_BEGIN
|
||||
|
||||
/*
|
||||
@brief Loads an image from disk.
|
||||
|
||||
@param[filename] The name of the image file.
|
||||
@param[width] The image width.
|
||||
@param[height] The image height.
|
||||
@param[type] The color type of the final image data.
|
||||
|
||||
@return The image data in unsigned chars. If an error occurs, then nullptr will be returned.
|
||||
*/
|
||||
unsigned char * load(const char * filename, int & width, int & height, TinyImgColorType type);
|
||||
|
||||
/*
|
||||
@brief Frees the image data.
|
||||
|
||||
@param[image] The image data.
|
||||
*/
|
||||
void free(unsigned char * image);
|
||||
/*
|
||||
@brief Loads an image from disk into a vector.
|
||||
|
||||
@param[filename] The name of the image file.
|
||||
@param[width] The image width.
|
||||
@param[height] The image height.
|
||||
@param[type] The color type of the final image data.
|
||||
|
||||
@return The image data in a vector. If an error occurs, an empty vector will be returned.
|
||||
*/
|
||||
std::vector<unsigned char> load_vec(const char * filename, int & width, int & height, TinyImgColorType type);
|
||||
|
||||
/*
|
||||
@brief Saves the given image to disk.
|
||||
|
||||
@param[filename] The name of the image file.
|
||||
@param[width] The image width.
|
||||
@param[height] The image height.
|
||||
@param[type] The color type of the image data.
|
||||
@param[image] The image data.
|
||||
|
||||
@return true if successful, false if fails.
|
||||
*/
|
||||
bool save(const char * filename, int width, int height, TinyImgColorType type, const unsigned char * image);
|
||||
|
||||
/*
|
||||
@brief Gets the error code of the last error.
|
||||
|
||||
@return The error code. If no error occured, 0 will be returned.
|
||||
*/
|
||||
TinyImgError get_error();
|
||||
|
||||
/*
|
||||
@brief Gets the description of the last error.
|
||||
|
||||
@return The error string. If no error occured, nullptr will be returned.
|
||||
*/
|
||||
const char * get_error_str();
|
||||
|
||||
TINYIMG_END
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* TINYIMAGE_H_ */
|
||||
104
cpp/src/Camera.cpp
Normal file
104
cpp/src/Camera.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#include <Camera.hpp>
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
|
||||
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))
|
||||
{
|
||||
BuildRotationMatrix();
|
||||
}
|
||||
|
||||
// Getters
|
||||
const Eigen::Vector3d& Camera::GetForward() const {
|
||||
return fwd;
|
||||
}
|
||||
|
||||
const Eigen::Vector3d& Camera::GetUp() const {
|
||||
return up;
|
||||
}
|
||||
|
||||
const Eigen::Matrix3d& Camera::GetR() const {
|
||||
return R;
|
||||
}
|
||||
|
||||
const Eigen::Matrix3d& Camera::GetR_T() const {
|
||||
return R_T;
|
||||
}
|
||||
|
||||
double Camera::GetFOV() const {
|
||||
return FOV;
|
||||
}
|
||||
|
||||
// Setters
|
||||
Camera& Camera::SetForward(const Eigen::Vector3d& fwd_) {
|
||||
fwd = fwd_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Camera& Camera::SetUp(const Eigen::Vector3d& up_) {
|
||||
up = up_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Camera& Camera::SetR(const Eigen::Matrix3d& R_) {
|
||||
R = R_;
|
||||
R_T = R.transpose();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Camera& Camera::SetFOV(double fov) {
|
||||
FOV = fov;
|
||||
tanFOV2 = tan(FOV/2.0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Camera & Camera::BuildRotationMatrix() {
|
||||
Eigen::Vector3d left = up.cross(fwd);
|
||||
left = left/left.norm();
|
||||
Eigen::Vector3d cam_up = fwd.cross(left);
|
||||
R.col(0) = -left; // x-axis is to the right
|
||||
R.col(1) = cam_up; // y-axis is up
|
||||
R.col(2) = -fwd; // z-axis is backwards
|
||||
R_T = R.transpose();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Camera & Camera::SetTarget(Eigen::Vector3d const& target) {
|
||||
fwd = target/target.norm();
|
||||
BuildRotationMatrix();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Eigen::Vector3d Camera::ComputeRayDirInCameraFrame(Eigen::Vector2d const& p_norm) const {
|
||||
return Eigen::Vector3d(p_norm(0)*tanFOV2, p_norm(1)*tanFOV2, -1.0).normalized();
|
||||
}
|
||||
|
||||
Eigen::Vector3d Camera::ComputeRayDirInInertialFrame(Eigen::Vector2d const& p_norm) const {
|
||||
return R*ComputeRayDirInCameraFrame(p_norm);
|
||||
}
|
||||
|
||||
Eigen::Vector3d Camera::ProjectPointToSensorCameraFrame(Eigen::Vector3d const& p_camera) const {
|
||||
Eigen::Vector3d p_sensor = p_camera;
|
||||
if(p_camera[2] != 0.0)
|
||||
p_sensor *= tanFOV2/std::abs(p_camera[2]);
|
||||
return p_sensor;
|
||||
}
|
||||
|
||||
Eigen::Vector3d Camera::ProjectPointToSensorInertialFrame(Eigen::Vector3d const& p_inertial) const {
|
||||
return ProjectPointToSensorCameraFrame(R_T*p_inertial);
|
||||
}
|
||||
|
||||
bool Camera::IsPointVisibleCameraFrame(Eigen::Vector3d const& p_camera) const {
|
||||
Eigen::Vector3d p_sensor = ProjectPointToSensorCameraFrame(p_camera);
|
||||
return (p_sensor[0] >= -1.0) && (p_sensor[0] <= 1.0) && (p_sensor[1] >= -1.0) && (p_sensor[1] <= 1.0) && (p_sensor[2] < 0.0);
|
||||
}
|
||||
|
||||
bool Camera::IsPointVisibleInertialFrame(Eigen::Vector3d const& p_inertial) const {
|
||||
Eigen::Vector3d p_sensor = ProjectPointToSensorInertialFrame(p_inertial);
|
||||
return (p_sensor[0] >= -1.0) && (p_sensor[0] <= 1.0) && (p_sensor[1] >= -1.0) && (p_sensor[1] <= 1.0) && (p_sensor[2] < 0.0);
|
||||
}
|
||||
|
||||
Eigen::Vector2d Camera::PixelToNormalizedCoordinates(unsigned int i, unsigned int j, unsigned int width, unsigned int height) {
|
||||
return Eigen::Vector2d(2.0*((double)j)/(width - 1) - 1.0, -2.0*((double)i)/(height - 1) + 1.0);
|
||||
}
|
||||
39
cpp/src/frame_conversions.cpp
Normal file
39
cpp/src/frame_conversions.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#include <frame_conversions.hpp>
|
||||
|
||||
#define M_PI 3.14159265358979323846
|
||||
|
||||
Eigen::Vector3d sph2cart(const Eigen::Vector3d & sph) {
|
||||
double r = sph(0);
|
||||
double theta = sph(1);
|
||||
double phi = sph(2);
|
||||
double x = r*sin(theta)*cos(phi);
|
||||
double y = r*sin(theta)*sin(phi);
|
||||
double z = r*cos(theta);
|
||||
return Eigen::Vector3d(x,y,z);
|
||||
}
|
||||
|
||||
Eigen::Vector3d cart2sph(const Eigen::Vector3d & cart) {
|
||||
double x = cart(0);
|
||||
double y = cart(1);
|
||||
double z = cart(2);
|
||||
double r = sqrt(x*x + y*y + z*z);
|
||||
double theta = acos(z/r);
|
||||
double phi = atan2(y,x);
|
||||
return Eigen::Vector3d(r,theta,phi);
|
||||
}
|
||||
|
||||
Eigen::Vector3d equirectangular2sph(unsigned int i, unsigned 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));
|
||||
}
|
||||
|
||||
Eigen::Vector2d sph2equirectangular_d(Eigen::Vector3d const& p_sph, unsigned int width, unsigned int height) {
|
||||
double theta = p_sph[1];
|
||||
double phi = p_sph[2];
|
||||
double i = std::fmod(((height - 1)*theta)/M_PI, height);
|
||||
double j = std::fmod((width*(M_PI + phi))/(2.0*M_PI), width);
|
||||
return Eigen::Vector2d(i, j);
|
||||
}
|
||||
|
||||
Eigen::Vector2i sph2equirectangular_i(Eigen::Vector3d const& p_sph, unsigned int width, unsigned int height) {
|
||||
return sph2equirectangular_d(p_sph, width, height).cast<int>();
|
||||
}
|
||||
146
cpp/src/tests.cpp
Normal file
146
cpp/src/tests.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#include <iostream>
|
||||
#include <Camera.hpp>
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#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 { \
|
||||
auto&& d = (actual) - (expected); \
|
||||
EXPECT_DOUBLE_EQ_IMPL(d.array().abs().maxCoeff(), 0.0, 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() {
|
||||
std::cout.precision(16);
|
||||
std::cout << "Running Unit Tests\n";
|
||||
|
||||
{// Camera
|
||||
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);
|
||||
EXPECT_EQ(camera.GetR(), default_R);
|
||||
EXPECT_EQ(camera.GetR_T(), default_R.transpose());
|
||||
|
||||
// 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));
|
||||
EXPECT_VEC_EQ(camera.GetR(), target_R);
|
||||
|
||||
// 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);
|
||||
|
||||
// Test point projections
|
||||
camera.SetFOV(20*DEG2RAD);
|
||||
camera.SetTarget(Eigen::Vector3d(1.,.5,.3));
|
||||
PRINT_VAR(camera.GetR());
|
||||
PRINT_VAR(camera.GetR_T());
|
||||
|
||||
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));
|
||||
|
||||
std::cout << (camera.ComputeRayDirInInertialFrame(Eigen::Vector2d(0.0, 0.0)).transpose()) << std::endl;
|
||||
std::cout << (camera.ComputeRayDirInInertialFrame(Eigen::Vector2d(1.0, 0.0)).transpose()) << std::endl;
|
||||
std::cout << (camera.ComputeRayDirInInertialFrame(Eigen::Vector2d(1.0, 1.0)).transpose()) << std::endl;
|
||||
std::cout << (camera.ComputeRayDirInInertialFrame(Eigen::Vector2d(1.0, -1.0)).transpose()) << std::endl;
|
||||
std::cout << (camera.ComputeRayDirInInertialFrame(Eigen::Vector2d(-1.0, -1.0)).transpose()) << std::endl;
|
||||
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_TESTS_SUMMARY();
|
||||
|
||||
return 0;
|
||||
}
|
||||
153
cpp/src/tinyimage.cpp
Normal file
153
cpp/src/tinyimage.cpp
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/* Source file for TinyImage */
|
||||
|
||||
/* Prevent error C4996 in Visual Studio compilers */
|
||||
#ifdef _MSC_VER
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "tinyimage.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h> // for pow()
|
||||
|
||||
/* The error code */
|
||||
TinyImgError _error;
|
||||
|
||||
/* Internal functions */
|
||||
|
||||
int _equal(const char * str1, const char * str2);
|
||||
int _tinyimg_check_format(const char * extension);
|
||||
int _tinyimg_open_file(FILE ** fp, const char * filename);
|
||||
unsigned char * _tinyimg_load(FILE * fp, const char * extension, int * width, int * height);
|
||||
unsigned char * _tinyimg_load_bmp(FILE * fp, int * width, int * height);
|
||||
|
||||
int _equal(const char * str1, const char * str2)
|
||||
{
|
||||
return strcmp(str1, str2) == 0;
|
||||
}
|
||||
|
||||
int _tinyimg_check_format(const char * extension)
|
||||
{
|
||||
return extension != NULL && (_equal(extension, ".bmp"));
|
||||
}
|
||||
|
||||
int _tinyimg_open_file(FILE ** fp, const char * filename)
|
||||
{
|
||||
*fp = fopen(filename, "rb");
|
||||
return *fp != NULL;
|
||||
}
|
||||
|
||||
unsigned char * _tinyimg_load(FILE * fp, const char * extension, int * width, int * height)
|
||||
{
|
||||
if (_equal(extension, ".bmp"))
|
||||
return _tinyimg_load_bmp(fp, width, height);
|
||||
}
|
||||
|
||||
unsigned char * _tinyimg_load_bmp(FILE * fp, int * width, int * height)
|
||||
{
|
||||
unsigned char * image; // image data
|
||||
unsigned char * palette = NULL; // palette data (if uses palette)
|
||||
int width_file; // pixels per line in BMP (note that BMP has 4-pixel alignment for each line)
|
||||
int size; // pixel count
|
||||
short bit_count; // bit count per pixel (24 if no palette)
|
||||
int color_count = 0; // colors used (if uses palette)
|
||||
unsigned char temp;
|
||||
int count = 0;
|
||||
|
||||
/* Checks if the pointers are not NULL */
|
||||
if (width == NULL || height == NULL)
|
||||
{
|
||||
_error = TINYIMG_INVALID_ARGUMENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(fp, 18, SEEK_SET);
|
||||
fread(width, sizeof(int), 1, fp); // read width
|
||||
fread(height, sizeof(int), 1, fp); // read height
|
||||
|
||||
fseek(fp, 28, SEEK_SET);
|
||||
fread(&bit_count, sizeof(short), 1, fp); // read bit count
|
||||
|
||||
fseek(fp, 54, SEEK_SET);
|
||||
/* If uses palette (bit count is not 24) */
|
||||
if (bit_count != 24)
|
||||
{
|
||||
color_count = (int)pow(2.0, bit_count);
|
||||
palette = (unsigned char *)malloc(sizeof(unsigned char) * 4 * color_count);
|
||||
fread(palette, sizeof(unsigned char), 4 * color_count, fp);
|
||||
}
|
||||
|
||||
/* Allocates memory for the image */
|
||||
width_file = ((*width) + 3) / 4 * 4; // Increase the width to make sure it's multiple of 4
|
||||
size = width_file * (*height);
|
||||
image = (unsigned char *)malloc(sizeof(unsigned char) * size * 3);
|
||||
|
||||
/* Reads the image data */
|
||||
if (bit_count == 24)
|
||||
fread(image, sizeof(unsigned char), size * 3, fp);
|
||||
else
|
||||
{
|
||||
while (count < size)
|
||||
{
|
||||
fread(&temp, sizeof(unsigned char), 1, fp);
|
||||
image[count * 3] = palette[temp * 4];
|
||||
image[count * 3 + 1] = palette[temp * 4 + 1];
|
||||
image[count * 3 + 2] = palette[temp * 4 + 2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return image;
|
||||
}
|
||||
|
||||
/* External functions */
|
||||
|
||||
unsigned char * tinyimg_load(const char * filename, int * width, int * height, TinyImgColorType type)
|
||||
{
|
||||
const char * extension;
|
||||
FILE * fp;
|
||||
|
||||
/* Check if the image format is supported */
|
||||
extension = strrchr(filename, '.');
|
||||
if (!_tinyimg_check_format(extension))
|
||||
{
|
||||
_error = TINYIMG_FORMAT_UNSUPPORTED;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the file exists */
|
||||
if (!_tinyimg_open_file(&fp, filename))
|
||||
{
|
||||
_error = TINYIMG_FILE_NOT_FOUND;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return _tinyimg_load(fp, extension, width, height);
|
||||
}
|
||||
|
||||
void tinyimg_free(unsigned char * image)
|
||||
{
|
||||
free(image);
|
||||
}
|
||||
|
||||
TinyImgError tinyimg_get_error()
|
||||
{
|
||||
return _error;
|
||||
}
|
||||
|
||||
const char * tinyimg_get_error_str()
|
||||
{
|
||||
switch (_error)
|
||||
{
|
||||
case TINYIMG_OK:
|
||||
return NULL;
|
||||
case TINYIMG_FORMAT_UNSUPPORTED:
|
||||
return "format unsupported";
|
||||
case TINYIMG_FILE_NOT_FOUND:
|
||||
return "file not found";
|
||||
case TINYIMG_INVALID_ARGUMENT:
|
||||
return "invalid argument";
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue