Add image converter
This commit is contained in:
parent
5991c71095
commit
f743608723
|
@ -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
|
|
@ -0,0 +1,7 @@
|
|||
Image Converter
|
||||
===============
|
||||
|
||||
Converts output images of depth and semantic segmentation to a prettier format.
|
||||
|
||||
make
|
||||
./image_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 <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
#include "image_converter_types.h"
|
||||
|
||||
namespace image_converter {
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
static inline float cast(T i) {
|
||||
return static_cast<float>(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<uint8>(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
|
Binary file not shown.
|
@ -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"
|
|
@ -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 <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include <boost/gil/typedefs.hpp>
|
||||
|
||||
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
|
|
@ -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 <boost/gil/image.hpp>
|
||||
|
||||
#if __has_include("jpeglib.h")
|
||||
# define IMAGE_CONVERTER_WITH_JPEG_SUPPORT true
|
||||
# include <boost/gil/extension/io/jpeg_io.hpp>
|
||||
#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 <boost/gil/extension/io/png_io.hpp>
|
||||
#else
|
||||
# define IMAGE_CONVERTER_WITH_PNG_SUPPORT false
|
||||
#endif
|
||||
|
||||
#if __has_include("tiffio.h")
|
||||
# define IMAGE_CONVERTER_WITH_TIFF_SUPPORT true
|
||||
# include <boost/gil/extension/io/tiff_io.hpp>
|
||||
#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 <typename IMAGE>
|
||||
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 <typename VIEW>
|
||||
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 <typename IMAGE>
|
||||
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 <typename VIEW>
|
||||
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 <typename IMAGE>
|
||||
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 <typename VIEW>
|
||||
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 <bool IS_SUPPORTED, typename READER, typename WRITER>
|
||||
struct image_io;
|
||||
|
||||
template <typename READER, typename WRITER>
|
||||
struct image_io<false, READER, WRITER> {
|
||||
constexpr static bool is_supported = false;
|
||||
};
|
||||
|
||||
template <typename READER, typename WRITER>
|
||||
struct image_io<true, READER, WRITER> {
|
||||
constexpr static bool is_supported = true;
|
||||
using reader_type = READER;
|
||||
using writer_type = WRITER;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
struct jpeg_io : public detail::image_io<has_jpeg_support(), detail::jpeg_reader, detail::jpeg_writer> {};
|
||||
|
||||
struct png_io : public detail::image_io<has_png_support(), detail::png_reader, detail::png_writer> {};
|
||||
|
||||
struct tiff_io : public detail::image_io<has_tiff_support(), detail::tiff_reader, detail::tiff_writer> {};
|
||||
|
||||
// ===========================================================================
|
||||
// -- 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 <typename PIXEL_CONVERTER>
|
||||
void apply(PIXEL_CONVERTER pixel_converter) {
|
||||
for (auto &pixel : view()) {
|
||||
pixel_converter(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OTHER_FORMAT=IO_WRITER>
|
||||
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 <typename OTHER_FORMAT=IO_WRITER>
|
||||
void write(const std::string &out_filename) const {
|
||||
write<OTHER_FORMAT>(out_filename.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
image_type _image;
|
||||
};
|
||||
|
||||
} // namespace image_converter
|
|
@ -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
|
|
@ -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 <cstdint>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include "image_converter.h"
|
||||
|
||||
enum MainFunctionReturnValues {
|
||||
Success,
|
||||
InvalidArgument,
|
||||
UnknownException
|
||||
};
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
using pixel_converter_function = std::function<void(boost::gil::rgb8_pixel_t &)>;
|
||||
|
||||
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 <typename IO>
|
||||
static void parse_image(
|
||||
const std::string &in_filename,
|
||||
const std::string &out_filename,
|
||||
pixel_converter_function converter) {
|
||||
image_converter::image_file<IO> 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<ic::png_io>(in_filename, out_filename, converter);
|
||||
} else if (ic::has_jpeg_support() && match(in_filename, ".*\\.(jpg|jpeg)$")) {
|
||||
parse_image<ic::jpeg_io>(in_filename, out_filename, converter);
|
||||
} else if (ic::has_tiff_support() && match(in_filename, ".*\\.tiff$")) {
|
||||
parse_image<ic::tiff_io>(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<fs::directory_entry> 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<std::string>(&converter_name)->required(), "converter (semseg or depth or logdepth)")
|
||||
("input-folder,i", po::value<fs::path>(&input_folder)->default_value("."), "input folder containing images")
|
||||
("output-folder,o", po::value<fs::path>(&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;
|
||||
}
|
Loading…
Reference in New Issue