diff --git a/Util/ImageConverter/Makefile b/Util/ImageConverter/Makefile new file mode 100644 index 000000000..ffb286355 --- /dev/null +++ b/Util/ImageConverter/Makefile @@ -0,0 +1,17 @@ +CXX=g++ +FLAGS=-Wall -Wextra -std=c++14 -fopenmp +LIBS=-lboost_system -lboost_filesystem -lboost_program_options -lpng -ljpeg -ltiff +HEADERS=*.h +SOURCES=main.cpp +EXE=image_converter + +build: release + +release: $(SOURCES) $(HEADERS) + $(CXX) $(FLAGS) -O3 -DNDEBUG -o $(EXE) $(SOURCES) $(LIBS) + +debug: $(SOURCES) $(HEADERS) + $(CXX) $(FLAGS) -O0 -g -D_DEBUG -o $(EXE)_debug $(SOURCES) $(LIBS) + +clean: + rm -f $(EXE) $(EXE)_debug diff --git a/Util/ImageConverter/README.md b/Util/ImageConverter/README.md new file mode 100644 index 000000000..4b75aa657 --- /dev/null +++ b/Util/ImageConverter/README.md @@ -0,0 +1,7 @@ +Image Converter +=============== + +Converts output images of depth and semantic segmentation to a prettier format. + + make + ./image_converter -h diff --git a/Util/ImageConverter/depth_pixel_converter.h b/Util/ImageConverter/depth_pixel_converter.h new file mode 100644 index 000000000..06a09b2c2 --- /dev/null +++ b/Util/ImageConverter/depth_pixel_converter.h @@ -0,0 +1,58 @@ +// MIT License. Copyright (c) 2017 Computer Vision Center (CVC) at the +// Universitat Autonoma de Barcelona (UAB), and the INTEL Visual Computing Lab. + +#pragma once + +#include +#include +#include + +#include "image_converter_types.h" + +namespace image_converter { +namespace detail { + + template + static inline float cast(T i) { + return static_cast(i); + } + + static auto normalized_depth(boost::gil::rgb8_pixel_t &pixel) { + const auto depth = + (cast(pixel[Color::Red]) + + (cast(pixel[Color::Green]) * 256.0f) + + (cast(pixel[Color::Blue]) * 256.0f * 256.0f)); + return depth / cast(256 * 256 * 256 - 1); + } + + static float logdepth(float depth) { + return 1.0f + std::log(depth) / 5.70378f; + } + + static float clamp(float value) { + return std::max(std::min(value, 1.0f), 0.0f); + } + + static void copy_to_pixel(float depth, boost::gil::rgb8_pixel_t &pixel) { + const auto grayscale = Color(static_cast(255.0f * depth)); + grayscale.copy_to_pixel(pixel); + } + +} // namespace detail + +struct depth_pixel_converter { + void operator()(boost::gil::rgb8_pixel_t &pixel) const { + using namespace detail; + copy_to_pixel(normalized_depth(pixel), pixel); + } +}; + +struct logarithmic_depth_pixel_converter { + void operator()(boost::gil::rgb8_pixel_t &pixel) const { + using namespace detail; + const auto depth = clamp(logdepth(normalized_depth(pixel))); + copy_to_pixel(depth, pixel); + } +}; + +} // namespace image_converter diff --git a/Util/ImageConverter/image_converter b/Util/ImageConverter/image_converter new file mode 100755 index 000000000..c45b630e3 Binary files /dev/null and b/Util/ImageConverter/image_converter differ diff --git a/Util/ImageConverter/image_converter.h b/Util/ImageConverter/image_converter.h new file mode 100644 index 000000000..cca50bb9d --- /dev/null +++ b/Util/ImageConverter/image_converter.h @@ -0,0 +1,9 @@ +// MIT License. Copyright (c) 2017 Computer Vision Center (CVC) at the +// Universitat Autonoma de Barcelona (UAB), and the INTEL Visual Computing Lab. + +#pragma once + +#include "image_converter_types.h" +#include "image_io.h" +#include "depth_pixel_converter.h" +#include "label_pixel_converter.h" diff --git a/Util/ImageConverter/image_converter_types.h b/Util/ImageConverter/image_converter_types.h new file mode 100644 index 000000000..03cda3fd6 --- /dev/null +++ b/Util/ImageConverter/image_converter_types.h @@ -0,0 +1,54 @@ +// MIT License. Copyright (c) 2017 Computer Vision Center (CVC) at the +// Universitat Autonoma de Barcelona (UAB), and the INTEL Visual Computing Lab. + +#pragma once + +#include +#include + +#include + +namespace image_converter { + + // =========================================================================== + // -- Basic types ------------------------------------------------------------ + // =========================================================================== + + using uint8 = uint8_t; + + using uint32 = uint_fast32_t; + + // =========================================================================== + // -- Color ------------------------------------------------------------------ + // =========================================================================== + + struct Color { + enum Channels { + Red, + Green, + Blue, + NUMBER_OF_CHANNELS + }; + + constexpr Color(uint8 R, uint8 G, uint8 B) : data{R, G, B} {} + + constexpr explicit Color(uint8 Grey) : data{Grey, Grey, Grey} {} + + constexpr const uint8 &operator[](uint_fast8_t i) const { + return data[i]; + } + + constexpr uint8 &operator[](uint_fast8_t i) { + return data[i]; + } + + void copy_to_pixel(boost::gil::rgb8_pixel_t &pixel) const { + pixel[Red] = data[Red]; + pixel[Green] = data[Green]; + pixel[Blue] = data[Blue]; + } + + uint8 data[NUMBER_OF_CHANNELS]; + }; + +} // namespace image_converter diff --git a/Util/ImageConverter/image_io.h b/Util/ImageConverter/image_io.h new file mode 100644 index 000000000..93b2759cf --- /dev/null +++ b/Util/ImageConverter/image_io.h @@ -0,0 +1,215 @@ +// MIT License. Copyright (c) 2017 Computer Vision Center (CVC) at the +// Universitat Autonoma de Barcelona (UAB), and the INTEL Visual Computing Lab. + +#pragma once + +#include + +#if __has_include("jpeglib.h") +# define IMAGE_CONVERTER_WITH_JPEG_SUPPORT true +# include +#else +# define IMAGE_CONVERTER_WITH_JPEG_SUPPORT false +#endif + +#if __has_include("png.h") +# define IMAGE_CONVERTER_WITH_PNG_SUPPORT true +# ifndef png_infopp_NULL +# define png_infopp_NULL (png_infopp)NULL +# endif // png_infopp_NULL +# ifndef int_p_NULL +# define int_p_NULL (int*)NULL +# endif // int_p_NULL +# include +#else +# define IMAGE_CONVERTER_WITH_PNG_SUPPORT false +#endif + +#if __has_include("tiffio.h") +# define IMAGE_CONVERTER_WITH_TIFF_SUPPORT true +# include +#else +# define IMAGE_CONVERTER_WITH_TIFF_SUPPORT false +#endif + +static_assert( + IMAGE_CONVERTER_WITH_JPEG_SUPPORT || + IMAGE_CONVERTER_WITH_PNG_SUPPORT || + IMAGE_CONVERTER_WITH_TIFF_SUPPORT, + "No image format supported, please install at least one of libjpeg, libpng, libtiff"); + +namespace image_converter { + + // =========================================================================== + // -- Format support checks -------------------------------------------------- + // =========================================================================== + + constexpr bool has_jpeg_support() { + return IMAGE_CONVERTER_WITH_JPEG_SUPPORT; + } + + constexpr bool has_png_support() { + return IMAGE_CONVERTER_WITH_PNG_SUPPORT; + } + + constexpr bool has_tiff_support() { + return IMAGE_CONVERTER_WITH_TIFF_SUPPORT; + } + + // =========================================================================== + // -- readers and writers ---------------------------------------------------- + // =========================================================================== + +namespace detail { + + struct jpeg_reader { +#if IMAGE_CONVERTER_WITH_JPEG_SUPPORT + template + static void read_image(const char *in_filename, IMAGE &image) { + static_assert(has_jpeg_support(), "JPEG not supported"); + boost::gil::jpeg_read_image(in_filename, image); + } +#endif // IMAGE_CONVERTER_WITH_JPEG_SUPPORT + }; + + struct jpeg_writer { +#if IMAGE_CONVERTER_WITH_JPEG_SUPPORT + template + static void write_view(const char *out_filename, const VIEW &view) { + static_assert(has_jpeg_support(), "JPEG not supported"); + boost::gil::jpeg_write_view(out_filename, view); + } +#endif // IMAGE_CONVERTER_WITH_JPEG_SUPPORT + }; + + struct png_reader { +#if IMAGE_CONVERTER_WITH_PNG_SUPPORT + template + static void read_image(const char *in_filename, IMAGE &image) { + static_assert(has_png_support(), "PNG not supported"); + boost::gil::png_read_and_convert_image(in_filename, image); + } +#endif // IMAGE_CONVERTER_WITH_PNG_SUPPORT + }; + + struct png_writer { +#if IMAGE_CONVERTER_WITH_PNG_SUPPORT + template + static void write_view(const char *out_filename, const VIEW &view) { + static_assert(has_png_support(), "PNG not supported"); + boost::gil::png_write_view(out_filename, view); + } +#endif // IMAGE_CONVERTER_WITH_PNG_SUPPORT + }; + + struct tiff_reader { +#if IMAGE_CONVERTER_WITH_TIFF_SUPPORT + template + static void read_image(const char *in_filename, IMAGE &image) { + static_assert(has_tiff_support(), "TIFF not supported"); + boost::gil::tiff_read_and_convert_image(in_filename, image); + } +#endif // IMAGE_CONVERTER_WITH_TIFF_SUPPORT + }; + + struct tiff_writer { +#if IMAGE_CONVERTER_WITH_TIFF_SUPPORT + template + static void write_view(const char *out_filename, const VIEW &view) { + static_assert(has_tiff_support(), "TIFF not supported"); + boost::gil::tiff_write_view(out_filename, view); + } +#endif // IMAGE_CONVERTER_WITH_TIFF_SUPPORT + }; + +} // namespace detail + + // =========================================================================== + // -- image_io definitions --------------------------------------------------- + // =========================================================================== + +namespace detail { + + template + struct image_io; + + template + struct image_io { + constexpr static bool is_supported = false; + }; + + template + struct image_io { + constexpr static bool is_supported = true; + using reader_type = READER; + using writer_type = WRITER; + }; + +} // namespace detail + + struct jpeg_io : public detail::image_io {}; + + struct png_io : public detail::image_io {}; + + struct tiff_io : public detail::image_io {}; + + // =========================================================================== + // -- any_image_io ----------------------------------------------------------- + // =========================================================================== + + // =========================================================================== + // -- image_file ------------------------------------------------------------- + // =========================================================================== + + template < + typename IO_READER, + typename IO_WRITER=IO_READER, + typename IMAGE=boost::gil::rgb8_image_t> + class image_file { + public: + + static_assert(IO_READER::is_supported, "I/O format not supported!"); + + using image_type = IMAGE; + using reader_type = typename IO_READER::reader_type; + using writer_type = typename IO_WRITER::writer_type; + + explicit image_file(const char *in_filename) { + reader_type::read_image(in_filename, _image); + } + + explicit image_file(const std::string &in_filename) : + image_file(in_filename.c_str()) {} + + auto view() { + return boost::gil::view(_image); + } + + auto view() const { + return boost::gil::const_view(_image); + } + + template + void apply(PIXEL_CONVERTER pixel_converter) { + for (auto &pixel : view()) { + pixel_converter(pixel); + } + } + + template + void write(const char *out_filename) const { + static_assert(OTHER_FORMAT::is_supported, "I/O format not supported!"); + OTHER_FORMAT::writer_type::write_view(out_filename, view()); + } + + template + void write(const std::string &out_filename) const { + write(out_filename.c_str()); + } + + private: + + image_type _image; + }; + +} // namespace image_converter diff --git a/Util/ImageConverter/label_pixel_converter.h b/Util/ImageConverter/label_pixel_converter.h new file mode 100644 index 000000000..3677906fc --- /dev/null +++ b/Util/ImageConverter/label_pixel_converter.h @@ -0,0 +1,39 @@ +// MIT License. Copyright (c) 2017 Computer Vision Center (CVC) at the +// Universitat Autonoma de Barcelona (UAB), and the INTEL Visual Computing Lab. + +#pragma once + +#include "image_converter_types.h" + +namespace image_converter { +namespace detail { + + // Someday this will be in a config file or something. + constexpr static Color LABEL_COLOR_MAP[] = { + { 0u, 0u, 0u}, // None = 0u, + { 70u, 70u, 70u}, // Buildings = 1u, + {190u, 153u, 153u}, // Fences = 2u, + {250u, 170u, 160u}, // Other = 3u, + {220u, 20u, 60u}, // Pedestrians = 4u, + {153u, 153u, 153u}, // Poles = 5u, + {153u, 153u, 153u}, // RoadLines = 6u, + {128u, 64u, 128u}, // Roads = 7u, + {244u, 35u, 232u}, // Sidewalks = 8u, + {107u, 142u, 35u}, // Vegetation = 9u, + { 0u, 0u, 142u}, // Vehicles = 10u, + {102u, 102u, 156u}, // Walls = 11u, + {220u, 220u, 0u} // TrafficSigns = 12u, + }; + +} // namespace detail + +struct label_pixel_converter { + void operator()(boost::gil::rgb8_pixel_t &pixel) const { + using namespace detail; + constexpr auto size = sizeof(LABEL_COLOR_MAP)/sizeof(*LABEL_COLOR_MAP); + const auto index = pixel[Color::Red] % size; + LABEL_COLOR_MAP[index].copy_to_pixel(pixel); + } +}; + +} // namespace image_converter diff --git a/Util/ImageConverter/main.cpp b/Util/ImageConverter/main.cpp new file mode 100644 index 000000000..23af64282 --- /dev/null +++ b/Util/ImageConverter/main.cpp @@ -0,0 +1,152 @@ +// MIT License. Copyright (c) 2017 Computer Vision Center (CVC) at the +// Universitat Autonoma de Barcelona (UAB), and the INTEL Visual Computing Lab. + +#include +#include +#include +#include +#include + +#include +#include + +#include "image_converter.h" + +enum MainFunctionReturnValues { + Success, + InvalidArgument, + UnknownException +}; + +namespace fs = boost::filesystem; + +using pixel_converter_function = std::function; + +static pixel_converter_function get_pixel_converter(const std::string &name) { + if (name == "semseg") { + return image_converter::label_pixel_converter(); + } else if (name == "depth") { + return image_converter::depth_pixel_converter(); + } else if (name == "logdepth") { + return image_converter::logarithmic_depth_pixel_converter(); + } else { + throw std::invalid_argument("invalid converter, please choose \"semseg\", \"depth\", or \"logdepth\""); + } +} + +// Match filepath with a regular expression (case insensitive). +static bool match(const std::string &filepath, const std::string ®ex) { + return std::regex_match( + filepath, + std::regex(regex, std::regex_constants::icase)); +} + +// Load image, apply pixel converter, and save it. +template +static void parse_image( + const std::string &in_filename, + const std::string &out_filename, + pixel_converter_function converter) { + image_converter::image_file file_io(in_filename); + file_io.apply(converter); + file_io.write(out_filename); +} + +// Determine the file format and parse it accordingly. +static void parse_any_image( + const std::string &in_filename, + const std::string &out_filename, + pixel_converter_function converter) { + namespace ic = image_converter; + try { + if (ic::has_png_support() && match(in_filename, ".*\\.png$")) { + parse_image(in_filename, out_filename, converter); + } else if (ic::has_jpeg_support() && match(in_filename, ".*\\.(jpg|jpeg)$")) { + parse_image(in_filename, out_filename, converter); + } else if (ic::has_tiff_support() && match(in_filename, ".*\\.tiff$")) { + parse_image(in_filename, out_filename, converter); + } + } catch (const std::exception &e) { + std::cerr << "exception thrown parsing file \"" << in_filename << "\"\n" << e.what() << std::endl; + } +} + +// Parse in parallel every regular file in input_folder. +static void do_the_thing( + const fs::path &input_folder, + const fs::path &output_folder, + const pixel_converter_function converter) { + const std::vector entries{ + fs::directory_iterator(input_folder), + fs::directory_iterator()}; + std::cout << "parsing " << entries.size() << " files in folder\n"; + +#pragma omp parallel for + for (auto i = 0u; i < entries.size(); ++i) { + const auto &entry = entries[i]; + if (fs::is_regular_file(entry.status())) { + const auto &in_path = entry.path(); + const auto &out_path = output_folder / in_path.filename(); + parse_any_image(in_path.string(), out_path.string(), converter); + } + } +} + +int main(int argc, char *argv[]) { + try { + std::string converter_name; + fs::path input_folder; + fs::path output_folder; + + // Fill program options. + namespace po = boost::program_options; + po::options_description desc("Allowed options"); + desc.add_options() + ("help,h", "produce help message") + ("converter,c", po::value(&converter_name)->required(), "converter (semseg or depth or logdepth)") + ("input-folder,i", po::value(&input_folder)->default_value("."), "input folder containing images") + ("output-folder,o", po::value(&output_folder)->default_value("./converted_images"), "output folder to save converted images") + ; + + try { + + // Parse command-line args. + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + + // Print help and exit if requested. + if (vm.count("help")) { + std::cout << desc << "\n"; + return Success; + } + + // Throw if any argument is invalid. + po::notify(vm); + + // Check if input_folder exists. + if (!fs::is_directory(input_folder)) { + throw std::invalid_argument("not a folder: " + input_folder.string()); + } + + // Create output_folder if it doesn't exist. + if (!fs::is_directory(output_folder) && !fs::create_directories(output_folder)) { + throw std::invalid_argument("cannot create folder: " + output_folder.string()); + } + + // Retrieve the pixel converter. + const pixel_converter_function converter = get_pixel_converter(converter_name); + + do_the_thing(input_folder, output_folder, converter); + + } catch (const std::invalid_argument &e) { + std::cerr << desc << "\n" << e.what() << std::endl; + return InvalidArgument; + } + + } catch (const std::exception &e) { + std::cerr << "unexpected exception thrown: " << e.what() << std::endl; + return UnknownException; + } + + return Success; +}