Added projection to perspective and OpenMP support.

This commit is contained in:
Jerome 2023-05-01 13:41:51 +02:00
parent 80c4a9c0a1
commit bd6948ecdc
5 changed files with 157 additions and 19 deletions

View file

@ -1,6 +1,7 @@
CXX = g++ CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -pedantic -g CXXFLAGS = -std=c++17 -Wall -Wextra -pedantic -g
INCLUDES = -I./include -ID:/Users/Jerome/Documents/Ingenierie/Programmation/eigen-3.4.0 -I./stb-master INCLUDES = -I./include -ID:/Users/Jerome/Documents/Ingenierie/Programmation/eigen-3.4.0 -I./stb-master
LDFLAGS = -fopenmp
SRC_DIR = src SRC_DIR = src
OBJ_DIR = obj OBJ_DIR = obj
@ -14,10 +15,10 @@ EXEC = $(OBJ_DIR)/360_to_perspective
all: $(EXEC) all: $(EXEC)
$(EXEC): $(OBJS) $(EXEC): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $^
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp | $(OBJ_DIR) $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp | $(OBJ_DIR)
$(CXX) $(CXXFLAGS) $(INCLUDES) -c -o $@ $< $(CXX) $(CXXFLAGS) $(INCLUDES) $(LDFLAGS) -c -o $@ $<
$(OBJ_DIR): $(OBJ_DIR):
mkdir -p $@ mkdir -p $@

View file

@ -31,19 +31,24 @@ class Image {
/// @param width_ Width of the image. /// @param width_ Width of the image.
/// @param height_ Height of the image. /// @param height_ Height of the image.
/// @param depth_ Depth of the image. /// @param depth_ Depth of the image.
Image(int width_, int height_, int depth_ = 1); Image(int width_ = 1, int height_ = 1, int depth_ = 1);
/// @brief Copies another image. Performs a deep copy. /// @brief Copies another image. Performs a deep copy.
/// @param other The image to copy. /// @param other The image to copy.
Image(Image const& other); Image(Image const& other);
/// @brief Frees the allocated memory of the image.
~Image();
/// @brief Loads an image from a file. /// @brief Loads an image from a file.
/// @param filename Path to the image file. /// @param filename Path to the image file.
Image(std::string const& filename); Image(std::string const& filename);
/// @brief Frees the allocated memory of the image.
~Image();
/// @brief Copies the other image into this one. Performs a deep copy.
/// @param other Image to copy.
/// @return A reference to this image.
Image & operator=(Image const& other);
/// @brief Saves the image to a file. /// @brief Saves the image to a file.
/// Supports the following formats : PNG, BMP, TGA, JPG. The format is determined from the file extension. /// Supports the following formats : PNG, BMP, TGA, JPG. The format is determined from the file extension.
/// @param filename Path to save the image to. /// @param filename Path to save the image to.

View file

@ -4,21 +4,71 @@
#include <Camera.hpp> #include <Camera.hpp>
#include <Image.hpp> #include <Image.hpp>
/// @brief This class encapsulates the algorithms to convert a 360-degree image to a perspective image. /// @brief This class encapsulates the algorithms to convert a 360-degree image to perspective images.
/// @details The 360-degree image is converted to perspective images by projecting the pixels of the 360-degree image using perspective projection and virtual cameras.
/// The virtual cameras are added to the slicer using the AddCamera() method.
/// The output images are saved in the folder specified by the SetOutputFolder() method.
/// By default, the ProjectToCameras and ProjectToAllCameras are parallelized using OpenMP.
/// If the DO_NOT_USE_OPENMP macro is defined, the OpenMP parallization is disabled and the computation is sequential regardless of the value of "parallel".
class Slicer360ToPerspective { class Slicer360ToPerspective {
public: public:
Slicer360ToPerspective(); /// @brief Builds a Slicer360ToPerspective object using a 360-degree image.
/// @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 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.
/// @return A reference to the current object.
Slicer360ToPerspective & SetOutputImageSize(int width, int height = -1);
/// @brief Sets the interpolation method used to project the 360-degree image to perspective images. See Image::InterpMethod.
/// @param interpolation_method_ Interpolation method used to project the 360-degree image to perspective images.
/// @return A reference to the current object.
Slicer360ToPerspective & SetInterpolationMethod(Image::InterpMethod const& interpolation_method_);
/// @brief Sets the folder where the output images are saved. By default, the output images are saved in the current folder.
/// @param folder Folder where the output images are saved.
/// @return A reference to the current object.
Slicer360ToPerspective & SetOutputFolder(std::string const& folder);
/// @brief Computes the coverage of the 360-degree image by the cameras added to the slicer. /// @brief Computes the coverage of the 360-degree image by the cameras added to the slicer.
/// @param width Width of the generated 360-degree image containing the results of the analysis. /// @param width Width of the generated 360-degree image containing the results of the analysis.
/// @param raw If true, the output is the number of cameras that see the pixel of the 360 image (8-bit monochrome image). If false, the output is a colored RGB image. /// @param raw If true, the output is the number of cameras that see the pixel of the 360 image (8-bit monochrome image). If false, the output is a colored RGB image.
/// @return The coverage of the 360-degree image. /// @return The coverage of the 360-degree image.
Image ComputeCoverage(int width, bool raw = false); Image ComputeCoverage(int width, bool raw = false) const;
/// @brief Projects the 360-degree image to a perspective image using the specified camera.
/// @param camera The camera used to project the 360-degree image.
/// @return The generated perspective image.
Image ProjectToCamera(Camera const& camera, bool parallel = true) const;
/// @brief Projects the 360-degree image to perspective images using the specified cameras.
/// @param cameras A vector of cameras used to project the 360-degree image.
/// @param parallel If true, the projection is done in parallel using all the available cores.
/// @return A vector of generated perspective images.
std::vector<Image> ProjectToCameras(std::vector<Camera> const& cameras_, bool parallel = true) const;
/// @brief Projects the 360-degree image to perspective images using all the cameras added to the slicer.
/// @param parallel If true, the projection is done in parallel using all the available cores.
/// @return A vector of generated perspective images.
std::vector<Image> ProjectToAllCameras(bool parallel = true) const;
std::vector<Camera> cameras;//!< A vector of cameras that are used to convert the 360-degree image to perspective images. std::vector<Camera> cameras;//!< A vector of cameras that are used to convert the 360-degree image to perspective images.
private: private:
/// @brief Computes the projection of the 360-degree image to a perspective image using the specified camera on a single pixel of the output image.
/// @param camera The camera used to project the 360-degree image.
/// @param i The y-coordinate of the pixel of the output image (row).
/// @param j The x-coordinate of the pixel of the output image (column).
/// @return The color of the pixel of the output image.
Eigen::Vector3i ProjectToCameraPixel(Camera const& camera, int i, int j) const;
Image panorama_image; //!< The 360-degree input image.
int output_image_width; //!< Width of the output images in pixels.
int output_image_height; //!< Height of the output images in pixels.
Image::InterpMethod interpolation_method; //!< Interpolation method used to project the 360-degree image to perspective images.
std::string output_folder; //!< Folder where the output images are saved.
}; };
#endif #endif

View file

@ -9,10 +9,6 @@ Image::Image(Image const& other) : width(other.width), height(other.height), dep
std::memcpy(data, other.data, width * height * depth); std::memcpy(data, other.data, width * height * depth);
} }
Image::~Image() {
delete[] data;
}
Image::Image(std::string const& filename) { Image::Image(std::string const& filename) {
// Load image data using stb_image.h // Load image data using stb_image.h
unsigned char* image_data = stbi_load(filename.c_str(), &width, &height, &depth, 0); unsigned char* image_data = stbi_load(filename.c_str(), &width, &height, &depth, 0);
@ -31,6 +27,25 @@ Image::Image(std::string const& filename) {
} }
} }
Image::~Image() {
delete[] data;
}
Image & Image::operator=(Image const& other) {
// Delete current data
delete[] data;
// Copy image attributes
width = other.width;
height = other.height;
depth = other.depth;
// Allocate new data and copy the data from other.data.
data = new unsigned char[width * height * depth];
std::memcpy(data, other.data, width * height * depth);
return *this;
}
int Image::Save(std::string const& filename) const { int Image::Save(std::string const& filename) const {
if(filename.size() > 4 && filename.substr(filename.size() - 4) == ".png") if(filename.size() > 4 && filename.substr(filename.size() - 4) == ".png")
return stbi_write_png(filename.c_str(), width, height, depth, (const void *)data, 0); return stbi_write_png(filename.c_str(), width, height, depth, (const void *)data, 0);

View file

@ -1,13 +1,30 @@
#include <Slicer360ToPerspective.hpp> #include <Slicer360ToPerspective.hpp>
#include <frame_conversions.hpp> #include <frame_conversions.hpp>
Slicer360ToPerspective::Slicer360ToPerspective() #ifndef DO_NOT_USE_OPENMP
{ #include <omp.h>
#endif
Slicer360ToPerspective::Slicer360ToPerspective(Image const& panorama_image_) : panorama_image(panorama_image_), output_image_width(2), interpolation_method(Image::InterpMethod::BILINEAR), output_folder(".")
{}
Slicer360ToPerspective & Slicer360ToPerspective::SetOutputImageSize(int width, int height) {
output_image_width = (width > 0) ? width : 1024;
output_image_height = (height > 0) ? height : output_image_width;
return *this;
} }
Image Slicer360ToPerspective::ComputeCoverage(int width, bool raw) Slicer360ToPerspective & Slicer360ToPerspective::SetInterpolationMethod(Image::InterpMethod const& interpolation_method_) {
{ interpolation_method = interpolation_method_;
return *this;
}
Slicer360ToPerspective & Slicer360ToPerspective::SetOutputFolder(std::string const& folder) {
output_folder = folder;
return *this;
}
Image Slicer360ToPerspective::ComputeCoverage(int width, bool raw) const {
int height = width/2; int height = width/2;
Image coverage(width, height, 1); Image coverage(width, height, 1);
@ -32,3 +49,53 @@ Image Slicer360ToPerspective::ComputeCoverage(int width, bool raw)
else else
return coverage.Normalized().Colorized(Image::Colormap::PARULA); return coverage.Normalized().Colorized(Image::Colormap::PARULA);
} }
Eigen::Vector3i Slicer360ToPerspective::ProjectToCameraPixel(Camera const& camera, int i, int j) const {
// Get the ray direction in inertial frame by projecting the pixel to the sphere
// Eigen::Vector2d Camera::PixelToNormalizedCoordinates(unsigned int i, unsigned int j, unsigned int width, unsigned int height) {
Eigen::Vector2d p_sensor = Camera::PixelToNormalizedCoordinates(i, j, output_image_width, output_image_height);
p_sensor[0] *= -1;
p_sensor[1] *= ((double)output_image_height)/((double)output_image_width);// Take aspect ratio into account.
// Compute ray direction from sensor coordinates
Eigen::Vector3d ray_dir = camera.ComputeRayDirInInertialFrame(p_sensor);
Eigen::Vector3d ray_dir_sph = cart2sph(ray_dir);
Eigen::Vector2d p_equi = sph2equirectangular_d(ray_dir_sph, output_image_width, output_image_height);
return panorama_image.GetPixelInterp(p_equi, interpolation_method);
}
Image Slicer360ToPerspective::ProjectToCamera(Camera const& camera, bool parallel) const {
Image image(output_image_width, output_image_height, 3);
if(parallel) {
#ifndef DO_NOT_USE_OPENMP
#pragma omp parallel for
#endif
for(int i = 0 ; i < output_image_height ; i++)
for(int j = 0 ; j < output_image_width ; j++)
image.SetPixel(i, j, ProjectToCameraPixel(camera, i, j));
} else {
for(int i = 0 ; i < output_image_height ; i++)
for(int j = 0 ; j < output_image_width ; j++)
image.SetPixel(i, j, ProjectToCameraPixel(camera, i, j));
}
return image;
}
std::vector<Image> Slicer360ToPerspective::ProjectToCameras(std::vector<Camera> const& cameras_, bool parallel) const {
std::vector<Image> images(cameras_.size());
if(parallel) {
#ifndef DO_NOT_USE_OPENMP
#pragma omp parallel for
#endif
for(unsigned int i = 0 ; i < cameras_.size(); i++)
images[i] = ProjectToCamera(cameras_[i], false);// Parallelization is already done at the camera level.
} else {
for(unsigned int i = 0 ; i < cameras_.size(); i++)
images[i] = ProjectToCamera(cameras_[i], false);// Parallelization is already done at the camera level.
}
return images;
}
std::vector<Image> Slicer360ToPerspective::ProjectToAllCameras(bool parallel) const {
return ProjectToCameras(cameras, parallel);
}