Added CSV parser and Camera loading from decoded CSV.
This commit is contained in:
parent
e20c153f1e
commit
71a78e12e4
7 changed files with 154 additions and 2 deletions
|
|
@ -23,6 +23,8 @@ class Camera {
|
|||
Camera & SetForward(Eigen::Vector3d const& fwd_);
|
||||
Camera & SetUp(Eigen::Vector3d const& up_);
|
||||
Camera & SetR(Eigen::Matrix3d const& R_);
|
||||
|
||||
/// @brief Sets the field of view of the camera, in radians.
|
||||
Camera & SetFOV(double fov);
|
||||
|
||||
// Methods
|
||||
|
|
@ -68,6 +70,11 @@ class Camera {
|
|||
// 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);
|
||||
|
||||
/// @brief Creates a Camera object from a vector of yaw, pitch, roll and FOV, read from a CSV file.
|
||||
/// @param yaw_pitch_roll_fov 4-D Vector containing yaw, pitch, roll and FOV as double-precision floating point numbers.
|
||||
/// @return A Camera object with the specified parameters.
|
||||
static Camera FromYawPitchRollFOV(std::vector<double> const& yaw_pitch_roll_fov);
|
||||
|
||||
private:
|
||||
Eigen::Vector3d fwd; //!< Forward vector in inertial frame
|
||||
|
|
|
|||
|
|
@ -54,6 +54,16 @@ class Slicer360ToPerspective {
|
|||
/// @return A vector of generated perspective images.
|
||||
std::vector<Image> ProjectToAllCameras(bool parallel = true) const;
|
||||
|
||||
/// @brief Loads the cameras from a CSV file.
|
||||
/// @details The CSV file should contain one camera per row and the columns should be in the following order:
|
||||
/// - Yaw (degrees)
|
||||
/// - Pitch (degrees)
|
||||
/// - Roll (degrees)
|
||||
/// - FOV (degrees)
|
||||
/// @param filename File path of the CSV file.
|
||||
/// @return A reference to the current object.
|
||||
Slicer360ToPerspective & LoadCamerasFromFile(std::string const& filename);
|
||||
|
||||
std::vector<Camera> cameras;//!< A vector of cameras that are used to convert the 360-degree image to perspective images.
|
||||
|
||||
private:
|
||||
|
|
|
|||
25
cpp/include/csv_parser.hpp
Normal file
25
cpp/include/csv_parser.hpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef H_CSVparser
|
||||
#define H_CSVparser
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief Parses a CSV file containing floating-point numbers.
|
||||
*
|
||||
* @param filename The name of the CSV file to parse.
|
||||
* @return A vector of vectors of doubles containing the parsed data.
|
||||
* Returns an empty vector if there was an error.
|
||||
*
|
||||
* This function reads a CSV file containing floating-point numbers and
|
||||
* returns the data as a vector of vectors of doubles. The CSV file should
|
||||
* use commas as delimiters and should not contain any quotes or other
|
||||
* special characters. Each row in the CSV file should have the same number
|
||||
* of columns. If there is an error opening the file or parsing the data,
|
||||
* this function returns an empty vector and prints an error message to
|
||||
* standard error. The error message will indicate the type of error and
|
||||
* the name of the file where the error occurred.
|
||||
*/
|
||||
std::vector<std::vector<double>> parse_csv_file(const std::string& filename, char delimiter = ',');
|
||||
|
||||
#endif
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include <Camera.hpp>
|
||||
#include <iostream>
|
||||
|
||||
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))
|
||||
|
|
@ -122,4 +123,12 @@ bool Camera::IsPointVisibleInertialFrame(Eigen::Vector3d const& p_inertial) cons
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Camera Camera::FromYawPitchRollFOV(std::vector<double> const& yaw_pitch_roll_fov) {
|
||||
if(yaw_pitch_roll_fov.size() != 4) {
|
||||
std::cerr << "Error: Camera::FromYawPitchRollFOV: yaw_pitch_roll_fov must be a vector of size 4." << std::endl;
|
||||
return Camera();
|
||||
}
|
||||
return Camera().SetEulerAngles(yaw_pitch_roll_fov[0], yaw_pitch_roll_fov[1], yaw_pitch_roll_fov[2]).SetFOV(yaw_pitch_roll_fov[3]*DEG2RAD);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include <Slicer360ToPerspective.hpp>
|
||||
#include <frame_conversions.hpp>
|
||||
#include <csv_parser.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#ifndef DO_NOT_USE_OPENMP
|
||||
#include <omp.h>
|
||||
|
|
@ -99,3 +101,21 @@ std::vector<Image> Slicer360ToPerspective::ProjectToCameras(std::vector<Camera>
|
|||
std::vector<Image> Slicer360ToPerspective::ProjectToAllCameras(bool parallel) const {
|
||||
return ProjectToCameras(cameras, parallel);
|
||||
}
|
||||
|
||||
Slicer360ToPerspective & Slicer360ToPerspective::LoadCamerasFromFile(std::string const& filename) {
|
||||
std::vector<std::vector<double>> camera_specs = parse_csv_file(filename);
|
||||
if(camera_specs.size() == 0) {
|
||||
std::cerr << "Error: no camera specification found in file " << filename << std::endl;
|
||||
return *this;
|
||||
} else if(camera_specs[0].size() < 4) {
|
||||
std::cerr << "Error: camera specification in file " << filename << " must have 4 columns : yaw, pitch, roll, FOV (all in degrees)." << std::endl;
|
||||
return *this;
|
||||
} else if(camera_specs[0].size() > 4) {
|
||||
std::cerr << "Warning: camera specification in file " << filename << " has more than 4 columns. Only the first 4 columns will be used." << std::endl;
|
||||
}
|
||||
// Load cameras from decoded CSV file
|
||||
cameras.clear();
|
||||
for(auto& camera_spec : camera_specs)
|
||||
cameras.push_back(Camera::FromYawPitchRollFOV(camera_spec));
|
||||
return *this;
|
||||
}
|
||||
|
|
|
|||
46
cpp/src/csv_parser.cpp
Normal file
46
cpp/src/csv_parser.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include <csv_parser.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
std::vector<std::vector<double>> parse_csv_file(const std::string& filename, char delimiter)
|
||||
{
|
||||
// Open file for reading
|
||||
std::ifstream file(filename);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Error: could not open file " << filename << std::endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Read file line by line
|
||||
std::vector<std::vector<double>> data;
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
// Parse line into a vector of doubles
|
||||
std::vector<double> row;
|
||||
std::istringstream iss(line);
|
||||
std::string token;
|
||||
while (std::getline(iss, token, delimiter)) {
|
||||
try {
|
||||
double value = std::stod(token);
|
||||
row.push_back(value);
|
||||
} catch (const std::invalid_argument&) {
|
||||
std::cerr << "Error: invalid floating-point number \"" << token << "\" in file " << filename << std::endl;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
data.push_back(row);
|
||||
}
|
||||
|
||||
// Check that all rows have the same size
|
||||
const size_t num_columns = data[0].size();
|
||||
for (const auto& row : data) {
|
||||
if (row.size() != num_columns) {
|
||||
std::cerr << "Error: inconsistent number of columns in file " << filename << std::endl;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
|
@ -3,7 +3,8 @@
|
|||
#include <Image.hpp>
|
||||
#include <Slicer360ToPerspective.hpp>
|
||||
#include <frame_conversions.hpp>
|
||||
#include "../include/parser.hpp"
|
||||
#include <parser.hpp>
|
||||
#include <csv_parser.hpp>
|
||||
|
||||
#include <test_framework.hpp>
|
||||
|
||||
|
|
@ -254,6 +255,14 @@ int main(int argc, char** argv) {
|
|||
|
||||
EXPECT_EQ(Camera().SetEulerAngles(45.0, 0.0, 0.0).GetR(), Camera().SetEulerAngles(45.0*DEG2RAD, 0.0, 0.0, true).GetR());// input in radians vs degrees
|
||||
}
|
||||
{// Test Camera creation from CSV line
|
||||
std::cout << "Creation from CSV line\n";
|
||||
std::vector<double> yaw_pitch_roll_FOV = {45.0, 30.0, -15.0, 90.0};
|
||||
Camera camera = Camera::FromYawPitchRollFOV(yaw_pitch_roll_FOV);
|
||||
|
||||
EXPECT_EQ(camera.GetFOV(), yaw_pitch_roll_FOV[3]*DEG2RAD);
|
||||
EXPECT_EQ(camera.GetR(), Camera().SetEulerAngles(yaw_pitch_roll_FOV[0], yaw_pitch_roll_FOV[1], yaw_pitch_roll_FOV[2]).GetR());
|
||||
}
|
||||
}
|
||||
|
||||
if(0){// Image
|
||||
|
|
@ -455,6 +464,32 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
}
|
||||
|
||||
if(1){// CSV parser
|
||||
std::cout << "CSV parser\n";
|
||||
{ std::cout << "Valid CSV file\n";
|
||||
auto data = parse_csv_file("../data_test_valid.csv");
|
||||
EXPECT_EQ(data.empty(), false);
|
||||
for(auto& row : data) {
|
||||
for(auto& cell : row) {
|
||||
std::cout << cell << "\t";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
{ std::cout << "Invalid CSV file (missing column on one row)\n";
|
||||
auto data = parse_csv_file("../data_test_invalid.csv");
|
||||
EXPECT_EQ(data.empty(), true);
|
||||
}
|
||||
{ std::cout << "Invalid CSV file (bad floating-point number)\n";
|
||||
auto data = parse_csv_file("../data_test_invalid_floating_point.csv");
|
||||
EXPECT_EQ(data.empty(), true);
|
||||
}
|
||||
{ std::cout << "Non-existent CSV file\n";
|
||||
auto data = parse_csv_file("does_not_exist.csv");
|
||||
EXPECT_EQ(data.empty(), true);
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_TESTS_SUMMARY();
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue