Added actual image processing and logger.

This commit is contained in:
Jerome 2023-05-06 11:51:51 +02:00
parent 3fff8ce15b
commit d550bc7f83
6 changed files with 171 additions and 10 deletions

View file

@ -15,6 +15,11 @@ class Slicer360ToPerspective {
/// @param panorama_image The 360-degree image to be used as an input.
Slicer360ToPerspective(Image const& panorama_image_ = Image(1,1,3));
/// @brief Sets the 360-degree image to be used as an input.
/// @param panorama_image_ 360-degree image to be used as an input.
/// @return A reference to the current object.
Slicer360ToPerspective & SetPanoramaImage(Image const& panorama_image_);
/// @brief Sets the width of the output images.
/// @param width Width of the output images in pixels.
/// @param height Height of the output images in pixels. By default = -1. If < 0, the height is set to the width to yield a square image.

31
cpp/include/logger.hpp Normal file
View file

@ -0,0 +1,31 @@
#ifndef H_Logger
#define H_Logger
#include <iostream>
#include <chrono>
#include <ctime>
#include <iomanip>
/// @brief Implementation of the Logger class to print messages to the console. It is defined as a template class to save the .cpp file.
template <int _T=1>
class Logger_impl {
public:
// static constexpr char date_format[] = "%FT%T%z";// Works on Linux but not on Windows...
static constexpr char date_format[] = "%Y-%m-%dT%H:%M:%S";
static void log(const std::string& message) {
auto now = std::chrono::system_clock::now();
std::time_t now_time = std::chrono::system_clock::to_time_t(now);
std::cout << std::put_time(std::localtime(&now_time), date_format) << " " << message << std::endl;
}
static void error(const std::string& message) {
auto now = std::chrono::system_clock::now();
std::time_t now_time = std::chrono::system_clock::to_time_t(now);
std::cerr << std::put_time(std::localtime(&now_time), date_format) << " ERROR: " << message << std::endl;
}
};
/// @brief Logger class to print messages to the console.
using Logger = Logger_impl<1>;
#endif

View file

@ -0,0 +1,29 @@
#ifndef H_ProgressTimer
#define H_ProgressTimer
#include <chrono>
#include <iostream>
/// @brief This class implements a progress timer that predicts how long a loop will take to complete.
class ProgressTimer {
public:
/// @brief Creates a new progress timer.
/// @param num_iterations Number of iterations to perform.
ProgressTimer(size_t num_iterations);
/// @brief Increments the iteration counter and prints the progress.
/// @return A string with the current progress.
std::string increment();
/// @brief Converts a number of seconds to a string with hours, minutes and seconds.
/// @param seconds Total number of seconds (input).
/// @return "hh:mm:ss.uuuuuu"
static std::string seconds_to_hms_str(double seconds);
private:
std::chrono::steady_clock::time_point m_start;
size_t m_num_iterations;
size_t m_current_iteration;
};
#endif

View file

@ -6,7 +6,14 @@
#include <omp.h>
Slicer360ToPerspective::Slicer360ToPerspective(Image const& panorama_image_) : panorama_image(panorama_image_), output_image_width(2), interpolation_method(Image::InterpMethod::BILINEAR), output_folder(".")
{}
{
SetOutputImageSize(0);
}
Slicer360ToPerspective & Slicer360ToPerspective::SetPanoramaImage(Image const& panorama_image_) {
panorama_image = panorama_image_;
return *this;
}
Slicer360ToPerspective & Slicer360ToPerspective::SetOutputImageSize(int width, int height) {
output_image_width = (width > 0) ? width : 1024;

View file

@ -1,6 +1,13 @@
#include <Slicer360ToPerspective.hpp>
#include <parser.hpp>
#include <csv_parser.hpp>
#include <filesystem>
#include <locale>
#include <logger.hpp>
#include <progress_timer.hpp>
#include <omp.h>
namespace fs = std::filesystem;
std::string convert_interpolation_mode_to_str(Image::InterpMethod interpolation_mode) {
switch(interpolation_mode) {
@ -13,9 +20,20 @@ std::string convert_interpolation_mode_to_str(Image::InterpMethod interpolation_
}
}
void scan_folder_for_files(const fs::path& folder_path, std::vector<fs::path>& files, bool recursive = true)
{
for(const auto& entry : fs::directory_iterator(folder_path))
{
if(fs::is_directory(entry) && recursive)
scan_folder_for_files(entry, files, recursive);
if(fs::is_regular_file(entry))
files.push_back(entry.path());
}
}
struct ProgramConfiguration {
std::string cameras_filename;
std::string images_folder;
std::string images_folder = "";
std::string output_folder = ".";
std::string output_format = "jpg";
Image::InterpMethod interpolation_mode = Image::InterpMethod::BILINEAR;
@ -44,8 +62,13 @@ bool decode_arguments(cmd_line_parser::parser const& parser, ProgramConfiguratio
if(parser.parsed("cameras"))
program_config.cameras_filename = parser.get<std::string>("cameras");
if(parser.parsed("images"))
if(parser.parsed("images")) {
program_config.images_folder = parser.get<std::string>("images");
if(!fs::exists(program_config.images_folder)) {
std::cerr << "Error: images folder \"" << program_config.images_folder << "\" does not exist." << std::endl;
return false;
}
}
if(parser.parsed("output_folder"))
program_config.output_folder = parser.get<std::string>("output_folder");
@ -104,6 +127,7 @@ void configure_parser(cmd_line_parser::parser & parser) {
}
int main(int argc, char** argv) {
// std::locale::global(std::locale("en_US.utf8"));// DEBUG
std::cout.precision(16);
// Default configuration
@ -128,13 +152,13 @@ int main(int argc, char** argv) {
// Load the cameras
std::vector<Camera> cameras;
{
std::cout << "Loading cameras from \"" << program_config.cameras_filename << "\"..." << std::endl;
Logger::log("Loading cameras from \"" + program_config.cameras_filename + "\"...");
Slicer360ToPerspective slicer; // create an empty slicer
slicer.LoadCamerasFromFile(program_config.cameras_filename); // load the cameras from the file into the slicer
cameras = slicer.cameras; // copy the cameras that were loaded
std::cout << "Loaded " << cameras.size() << " cameras." << std::endl;
Logger::log("Loaded " + std::to_string(cameras.size()) + " cameras.");
if(!cameras.size()) { // If no cameras were loaded, abort
std::cerr << "Aborting now." << std::endl;
Logger::error("No cameras have been found, aborting now.");
return 1;
}
}
@ -153,13 +177,46 @@ int main(int argc, char** argv) {
// Generate the coverage image if requested
if(program_config.coverage) {
std::cout << "Generating coverage image..." << std::endl;
Logger::log("Generating coverage image...");
Image coverage_image = slicer.ComputeCoverage(program_config.size, true);
std::cout << "Coverage image generated." << std::endl;
Logger::log("Coverage image generated.");
auto [min_cov, max_cov, avg_cov] = coverage_image.ComputeMinMaxAvg();
std::cout << "Coverage statistics (per pixel) :\n\tmin = " << int(min_cov) << "\n\tmax = " << int(max_cov) << "\n\tavg = " << avg_cov << std::endl;
std::cout << "Exporting coverage image..." << std::endl;
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);
}
// Generate the perspective images
if(program_config.images_folder != "") {
Logger::log("Generating perspective images...");
std::vector<fs::path> image_files;
scan_folder_for_files(program_config.images_folder, image_files);
ProgressTimer progress_timer(image_files.size());
for(fs::path const& image_file : image_files) {
std::string image_path = image_file.string();
Logger::log("Processing \"" + image_path + "\"...");
Image input_image(image_path);
if(program_config.normalize) {
Logger::log("\tNormalizing image...");
input_image = input_image.HistogramNormalized();
}
slicer.SetPanoramaImage(input_image);
Logger::log("\tProjecting " + image_file.string() + " using " + std::to_string(slicer.cameras.size()) + " cameras...");
std::vector<Image> projected_images = slicer.ProjectToAllCameras();
#pragma omp parallel for
for(unsigned int i = 0; i < projected_images.size(); ++i) {
#pragma omp critical
{Logger::log("\t\tSaving perspective image " + std::to_string(i) + "...");}
std::string output_image_path = program_config.output_folder + "/" + image_file.stem().string() + "_" + std::to_string(i) + "." + program_config.output_format;
projected_images[i].Save(output_image_path);
}
Logger::log(progress_timer.increment());
}
Logger::log(std::to_string(image_files.size()) + " perspective images generated.");
}
return 0;
}

View file

@ -0,0 +1,32 @@
#include <progress_timer.hpp>
#include <iomanip>
#include <cmath>
ProgressTimer::ProgressTimer(size_t num_iterations)
: m_start(std::chrono::steady_clock::now()), m_num_iterations(num_iterations), m_current_iteration(0)
{
}
std::string ProgressTimer::increment()
{
++m_current_iteration;
auto current_time = std::chrono::steady_clock::now();
auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - m_start).count();
double progress_percent = static_cast<double>(m_current_iteration) / m_num_iterations * 100;
double elapsed_seconds = elapsed_time / 1000.0;
double remaining_seconds = (m_num_iterations - m_current_iteration) * (elapsed_seconds / m_current_iteration);
return "Progress: " + std::to_string(progress_percent) + " %, Elapsed time: " + seconds_to_hms_str(elapsed_seconds) + ", Remaining time: " + seconds_to_hms_str(remaining_seconds);
}
std::string ProgressTimer::seconds_to_hms_str(double seconds) {
unsigned int hours = seconds / 3600;
unsigned int minutes = std::fmod(seconds, 3600.0) / 60;
unsigned int remaining_seconds = std::fmod(seconds, 60.0);
std::ostringstream oss;
oss << std::setfill('0') << std::setw(2) << hours << ":"
<< std::setfill('0') << std::setw(2) << minutes << ":"
<< std::setfill('0') << std::setw(2) << remaining_seconds << "."
<< std::setfill('0') << std::setw(6) << int(std::fmod(seconds, 1) * 1000000);
return oss.str();
}