Version 1.0 : output folder is created automatically, everything is logged properly.

This commit is contained in:
Jerome 2023-05-07 12:20:40 +02:00
parent d550bc7f83
commit 03f635cae4
6 changed files with 84 additions and 25 deletions

View file

@ -1,5 +1,5 @@
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
LDFLAGS = -fopenmp

View file

@ -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).
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].
/// @param p_norm Normalized coordinates of the point on the sensor.
/// @return The ray direction in camera frame, cartesian coordinates.

View file

@ -159,6 +159,13 @@ class Image {
/// @return Reference to the image.
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, double> ComputeMinMaxAvg() const;

View file

@ -75,23 +75,60 @@ Camera & Camera::SetEulerAngles(double yaw, double pitch, double roll, bool radi
roll *= DEG2RAD;
}
// Calculate sin and cos values
const double s_roll = std::sin(roll);
const double c_roll = std::cos(roll);
const double s_pitch = std::sin(pitch);
const double c_pitch = std::cos(pitch);
const double s_yaw = std::sin(yaw);
const double c_yaw = std::cos(yaw);
// Compute sin and cos values for the angles
const double sy = sin(yaw);
const double cy = cos(yaw);
const double sp = sin(pitch);
const double cp = cos(pitch);
const double sr = sin(roll);
const double cr = cos(roll);
// Compute the rotation matrix
R(0,0) = cy*cp;
R(0,1) = cy*sp*sr - sy*cr;
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;
// Build rotation matrix
R << c_pitch * c_yaw, -c_pitch * s_yaw, s_pitch,
s_roll * s_pitch * c_yaw + c_roll * s_yaw, -s_roll * s_pitch * s_yaw + c_roll * c_yaw, -s_roll * c_pitch,
-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_T = R.transpose();
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 {
return Eigen::Vector3d(p_norm(0)*tanFOV2, p_norm(1)*tanFOV2, -1.0).normalized();
}

View file

@ -222,6 +222,16 @@ Image Image::Colorized(unsigned char const* colormap) const {
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 {
unsigned char min_val = 255, max_val = 0;
#pragma omp parallel for reduction(min:min_val) reduction(max:max_val)
@ -278,4 +288,4 @@ std::vector<uint64_t> Image::CumulativeHistogram(std::vector<uint64_t> const& hi
for(unsigned int i = 1 ; i < histogram.size() ; i++)
cumul_hist[i] = cumul_hist[i - 1] + histogram[i];
return cumul_hist;
}
}

View file

@ -2,7 +2,6 @@
#include <parser.hpp>
#include <csv_parser.hpp>
#include <filesystem>
#include <locale>
#include <logger.hpp>
#include <progress_timer.hpp>
#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("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("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("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) {
// std::locale::global(std::locale("en_US.utf8"));// DEBUG
std::cout.precision(16);
// Default configuration
@ -149,6 +147,12 @@ int main(int argc, char** argv) {
// Print the configuration
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
std::vector<Camera> cameras;
{
@ -161,11 +165,10 @@ int main(int argc, char** argv) {
Logger::error("No cameras have been found, aborting now.");
return 1;
}
}
// DEBUG
for(unsigned int i = 0; i < cameras.size(); ++i) {
std::cout << "Camera " << i << " :\nR =\n" << cameras[i].GetR() << "\nFOV = " << cameras[i].GetFOV() << std::endl;
for(unsigned int i = 0; i < cameras.size(); ++i) {
Eigen::Vector3d euler_angles = cameras[i].GetEulerAngles();
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));
}
}
// Build the slicer
@ -183,8 +186,7 @@ int main(int argc, char** argv) {
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("Exporting coverage image...");
coverage_image.Normalized().Colorized(Image::Colormap::PARULA);
coverage_image.Save(program_config.output_folder + "/coverage." + program_config.output_format);
coverage_image.Normalized().Colorized(Image::Colormap::PARULA).Save(program_config.output_folder + "/coverage." + program_config.output_format);
}
// Generate the perspective images
@ -215,7 +217,7 @@ int main(int argc, char** argv) {
}
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;