Add image converter

This commit is contained in:
nsubiron 2017-09-19 18:46:29 +02:00
parent 5991c71095
commit f743608723
9 changed files with 551 additions and 0 deletions

View File

@ -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

View File

@ -0,0 +1,7 @@
Image Converter
===============
Converts output images of depth and semantic segmentation to a prettier format.
make
./image_converter -h

View File

@ -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.

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 &regex) {
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;
}