Added actual image processing and logger.
This commit is contained in:
parent
3fff8ce15b
commit
d550bc7f83
6 changed files with 171 additions and 10 deletions
|
|
@ -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
31
cpp/include/logger.hpp
Normal 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
|
||||
29
cpp/include/progress_timer.hpp
Normal file
29
cpp/include/progress_timer.hpp
Normal 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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
32
cpp/src/progress_timer.cpp
Normal file
32
cpp/src/progress_timer.cpp
Normal 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();
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue