From 4ec6d6073e3287c5680e893245decc74799ee233 Mon Sep 17 00:00:00 2001 From: Manuel Gunther Date: Thu, 8 Sep 2016 11:27:30 -0600 Subject: [PATCH 1/5] Step 1: get image extension based on file content --- bob/io/image/include/bob.io.image/image.h | 89 ++++++++++++++++++++--- bob/io/image/main.cpp | 48 +++++++++--- 2 files changed, 114 insertions(+), 23 deletions(-) diff --git a/bob/io/image/include/bob.io.image/image.h b/bob/io/image/include/bob.io.image/image.h index a3e2561..75bc680 100644 --- a/bob/io/image/include/bob.io.image/image.h +++ b/bob/io/image/include/bob.io.image/image.h @@ -29,12 +29,75 @@ #include #include #include - +#include +#include namespace bob { namespace io { namespace image { -inline bool is_color_image(const std::string& filename){ - std::string extension = boost::filesystem::path(filename).extension().string(); +inline const std::string& get_correct_image_extension(const std::string& image_name){ + // global map of known magic numbers + // these numbers are based on: http://stackoverflow.com/questions/26350342/determining-the-extension-type-of-an-image-file-using-binary/26350431#26350431 + static std::map>> known_magic_numbers; + + // initialize list, if not done yet + if (known_magic_numbers.empty()){ + // BMP + known_magic_numbers[".bmp"].push_back({ 0x42, 0x4D }); + // NetPBM, see https://en.wikipedia.org/wiki/Netpbm_format + known_magic_numbers[".pbm"].push_back({ 0x50, 0x31 });// P1 + known_magic_numbers[".pbm"].push_back({ 0x50, 0x34 });// P4 + known_magic_numbers[".pgm"].push_back({ 0x50, 0x32 });// P2 + known_magic_numbers[".pgm"].push_back({ 0x50, 0x35 });// P5 + known_magic_numbers[".ppm"].push_back({ 0x50, 0x33 });// P3 + known_magic_numbers[".ppm"].push_back({ 0x50, 0x36 });// P6 + // TODO: what about P7? + +#ifdef HAVE_GIFLIB + // GIF + known_magic_numbers[".gif"].push_back({ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }); + known_magic_numbers[".gif"].push_back({ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }); +#endif // HAVE_GIFLIB +#ifdef HAVE_LIBPNG + // PNG + known_magic_numbers[".png"].push_back({ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }); +#endif // HAVE_LIB_PNG +#ifdef HAVE_LIBJPEG + // JPEG + known_magic_numbers[".jpg"].push_back({ 0xFF, 0xD8, 0xFF }); + known_magic_numbers[".jpg"].push_back({ 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20 }); +#endif // HAVE_LIBJPEG +#ifdef HAVE_LIBTIFF + // TIFF + known_magic_numbers[".tiff"].push_back({ 0x0C, 0xED }); + known_magic_numbers[".tiff"].push_back({ 0x49, 0x20, 0x49 }); + known_magic_numbers[".tiff"].push_back({ 0x49, 0x49, 0x2A, 0x00 }); + known_magic_numbers[".tiff"].push_back({ 0x4D, 0x4D, 0x00, 0x2A }); + known_magic_numbers[".tiff"].push_back({ 0x4D, 0x4D, 0x00, 0x2B }); +#endif // HAVE_LIBTIFF + } + + // read first 8 bytes from file + static uint8_t image_bytes[8]; + std::ifstream f(image_name); + if (!f) throw std::runtime_error("The given image '" + image_name + "' could not be opened for reading"); + f.read(reinterpret_cast(image_bytes), 8); + + // iterate over all extensions + for (auto eit = known_magic_numbers.begin(); eit != known_magic_numbers.end(); ++eit){ + // iterate over all magic bytes + for (auto mit = eit->second.begin(); mit != eit->second.end(); ++mit){ + // check magic number + if (std::equal(mit->begin(), mit->end(), image_bytes)) + return eit->first; + } + } + + throw std::runtime_error("The given image '" + image_name + "' does not contain an image of a known type"); +} + +inline bool is_color_image(const std::string& filename, std::string extension=""){ + if (extension.empty()) + extension = boost::filesystem::path(filename).extension().string(); boost::algorithm::to_lower(extension); if (extension == ".bmp") return true; #ifdef HAVE_GIFLIB @@ -55,8 +118,9 @@ inline bool is_color_image(const std::string& filename){ throw std::runtime_error("The filename extension '" + extension + "' is not known"); } -inline blitz::Array read_color_image(const std::string& filename){ - std::string extension = boost::filesystem::path(filename).extension().string(); +inline blitz::Array read_color_image(const std::string& filename, std::string extension=""){ + if (extension.empty()) + extension = boost::filesystem::path(filename).extension().string(); boost::algorithm::to_lower(extension); if (extension == ".bmp") return read_bmp(filename); #ifdef HAVE_GIFLIB @@ -76,8 +140,9 @@ inline blitz::Array read_color_image(const std::string& filename){ throw std::runtime_error("The filename extension '" + extension + "' is not known or not supported for color images"); } -blitz::Array read_gray_image(const std::string& filename){ - std::string extension = boost::filesystem::path(filename).extension().string(); +blitz::Array read_gray_image(const std::string& filename, std::string extension=""){ + if (extension.empty()) + extension = boost::filesystem::path(filename).extension().string(); boost::algorithm::to_lower(extension); #ifdef HAVE_LIBPNG if (extension == ".png") return read_png(filename); @@ -95,8 +160,9 @@ blitz::Array read_gray_image(const std::string& filename){ } -void write_color_image(const blitz::Array& image, const std::string& filename){ - std::string extension = boost::filesystem::path(filename).extension().string(); +void write_color_image(const blitz::Array& image, const std::string& filename, std::string extension=""){ + if (extension.empty()) + extension = boost::filesystem::path(filename).extension().string(); boost::algorithm::to_lower(extension); if (extension == ".bmp") return write_bmp(image, filename); // this will only work for T=uint8_t #ifdef HAVE_GIFLIB @@ -116,8 +182,9 @@ void write_color_image(const blitz::Array& image, const std::string& throw std::runtime_error("The filename extension '" + extension + "' is not known or not supported for color images"); } -void write_gray_image(const blitz::Array& image, const std::string& filename){ - std::string extension = boost::filesystem::path(filename).extension().string(); +void write_gray_image(const blitz::Array& image, const std::string& filename, std::string extension=""){ + if (extension.empty()) + extension = boost::filesystem::path(filename).extension().string(); boost::algorithm::to_lower(extension); #ifdef HAVE_LIBPNG if (extension == ".png") return write_png(image, filename); diff --git a/bob/io/image/main.cpp b/bob/io/image/main.cpp index da93c13..b0471de 100644 --- a/bob/io/image/main.cpp +++ b/bob/io/image/main.cpp @@ -59,8 +59,12 @@ BOB_TRY // BMP; only color images are supported boost::filesystem::path bmp(tempdir); bmp /= std::string("color.bmp"); bob::io::image::write_color_image(color_image, bmp.string()); + if (bob::io::image::get_correct_image_extension(bmp.string()) != ".bmp") + throw std::runtime_error("BMP image type check did not succeed, check " + bmp.string()); + if (!bob::io::image::is_color_image(bmp.string())) + throw std::runtime_error("BMP image " + bmp.string() + " is not color as expected"); - blitz::Array color_bmp = bob::io::image::read_color_image(bmp.string()); + blitz::Array color_bmp = bob::io::image::read_color_image(bmp.string(), ".bmp"); if (blitz::any(blitz::abs(color_image - color_bmp) > 0)) throw std::runtime_error("BMP image IO did not succeed, check " + bmp.string()); @@ -69,8 +73,12 @@ BOB_TRY // GIF; only color images are supported boost::filesystem::path gif(tempdir); gif /= std::string("color.gif"); bob::io::image::write_color_image(color_image, gif.string()); + if (bob::io::image::get_correct_image_extension(gif.string()) != ".gif") + throw std::runtime_error("GIF image type check did not succeed, check " + gif.string()); + if (!bob::io::image::is_color_image(gif.string(), ".gif")) + throw std::runtime_error("GIF image " + gif.string() + " is not color as expected"); - blitz::Array color_gif = bob::io::image::read_color_image(gif.string()); + blitz::Array color_gif = bob::io::image::read_color_image(gif.string(), ".gif"); if (blitz::any(blitz::abs(color_image - color_gif) > 8)) // TODO: why is GIF not lossless? throw std::runtime_error("GIF image IO did not succeed, check " + gif.string()); #endif @@ -78,19 +86,23 @@ BOB_TRY // NetPBM boost::filesystem::path pgm(tempdir); pgm /= std::string("gray.pgm"); bob::io::image::write_gray_image(gray_image, pgm.string()); - if (bob::io::image::is_color_p_m(pgm.string())) + if (bob::io::image::get_correct_image_extension(pgm.string()) != ".pgm") + throw std::runtime_error("PGM image type check did not succeed, check " + pgm.string()); + if (bob::io::image::is_color_p_m(pgm.string()) || bob::io::image::is_color_image(pgm.string(), ".pgm")) throw std::runtime_error("PGM image " + pgm.string() + " is not gray as expected"); - blitz::Array gray_pgm = bob::io::image::read_gray_image(pgm.string()); + blitz::Array gray_pgm = bob::io::image::read_gray_image(pgm.string(), ".pgm"); if (blitz::any(blitz::abs(gray_image - gray_pgm) > 0)) throw std::runtime_error("PGM image IO did not succeed, check " + pgm.string()); boost::filesystem::path ppm(tempdir); ppm /= std::string("color.ppm"); bob::io::image::write_color_image(color_image, ppm.string()); - if (!bob::io::image::is_color_p_m(ppm.string())) + if (bob::io::image::get_correct_image_extension(ppm.string()) != ".ppm") + throw std::runtime_error("PPM image type check did not succeed, check " + ppm.string()); + if (!bob::io::image::is_color_p_m(ppm.string()) || !bob::io::image::is_color_image(ppm.string())) throw std::runtime_error("PPM image " + ppm.string() + " is not color as expected"); - blitz::Array color_ppm = bob::io::image::read_color_image(ppm.string()); + blitz::Array color_ppm = bob::io::image::read_color_image(ppm.string(), ".ppm"); if (blitz::any(blitz::abs(color_image - color_ppm) > 0)) throw std::runtime_error("PPM image IO did not succeed, check " + ppm.string()); @@ -99,7 +111,9 @@ BOB_TRY // JPEG boost::filesystem::path jpeg_gray(tempdir); jpeg_gray /= std::string("gray.jpg"); bob::io::image::write_gray_image(gray_image, jpeg_gray.string()); - if (bob::io::image::is_color_image(jpeg_gray.string())) + if (bob::io::image::get_correct_image_extension(jpeg_gray.string()) != ".jpg") + throw std::runtime_error("JPEG image type check did not succeed, check " + jpeg_gray.string()); + if (bob::io::image::is_color_image(jpeg_gray.string()) || bob::io::image::is_color_image(jpeg_gray.string())) throw std::runtime_error("JPEG image " + jpeg_gray.string() + " is not gray as expected"); blitz::Array gray_jpeg = bob::io::image::read_gray_image(jpeg_gray.string()); @@ -108,7 +122,9 @@ BOB_TRY boost::filesystem::path jpeg_color(tempdir); jpeg_color /= std::string("color.jpg"); bob::io::image::write_color_image(color_image, jpeg_color.string()); - if (!bob::io::image::is_color_image(jpeg_color.string())) + if (bob::io::image::get_correct_image_extension(jpeg_color.string()) != ".jpg") + throw std::runtime_error("JPEG image type check did not succeed, check " + jpeg_color.string()); + if (!bob::io::image::is_color_image(jpeg_color.string()) || !bob::io::image::is_color_image(jpeg_color.string(), ".jpeg")) throw std::runtime_error("JPEG image " + jpeg_color.string() + " is not color as expected"); blitz::Array color_jpeg = bob::io::image::read_color_image(jpeg_color.string()); @@ -120,7 +136,9 @@ BOB_TRY // PNG boost::filesystem::path png_gray(tempdir); png_gray /= std::string("gray.png"); bob::io::image::write_gray_image(gray_image, png_gray.string()); - if (bob::io::image::is_color_image(png_gray.string())) + if (bob::io::image::get_correct_image_extension(png_gray.string()) != ".png") + throw std::runtime_error("PNG image type check did not succeed, check " + png_gray.string()); + if (bob::io::image::is_color_image(png_gray.string()) || bob::io::image::is_color_image(png_gray.string(), ".png")) throw std::runtime_error("PNG image " + png_gray.string() + " is not gray as expected"); blitz::Array gray_png = bob::io::image::read_gray_image(png_gray.string()); @@ -129,7 +147,9 @@ BOB_TRY boost::filesystem::path png_color(tempdir); png_color /= std::string("color.png"); bob::io::image::write_color_image(color_image, png_color.string()); - if (!bob::io::image::is_color_png(png_color.string())) + if (bob::io::image::get_correct_image_extension(png_color.string()) != ".png") + throw std::runtime_error("PNG image type check did not succeed, check " + png_color.string()); + if (!bob::io::image::is_color_png(png_color.string()) || !bob::io::image::is_color_image(png_color.string())) throw std::runtime_error("PNG image " + png_color.string() + " is not color as expected"); blitz::Array color_png = bob::io::image::read_color_image(png_color.string()); @@ -141,7 +161,9 @@ BOB_TRY // TIFF boost::filesystem::path tiff_gray(tempdir); tiff_gray /= std::string("gray.tiff"); bob::io::image::write_gray_image(gray_image, tiff_gray.string()); - if (bob::io::image::is_color_image(tiff_gray.string())) + if (bob::io::image::get_correct_image_extension(tiff_gray.string()) != ".tiff") + throw std::runtime_error("TIFF image type check did not succeed, check " + tiff_gray.string()); + if (bob::io::image::is_color_image(tiff_gray.string()) || bob::io::image::is_color_image(tiff_gray.string(), ".tif")) throw std::runtime_error("TIFF image " + tiff_gray.string() + " is not gray as expected"); blitz::Array gray_tiff = bob::io::image::read_gray_image(tiff_gray.string()); @@ -150,7 +172,9 @@ BOB_TRY boost::filesystem::path tiff_color(tempdir); tiff_color /= std::string("color.tiff"); bob::io::image::write_color_image(color_image, tiff_color.string()); - if (!bob::io::image::is_color_image(tiff_color.string())) + if (bob::io::image::get_correct_image_extension(tiff_color.string()) != ".tiff") + throw std::runtime_error("TIFF image type check did not succeed, check " + tiff_color.string()); + if (!bob::io::image::is_color_image(tiff_color.string()) || !bob::io::image::is_color_image(tiff_color.string(), ".tiff")) throw std::runtime_error("TIFF image " + tiff_color.string() + " is not color as expected"); blitz::Array color_tiff = bob::io::image::read_color_image(tiff_color.string()); -- GitLab From 554e4edd1a3b25664ab9ab96e3f4e7bbc5c1470f Mon Sep 17 00:00:00 2001 From: Manuel Gunther Date: Thu, 8 Sep 2016 11:55:38 -0600 Subject: [PATCH 2/5] Step 2: bind function to Python and use it instead of imghdr --- bob/io/image/__init__.py | 14 +++----------- bob/io/image/main.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/bob/io/image/__init__.py b/bob/io/image/__init__.py index 4081dc2..5dcb5a7 100644 --- a/bob/io/image/__init__.py +++ b/bob/io/image/__init__.py @@ -9,6 +9,8 @@ from . import _library from . import version from .version import module as __version__ +from ._library import * + import os def get_config(): @@ -17,13 +19,6 @@ def get_config(): import bob.extension return bob.extension.get_config(__name__, version.externals) -# fix imghdr's jpeg detection to use the first two bytes (according to https://en.wikipedia.org/wiki/List_of_file_signatures) -import imghdr -def _test_jpeg(h, f): - if h.startswith('\xff\xd8'): - return "jpeg" -imghdr.tests.append(_test_jpeg) - def load(filename, extension=None): """load(filename) -> image @@ -54,10 +49,7 @@ def load(filename, extension=None): f = bob.io.base.File(filename, 'r') else: if extension == 'auto': - extension = imghdr.what(filename) - if extension is None: - raise IOError("Could not detect the image type of file %s" % filename) - extension = "." + extension + extension = get_correct_image_extension(filename) f = bob.io.base.File(filename, 'r', extension) return f.read() diff --git a/bob/io/image/main.cpp b/bob/io/image/main.cpp index b0471de..7829742 100644 --- a/bob/io/image/main.cpp +++ b/bob/io/image/main.cpp @@ -187,6 +187,29 @@ BOB_CATCH_FUNCTION("_test_io", 0) } +static auto s_image_extension = bob::extension::FunctionDoc( + "get_correct_image_extension", + "Estimates the image type and return a corresponding extension based on file content", + "This function loads the first bytes of the given image, and matches it with known magic numbers of image files. " + "If a match is found, it returns the corresponding image extension (including the leading ``'.'`` that can be used, e.g., in :py:func:`bob.io.image.load`." +) +.add_prototype("image_name", "extension") +.add_parameter("image_name", "str", "The name (including path) of the image to check") +.add_return("extension", "str", "The extension of the image based on the file content") +; +static PyObject* image_extension(PyObject*, PyObject *args, PyObject* kwds) { +BOB_TRY + static char** kwlist = s_image_extension.kwlist(); + + const char* image_name; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &image_name)) return 0; + + return Py_BuildValue("s", bob::io::image::get_correct_image_extension(image_name).c_str()); + +BOB_CATCH_FUNCTION("get_correct_image_extension", 0) +} + + static PyMethodDef module_methods[] = { { s_test_io.name(), @@ -194,6 +217,12 @@ static PyMethodDef module_methods[] = { METH_VARARGS|METH_KEYWORDS, s_test_io.doc(), }, + { + s_image_extension.name(), + (PyCFunction)image_extension, + METH_VARARGS|METH_KEYWORDS, + s_image_extension.doc(), + }, {0} /* Sentinel */ }; -- GitLab From d61dcc30f6d0388e388c51008b8977c05b77c75e Mon Sep 17 00:00:00 2001 From: Manuel Gunther Date: Thu, 8 Sep 2016 12:24:27 -0600 Subject: [PATCH 3/5] Moved code into bob_io_image.so library to keep headers C++11-free --- bob/io/image/cpp/image.cpp | 98 ++++++++++++++++++++++ bob/io/image/include/bob.io.image/config.h | 2 +- bob/io/image/include/bob.io.image/image.h | 84 +------------------ bob/io/image/include/bob.io.image/jpeg.h | 2 +- bob/io/image/include/bob.io.image/netpbm.h | 2 +- bob/io/image/include/bob.io.image/png.h | 2 +- bob/io/image/include/bob.io.image/tiff.h | 2 +- setup.py | 1 + 8 files changed, 106 insertions(+), 87 deletions(-) create mode 100644 bob/io/image/cpp/image.cpp diff --git a/bob/io/image/cpp/image.cpp b/bob/io/image/cpp/image.cpp new file mode 100644 index 0000000..e56ccf4 --- /dev/null +++ b/bob/io/image/cpp/image.cpp @@ -0,0 +1,98 @@ +/** + * @date Thu Sep 8 12:05:08 MDT 2016 + * @author Manuel Gunther + * + * @brief Implements an generic image functionalities. + * + * Copyright (c) 2016, Regents of the University of Colorado on behalf of the University of Colorado Colorado Springs. + */ + +#include + +namespace bob { namespace io { namespace image { + +const std::string& get_correct_image_extension(const std::string& image_name){ + // global map of known magic numbers + // these numbers are based on: http://stackoverflow.com/questions/26350342/determining-the-extension-type-of-an-image-file-using-binary/26350431#26350431 + static std::map>> known_magic_numbers; + + // initialize list, if not done yet + if (known_magic_numbers.empty()){ + // BMP + known_magic_numbers[".bmp"].push_back({ 0x42, 0x4D }); + // NetPBM, see https://en.wikipedia.org/wiki/Netpbm_format + known_magic_numbers[".pbm"].push_back({ 0x50, 0x31 });// P1 + known_magic_numbers[".pbm"].push_back({ 0x50, 0x34 });// P4 + known_magic_numbers[".pgm"].push_back({ 0x50, 0x32 });// P2 + known_magic_numbers[".pgm"].push_back({ 0x50, 0x35 });// P5 + known_magic_numbers[".ppm"].push_back({ 0x50, 0x33 });// P3 + known_magic_numbers[".ppm"].push_back({ 0x50, 0x36 });// P6 + // TODO: what about P7? + +#ifdef HAVE_GIFLIB + // GIF + known_magic_numbers[".gif"].push_back({ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }); + known_magic_numbers[".gif"].push_back({ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }); +#endif // HAVE_GIFLIB +#ifdef HAVE_LIBPNG + // PNG + known_magic_numbers[".png"].push_back({ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }); +#endif // HAVE_LIB_PNG +#ifdef HAVE_LIBJPEG + // JPEG + known_magic_numbers[".jpg"].push_back({ 0xFF, 0xD8, 0xFF }); + known_magic_numbers[".jpg"].push_back({ 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20 }); +#endif // HAVE_LIBJPEG +#ifdef HAVE_LIBTIFF + // TIFF + known_magic_numbers[".tiff"].push_back({ 0x0C, 0xED }); + known_magic_numbers[".tiff"].push_back({ 0x49, 0x20, 0x49 }); + known_magic_numbers[".tiff"].push_back({ 0x49, 0x49, 0x2A, 0x00 }); + known_magic_numbers[".tiff"].push_back({ 0x4D, 0x4D, 0x00, 0x2A }); + known_magic_numbers[".tiff"].push_back({ 0x4D, 0x4D, 0x00, 0x2B }); +#endif // HAVE_LIBTIFF + } + + // read first 8 bytes from file + static uint8_t image_bytes[8]; + std::ifstream f(image_name); + if (!f) throw std::runtime_error("The given image '" + image_name + "' could not be opened for reading"); + f.read(reinterpret_cast(image_bytes), 8); + + // iterate over all extensions + for (auto eit = known_magic_numbers.begin(); eit != known_magic_numbers.end(); ++eit){ + // iterate over all magic bytes + for (auto mit = eit->second.begin(); mit != eit->second.end(); ++mit){ + // check magic number + if (std::equal(mit->begin(), mit->end(), image_bytes)) + return eit->first; + } + } + + throw std::runtime_error("The given image '" + image_name + "' does not contain an image of a known type"); +} + +bool is_color_image(const std::string& filename, std::string extension){ + if (extension.empty()) + extension = boost::filesystem::path(filename).extension().string(); + boost::algorithm::to_lower(extension); + if (extension == ".bmp") return true; +#ifdef HAVE_GIFLIB + if (extension == ".gif") return true; +#endif +#ifdef HAVE_LIBPNG + if (extension == ".png") return is_color_png(filename); +#endif +#ifdef HAVE_LIBJPEG + if (extension == ".jpg" || extension == ".jpeg") return is_color_jpeg(filename); +#endif +#ifdef HAVE_LIBTIFF + if (extension == ".tif" || extension == ".tiff") return is_color_tiff(filename); +#endif + if (extension == ".pgm" || extension == ".pbm") return false; + if (extension == ".ppm") return true; + + throw std::runtime_error("The filename extension '" + extension + "' is not known"); +} + +} } } // namespaces diff --git a/bob/io/image/include/bob.io.image/config.h b/bob/io/image/include/bob.io.image/config.h index 0f43583..6b2fe3d 100644 --- a/bob/io/image/include/bob.io.image/config.h +++ b/bob/io/image/include/bob.io.image/config.h @@ -9,7 +9,7 @@ #define BOB_IO_IMAGE_CONFIG_H /* Macros that define versions and important names */ -#define BOB_IO_IMAGE_API_VERSION 0x0200 +#define BOB_IO_IMAGE_API_VERSION 0x0201 #ifdef BOB_IMPORT_VERSION diff --git a/bob/io/image/include/bob.io.image/image.h b/bob/io/image/include/bob.io.image/image.h index 75bc680..49eae4e 100644 --- a/bob/io/image/include/bob.io.image/image.h +++ b/bob/io/image/include/bob.io.image/image.h @@ -34,89 +34,9 @@ namespace bob { namespace io { namespace image { -inline const std::string& get_correct_image_extension(const std::string& image_name){ - // global map of known magic numbers - // these numbers are based on: http://stackoverflow.com/questions/26350342/determining-the-extension-type-of-an-image-file-using-binary/26350431#26350431 - static std::map>> known_magic_numbers; - - // initialize list, if not done yet - if (known_magic_numbers.empty()){ - // BMP - known_magic_numbers[".bmp"].push_back({ 0x42, 0x4D }); - // NetPBM, see https://en.wikipedia.org/wiki/Netpbm_format - known_magic_numbers[".pbm"].push_back({ 0x50, 0x31 });// P1 - known_magic_numbers[".pbm"].push_back({ 0x50, 0x34 });// P4 - known_magic_numbers[".pgm"].push_back({ 0x50, 0x32 });// P2 - known_magic_numbers[".pgm"].push_back({ 0x50, 0x35 });// P5 - known_magic_numbers[".ppm"].push_back({ 0x50, 0x33 });// P3 - known_magic_numbers[".ppm"].push_back({ 0x50, 0x36 });// P6 - // TODO: what about P7? +const std::string& get_correct_image_extension(const std::string& image_name); -#ifdef HAVE_GIFLIB - // GIF - known_magic_numbers[".gif"].push_back({ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }); - known_magic_numbers[".gif"].push_back({ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }); -#endif // HAVE_GIFLIB -#ifdef HAVE_LIBPNG - // PNG - known_magic_numbers[".png"].push_back({ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }); -#endif // HAVE_LIB_PNG -#ifdef HAVE_LIBJPEG - // JPEG - known_magic_numbers[".jpg"].push_back({ 0xFF, 0xD8, 0xFF }); - known_magic_numbers[".jpg"].push_back({ 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20 }); -#endif // HAVE_LIBJPEG -#ifdef HAVE_LIBTIFF - // TIFF - known_magic_numbers[".tiff"].push_back({ 0x0C, 0xED }); - known_magic_numbers[".tiff"].push_back({ 0x49, 0x20, 0x49 }); - known_magic_numbers[".tiff"].push_back({ 0x49, 0x49, 0x2A, 0x00 }); - known_magic_numbers[".tiff"].push_back({ 0x4D, 0x4D, 0x00, 0x2A }); - known_magic_numbers[".tiff"].push_back({ 0x4D, 0x4D, 0x00, 0x2B }); -#endif // HAVE_LIBTIFF - } - - // read first 8 bytes from file - static uint8_t image_bytes[8]; - std::ifstream f(image_name); - if (!f) throw std::runtime_error("The given image '" + image_name + "' could not be opened for reading"); - f.read(reinterpret_cast(image_bytes), 8); - - // iterate over all extensions - for (auto eit = known_magic_numbers.begin(); eit != known_magic_numbers.end(); ++eit){ - // iterate over all magic bytes - for (auto mit = eit->second.begin(); mit != eit->second.end(); ++mit){ - // check magic number - if (std::equal(mit->begin(), mit->end(), image_bytes)) - return eit->first; - } - } - - throw std::runtime_error("The given image '" + image_name + "' does not contain an image of a known type"); -} - -inline bool is_color_image(const std::string& filename, std::string extension=""){ - if (extension.empty()) - extension = boost::filesystem::path(filename).extension().string(); - boost::algorithm::to_lower(extension); - if (extension == ".bmp") return true; -#ifdef HAVE_GIFLIB - if (extension == ".gif") return true; -#endif -#ifdef HAVE_LIBPNG - if (extension == ".png") return is_color_png(filename); -#endif -#ifdef HAVE_LIBJPEG - if (extension == ".jpg" || extension == ".jpeg") return is_color_jpeg(filename); -#endif -#ifdef HAVE_LIBTIFF - if (extension == ".tif" || extension == ".tiff") return is_color_tiff(filename); -#endif - if (extension == ".pgm" || extension == ".pbm") return false; - if (extension == ".ppm") return true; - - throw std::runtime_error("The filename extension '" + extension + "' is not known"); -} +bool is_color_image(const std::string& filename, std::string extension=""); inline blitz::Array read_color_image(const std::string& filename, std::string extension=""){ if (extension.empty()) diff --git a/bob/io/image/include/bob.io.image/jpeg.h b/bob/io/image/include/bob.io.image/jpeg.h index 576ffa5..e71102b 100644 --- a/bob/io/image/include/bob.io.image/jpeg.h +++ b/bob/io/image/include/bob.io.image/jpeg.h @@ -88,7 +88,7 @@ namespace bob { namespace io { namespace image { static std::string s_codecname; }; - bool is_color_jpeg(const std::string& filename){ + inline bool is_color_jpeg(const std::string& filename){ JPEGFile jpeg(filename.c_str(), 'r'); return jpeg.type().nd == 3; } diff --git a/bob/io/image/include/bob.io.image/netpbm.h b/bob/io/image/include/bob.io.image/netpbm.h index 6d18ed2..0003f7d 100644 --- a/bob/io/image/include/bob.io.image/netpbm.h +++ b/bob/io/image/include/bob.io.image/netpbm.h @@ -123,7 +123,7 @@ namespace bob { namespace io { namespace image { } - bool is_color_p_m(const std::string& filename){ + inline bool is_color_p_m(const std::string& filename){ NetPBMFile p_m(filename.c_str(), 'r'); return p_m.type().nd == 3; } diff --git a/bob/io/image/include/bob.io.image/png.h b/bob/io/image/include/bob.io.image/png.h index 0bc0909..53733f0 100644 --- a/bob/io/image/include/bob.io.image/png.h +++ b/bob/io/image/include/bob.io.image/png.h @@ -88,7 +88,7 @@ namespace bob { namespace io { namespace image { static std::string s_codecname; }; - bool is_color_png(const std::string& filename){ + inline bool is_color_png(const std::string& filename){ PNGFile png(filename.c_str(), 'r'); return png.type().nd == 3; } diff --git a/bob/io/image/include/bob.io.image/tiff.h b/bob/io/image/include/bob.io.image/tiff.h index 0425403..923333d 100644 --- a/bob/io/image/include/bob.io.image/tiff.h +++ b/bob/io/image/include/bob.io.image/tiff.h @@ -88,7 +88,7 @@ namespace bob { namespace io { namespace image { static std::string s_codecname; }; - bool is_color_tiff(const std::string& filename){ + inline bool is_color_tiff(const std::string& filename){ TIFFFile tiff(filename.c_str(), 'r'); return tiff.type().nd == 3; } diff --git a/setup.py b/setup.py index 7d89f18..f41b9de 100644 --- a/setup.py +++ b/setup.py @@ -397,6 +397,7 @@ setup( "bob/io/image/cpp/bmp.cpp", "bob/io/image/cpp/pnmio.cpp", "bob/io/image/cpp/netpbm.cpp", + "bob/io/image/cpp/image.cpp", ], packages = packages, boost_modules = boost_modules, -- GitLab From ce3adfe98bcd2897961b5bd9291ce8d15800c29a Mon Sep 17 00:00:00 2001 From: Manuel Gunther Date: Fri, 9 Sep 2016 09:54:03 -0600 Subject: [PATCH 4/5] Initialize known_magic_number static to avoid race conditions --- bob/io/image/cpp/image.cpp | 64 ++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/bob/io/image/cpp/image.cpp b/bob/io/image/cpp/image.cpp index e56ccf4..95b6328 100644 --- a/bob/io/image/cpp/image.cpp +++ b/bob/io/image/cpp/image.cpp @@ -11,48 +11,50 @@ namespace bob { namespace io { namespace image { -const std::string& get_correct_image_extension(const std::string& image_name){ - // global map of known magic numbers +static std::map>> _initialize_magic_numbers(){ // these numbers are based on: http://stackoverflow.com/questions/26350342/determining-the-extension-type-of-an-image-file-using-binary/26350431#26350431 - static std::map>> known_magic_numbers; - - // initialize list, if not done yet - if (known_magic_numbers.empty()){ - // BMP - known_magic_numbers[".bmp"].push_back({ 0x42, 0x4D }); - // NetPBM, see https://en.wikipedia.org/wiki/Netpbm_format - known_magic_numbers[".pbm"].push_back({ 0x50, 0x31 });// P1 - known_magic_numbers[".pbm"].push_back({ 0x50, 0x34 });// P4 - known_magic_numbers[".pgm"].push_back({ 0x50, 0x32 });// P2 - known_magic_numbers[".pgm"].push_back({ 0x50, 0x35 });// P5 - known_magic_numbers[".ppm"].push_back({ 0x50, 0x33 });// P3 - known_magic_numbers[".ppm"].push_back({ 0x50, 0x36 });// P6 - // TODO: what about P7? + std::map>> magic_numbers; + // BMP + magic_numbers[".bmp"].push_back({ 0x42, 0x4D }); + // NetPBM, see https://en.wikipedia.org/wiki/Netpbm_format + magic_numbers[".pbm"].push_back({ 0x50, 0x31 });// P1 + magic_numbers[".pbm"].push_back({ 0x50, 0x34 });// P4 + magic_numbers[".pgm"].push_back({ 0x50, 0x32 });// P2 + magic_numbers[".pgm"].push_back({ 0x50, 0x35 });// P5 + magic_numbers[".ppm"].push_back({ 0x50, 0x33 });// P3 + magic_numbers[".ppm"].push_back({ 0x50, 0x36 });// P6 + // TODO: what about P7? #ifdef HAVE_GIFLIB - // GIF - known_magic_numbers[".gif"].push_back({ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }); - known_magic_numbers[".gif"].push_back({ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }); + // GIF + magic_numbers[".gif"].push_back({ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }); + magic_numbers[".gif"].push_back({ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }); #endif // HAVE_GIFLIB #ifdef HAVE_LIBPNG - // PNG - known_magic_numbers[".png"].push_back({ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }); + // PNG + magic_numbers[".png"].push_back({ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }); #endif // HAVE_LIB_PNG #ifdef HAVE_LIBJPEG - // JPEG - known_magic_numbers[".jpg"].push_back({ 0xFF, 0xD8, 0xFF }); - known_magic_numbers[".jpg"].push_back({ 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20 }); + // JPEG + magic_numbers[".jpg"].push_back({ 0xFF, 0xD8, 0xFF }); + magic_numbers[".jpg"].push_back({ 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20 }); #endif // HAVE_LIBJPEG #ifdef HAVE_LIBTIFF - // TIFF - known_magic_numbers[".tiff"].push_back({ 0x0C, 0xED }); - known_magic_numbers[".tiff"].push_back({ 0x49, 0x20, 0x49 }); - known_magic_numbers[".tiff"].push_back({ 0x49, 0x49, 0x2A, 0x00 }); - known_magic_numbers[".tiff"].push_back({ 0x4D, 0x4D, 0x00, 0x2A }); - known_magic_numbers[".tiff"].push_back({ 0x4D, 0x4D, 0x00, 0x2B }); + // TIFF + magic_numbers[".tiff"].push_back({ 0x0C, 0xED }); + magic_numbers[".tiff"].push_back({ 0x49, 0x20, 0x49 }); + magic_numbers[".tiff"].push_back({ 0x49, 0x49, 0x2A, 0x00 }); + magic_numbers[".tiff"].push_back({ 0x4D, 0x4D, 0x00, 0x2A }); + magic_numbers[".tiff"].push_back({ 0x4D, 0x4D, 0x00, 0x2B }); #endif // HAVE_LIBTIFF - } + return magic_numbers; +} +// global static thread-safe map of known magic numbers +static std::map>> known_magic_numbers = _initialize_magic_numbers(); + + +const std::string& get_correct_image_extension(const std::string& image_name){ // read first 8 bytes from file static uint8_t image_bytes[8]; std::ifstream f(image_name); -- GitLab From 077a7ca5f0aceadc3007352ca19aefb65c08a1d0 Mon Sep 17 00:00:00 2001 From: Manuel Gunther Date: Fri, 9 Sep 2016 10:51:21 -0600 Subject: [PATCH 5/5] Removed static variable in get_correct_image_extension to avoid race conditions --- bob/io/image/cpp/image.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bob/io/image/cpp/image.cpp b/bob/io/image/cpp/image.cpp index 95b6328..bbefa22 100644 --- a/bob/io/image/cpp/image.cpp +++ b/bob/io/image/cpp/image.cpp @@ -56,7 +56,7 @@ static std::map>> known_magic const std::string& get_correct_image_extension(const std::string& image_name){ // read first 8 bytes from file - static uint8_t image_bytes[8]; + uint8_t image_bytes[8]; std::ifstream f(image_name); if (!f) throw std::runtime_error("The given image '" + image_name + "' could not be opened for reading"); f.read(reinterpret_cast(image_bytes), 8); -- GitLab