Version 1.0 : output folder is created automatically, everything is logged properly.
This commit is contained in:
parent
d550bc7f83
commit
03f635cae4
6 changed files with 84 additions and 25 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
CXX = g++
|
CXX = g++
|
||||||
CXXFLAGS = -std=c++17 -Wall -Wextra -pedantic -g
|
CXXFLAGS = -std=c++17 -Wall -Wextra -pedantic -O3
|
||||||
INCLUDES = -I./include -ID:/Users/Jerome/Documents/Ingenierie/Programmation/eigen-3.4.0 -I./stb-master -ID:/Users/Jerome/Documents/Ingenierie/Programmation/cmd_line_parser-master/include
|
INCLUDES = -I./include -ID:/Users/Jerome/Documents/Ingenierie/Programmation/eigen-3.4.0 -I./stb-master -ID:/Users/Jerome/Documents/Ingenierie/Programmation/cmd_line_parser-master/include
|
||||||
LDFLAGS = -fopenmp
|
LDFLAGS = -fopenmp
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ class Camera {
|
||||||
/// @brief Orients the camera according to the provided Euler angles. The position of the camera is (0,0,0).
|
/// @brief Orients the camera according to the provided Euler angles. The position of the camera is (0,0,0).
|
||||||
Camera & SetEulerAngles(double yaw, double pitch, double roll, bool radians = false);
|
Camera & SetEulerAngles(double yaw, double pitch, double roll, bool radians = false);
|
||||||
|
|
||||||
|
/// @brief Returns the Euler angles of the camera (technically the Tait-Bryan ZYX angles).
|
||||||
|
Eigen::Vector3d GetEulerAngles() const;
|
||||||
|
|
||||||
/// @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].
|
/// @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.
|
/// @param p_norm Normalized coordinates of the point on the sensor.
|
||||||
/// @return The ray direction in camera frame, cartesian coordinates.
|
/// @return The ray direction in camera frame, cartesian coordinates.
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,13 @@ class Image {
|
||||||
/// @return Reference to the image.
|
/// @return Reference to the image.
|
||||||
Image Colorized(unsigned char const* colormap) const;
|
Image Colorized(unsigned char const* colormap) const;
|
||||||
|
|
||||||
|
/// @brief Resizes the image using the given interpolation method and returns the result. The original image is unchanged.
|
||||||
|
/// @param width Width of the resized image.
|
||||||
|
/// @param height Height of the resized image.
|
||||||
|
/// @param interp_method Interpolation method to use.
|
||||||
|
/// @return The resized image.
|
||||||
|
Image Resized(int new_width, int new_height, InterpMethod const& interp_method = InterpMethod::BILINEAR) const;
|
||||||
|
|
||||||
std::tuple<unsigned char, unsigned char> ComputeMinMax() const;
|
std::tuple<unsigned char, unsigned char> ComputeMinMax() const;
|
||||||
|
|
||||||
std::tuple<unsigned char, unsigned char, double> ComputeMinMaxAvg() const;
|
std::tuple<unsigned char, unsigned char, double> ComputeMinMaxAvg() const;
|
||||||
|
|
|
||||||
|
|
@ -75,23 +75,60 @@ Camera & Camera::SetEulerAngles(double yaw, double pitch, double roll, bool radi
|
||||||
roll *= DEG2RAD;
|
roll *= DEG2RAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate sin and cos values
|
// Compute sin and cos values for the angles
|
||||||
const double s_roll = std::sin(roll);
|
const double sy = sin(yaw);
|
||||||
const double c_roll = std::cos(roll);
|
const double cy = cos(yaw);
|
||||||
const double s_pitch = std::sin(pitch);
|
const double sp = sin(pitch);
|
||||||
const double c_pitch = std::cos(pitch);
|
const double cp = cos(pitch);
|
||||||
const double s_yaw = std::sin(yaw);
|
const double sr = sin(roll);
|
||||||
const double c_yaw = std::cos(yaw);
|
const double cr = cos(roll);
|
||||||
|
|
||||||
// Build rotation matrix
|
// Compute the rotation matrix
|
||||||
R << c_pitch * c_yaw, -c_pitch * s_yaw, s_pitch,
|
R(0,0) = cy*cp;
|
||||||
s_roll * s_pitch * c_yaw + c_roll * s_yaw, -s_roll * s_pitch * s_yaw + c_roll * c_yaw, -s_roll * c_pitch,
|
R(0,1) = cy*sp*sr - sy*cr;
|
||||||
-c_roll * s_pitch * c_yaw + s_roll * s_yaw, c_roll * s_pitch * s_yaw + s_roll * c_yaw, c_roll * c_pitch;
|
R(0,2) = cy*sp*cr + sy*sr;
|
||||||
|
R(1,0) = sy*cp;
|
||||||
|
R(1,1) = sy*sp*sr + cy*cr;
|
||||||
|
R(1,2) = sy*sp*cr - cy*sr;
|
||||||
|
R(2,0) = -sp;
|
||||||
|
R(2,1) = cp*sr;
|
||||||
|
R(2,2) = cp*cr;
|
||||||
|
|
||||||
|
// Align the axes so that -Z points forward, Y points up, and X points to the right
|
||||||
|
Eigen::Matrix3d R_Cam_Alignment; R_Cam_Alignment << 0., 0., -1.,
|
||||||
|
-1., 0., 0.,
|
||||||
|
0., 1., 0;
|
||||||
|
R = R*R_Cam_Alignment;
|
||||||
|
|
||||||
R_T = R.transpose();
|
R_T = R.transpose();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Eigen::Vector3d Camera::GetEulerAngles() const
|
||||||
|
{
|
||||||
|
// Align the axes so that -Z points forward, Y points up, and X points to the right
|
||||||
|
Eigen::Matrix3d R_Cam_Alignment; R_Cam_Alignment << 0., 0., -1.,
|
||||||
|
-1., 0., 0.,
|
||||||
|
0., 1., 0;
|
||||||
|
Eigen::Matrix3d _R = R*R_Cam_Alignment.transpose();
|
||||||
|
|
||||||
|
double sy = sqrt(_R(0,0)*_R(0,0) + _R(1,0)*_R(1,0));
|
||||||
|
|
||||||
|
double yaw, pitch, roll;
|
||||||
|
if (sy > 1e-6) {
|
||||||
|
yaw = atan2(_R(1,0), _R(0,0));
|
||||||
|
pitch = atan2(-_R(2,0), sy);
|
||||||
|
roll = atan2(_R(2,1), _R(2,2));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
yaw = atan2(-_R(0,1), _R(1,1));
|
||||||
|
pitch = atan2(-_R(2,0), sy);
|
||||||
|
roll = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Eigen::Vector3d(yaw*RAD2DEG, pitch*RAD2DEG, roll*RAD2DEG);
|
||||||
|
}
|
||||||
|
|
||||||
Eigen::Vector3d Camera::ComputeRayDirInCameraFrame(Eigen::Vector2d const& p_norm) const {
|
Eigen::Vector3d Camera::ComputeRayDirInCameraFrame(Eigen::Vector2d const& p_norm) const {
|
||||||
return Eigen::Vector3d(p_norm(0)*tanFOV2, p_norm(1)*tanFOV2, -1.0).normalized();
|
return Eigen::Vector3d(p_norm(0)*tanFOV2, p_norm(1)*tanFOV2, -1.0).normalized();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -222,6 +222,16 @@ Image Image::Colorized(unsigned char const* colormap) const {
|
||||||
return colorized;
|
return colorized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Image Image::Resized(int new_width, int new_height, InterpMethod const& interp_method) const {
|
||||||
|
Image resized(new_width, new_height, depth);
|
||||||
|
double resize_factor = (double)new_width / (double)width;
|
||||||
|
#pragma omp parallel for
|
||||||
|
for(int i = 0 ; i < resized.GetHeight() ; i++)
|
||||||
|
for(int j = 0 ; j < resized.GetWidth() ; j++)
|
||||||
|
resized.SetPixel(i, j, GetPixelInterp(Eigen::Vector2d(i / resize_factor, j / resize_factor), interp_method));
|
||||||
|
return resized;
|
||||||
|
}
|
||||||
|
|
||||||
std::tuple<unsigned char, unsigned char> Image::ComputeMinMax() const {
|
std::tuple<unsigned char, unsigned char> Image::ComputeMinMax() const {
|
||||||
unsigned char min_val = 255, max_val = 0;
|
unsigned char min_val = 255, max_val = 0;
|
||||||
#pragma omp parallel for reduction(min:min_val) reduction(max:max_val)
|
#pragma omp parallel for reduction(min:min_val) reduction(max:max_val)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
#include <parser.hpp>
|
#include <parser.hpp>
|
||||||
#include <csv_parser.hpp>
|
#include <csv_parser.hpp>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <locale>
|
|
||||||
#include <logger.hpp>
|
#include <logger.hpp>
|
||||||
#include <progress_timer.hpp>
|
#include <progress_timer.hpp>
|
||||||
#include <omp.h>
|
#include <omp.h>
|
||||||
|
|
@ -121,13 +120,12 @@ void configure_parser(cmd_line_parser::parser & parser) {
|
||||||
parser.add("output_folder", "Output folder containing the projected images.", "-o", false);
|
parser.add("output_folder", "Output folder containing the projected images.", "-o", false);
|
||||||
parser.add("size", "Size of generated images, in pixels. By default, 1000x1000 px.", "-s", false);
|
parser.add("size", "Size of generated images, in pixels. By default, 1000x1000 px.", "-s", false);
|
||||||
parser.add("interpolation", "Interpolation mode when projecting the equirectangular (360 degree) image : 'nearest' (fast but blocky), 'bilinear' (slow but better). By default, 'bilinear'.", "--interp", false);
|
parser.add("interpolation", "Interpolation mode when projecting the equirectangular (360 degree) image : 'nearest' (fast but blocky), 'bilinear' (slow but better). By default, 'bilinear'.", "--interp", false);
|
||||||
parser.add("output_format", "Output format among the following : jpg, png, bmp, tga. By default, jpg.", "-o", false);
|
parser.add("output_format", "Output format among the following : jpg, png, bmp, tga. By default, jpg.", "-f", false);
|
||||||
parser.add("coverage", "Generate an colorized image depicting the coverage of the cameras over the whole sphere. The image produced is in equirectangular coordinates.", "--cov", false, true);
|
parser.add("coverage", "Generate an colorized image depicting the coverage of the cameras over the whole sphere. The image produced is in equirectangular coordinates.", "--cov", false, true);
|
||||||
parser.add("normalize", "If set, the images are histogram-normalized before being converted. This equalizes the utilization of all intensity values available. It might help the photogrammetry process to normalize the images.", "-n", false, true);
|
parser.add("normalize", "If set, the images are histogram-normalized before being converted. This equalizes the utilization of all intensity values available. It might help the photogrammetry process to normalize the images.", "-n", false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
// std::locale::global(std::locale("en_US.utf8"));// DEBUG
|
|
||||||
std::cout.precision(16);
|
std::cout.precision(16);
|
||||||
|
|
||||||
// Default configuration
|
// Default configuration
|
||||||
|
|
@ -149,6 +147,12 @@ int main(int argc, char** argv) {
|
||||||
// Print the configuration
|
// Print the configuration
|
||||||
print_program_configuration(program_config);
|
print_program_configuration(program_config);
|
||||||
|
|
||||||
|
// Create the output folder if it does not exist
|
||||||
|
if(!fs::exists(program_config.output_folder)) {
|
||||||
|
Logger::log("Creating output folder \"" + program_config.output_folder + "\"...");
|
||||||
|
fs::create_directory(program_config.output_folder);
|
||||||
|
}
|
||||||
|
|
||||||
// Load the cameras
|
// Load the cameras
|
||||||
std::vector<Camera> cameras;
|
std::vector<Camera> cameras;
|
||||||
{
|
{
|
||||||
|
|
@ -161,11 +165,10 @@ int main(int argc, char** argv) {
|
||||||
Logger::error("No cameras have been found, aborting now.");
|
Logger::error("No cameras have been found, aborting now.");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
for(unsigned int i = 0; i < cameras.size(); ++i) {
|
||||||
|
Eigen::Vector3d euler_angles = cameras[i].GetEulerAngles();
|
||||||
// DEBUG
|
Logger::log("Camera " + std::to_string(i) + " : Euler angles (yaw, pitch, roll) = " + std::to_string(euler_angles[0]) + " " + std::to_string(euler_angles[1]) + " " + std::to_string(euler_angles[2]) + " FOV = " + std::to_string(cameras[i].GetFOV()*RAD2DEG));
|
||||||
for(unsigned int i = 0; i < cameras.size(); ++i) {
|
}
|
||||||
std::cout << "Camera " << i << " :\nR =\n" << cameras[i].GetR() << "\nFOV = " << cameras[i].GetFOV() << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the slicer
|
// Build the slicer
|
||||||
|
|
@ -183,8 +186,7 @@ int main(int argc, char** argv) {
|
||||||
auto [min_cov, max_cov, avg_cov] = coverage_image.ComputeMinMaxAvg();
|
auto [min_cov, max_cov, avg_cov] = coverage_image.ComputeMinMaxAvg();
|
||||||
Logger::log("Coverage statistics (per pixel) :\tmin = " + std::to_string(int(min_cov)) + "\tmax = " + std::to_string(int(max_cov)) + "\tavg = " + std::to_string(avg_cov));
|
Logger::log("Coverage statistics (per pixel) :\tmin = " + std::to_string(int(min_cov)) + "\tmax = " + std::to_string(int(max_cov)) + "\tavg = " + std::to_string(avg_cov));
|
||||||
Logger::log("Exporting coverage image...");
|
Logger::log("Exporting coverage image...");
|
||||||
coverage_image.Normalized().Colorized(Image::Colormap::PARULA);
|
coverage_image.Normalized().Colorized(Image::Colormap::PARULA).Save(program_config.output_folder + "/coverage." + program_config.output_format);
|
||||||
coverage_image.Save(program_config.output_folder + "/coverage." + program_config.output_format);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the perspective images
|
// Generate the perspective images
|
||||||
|
|
@ -215,7 +217,7 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
Logger::log(progress_timer.increment());
|
Logger::log(progress_timer.increment());
|
||||||
}
|
}
|
||||||
Logger::log(std::to_string(image_files.size()) + " perspective images generated.");
|
Logger::log(std::to_string(image_files.size()*slicer.cameras.size()) + " perspective images generated from " + std::to_string(image_files.size()) + " input images.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue