diff --git a/modules/imgcodecs/CMakeLists.txt b/modules/imgcodecs/CMakeLists.txt index c614d79cdd..5c709afaef 100644 --- a/modules/imgcodecs/CMakeLists.txt +++ b/modules/imgcodecs/CMakeLists.txt @@ -67,8 +67,8 @@ list(APPEND grfmt_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/bitstrm.hpp) list(APPEND grfmt_srcs ${CMAKE_CURRENT_LIST_DIR}/src/bitstrm.cpp) list(APPEND grfmt_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/rgbe.hpp) list(APPEND grfmt_srcs ${CMAKE_CURRENT_LIST_DIR}/src/rgbe.cpp) -list(APPEND grfmt_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/jpeg_exif.hpp) -list(APPEND grfmt_srcs ${CMAKE_CURRENT_LIST_DIR}/src/jpeg_exif.cpp) +list(APPEND grfmt_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/exif.hpp) +list(APPEND grfmt_srcs ${CMAKE_CURRENT_LIST_DIR}/src/exif.cpp) source_group("Src\\grfmts" FILES ${grfmt_hdrs} ${grfmt_srcs}) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index d551ae7bd0..0b9cafa278 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -73,7 +73,8 @@ enum ImreadModes { IMREAD_REDUCED_GRAYSCALE_4 = 32, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/4. IMREAD_REDUCED_COLOR_4 = 33, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/4. IMREAD_REDUCED_GRAYSCALE_8 = 64, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/8. - IMREAD_REDUCED_COLOR_8 = 65 //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/8. + IMREAD_REDUCED_COLOR_8 = 65, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/8. + IMREAD_IGNORE_ORIENTATION = 128 //!< If set, do not rotate the image according to EXIF's orientation flag. }; //! Imwrite flags diff --git a/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h b/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h index 2e76f7fe32..3130710e7b 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h +++ b/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h @@ -63,7 +63,9 @@ enum /* any depth, ? */ CV_LOAD_IMAGE_ANYDEPTH =2, /* ?, any color */ - CV_LOAD_IMAGE_ANYCOLOR =4 + CV_LOAD_IMAGE_ANYCOLOR =4, +/* ?, no rotate */ + CV_LOAD_IMAGE_IGNORE_ORIENTATION =128 }; /* load image from file diff --git a/modules/imgcodecs/src/jpeg_exif.cpp b/modules/imgcodecs/src/exif.cpp similarity index 99% rename from modules/imgcodecs/src/jpeg_exif.cpp rename to modules/imgcodecs/src/exif.cpp index 0704c2f49a..270848180a 100644 --- a/modules/imgcodecs/src/jpeg_exif.cpp +++ b/modules/imgcodecs/src/exif.cpp @@ -40,7 +40,7 @@ // //M*/ -#include "jpeg_exif.hpp" +#include "exif.hpp" namespace { @@ -73,7 +73,7 @@ ExifReader::~ExifReader() } /** - * @brief Parsing the jpeg file and prepare (internally) exif directory structure + * @brief Parsing the file and prepare (internally) exif directory structure * @return true if parsing was successful and exif information exists in JpegReader object * false in case of unsuccessful parsing */ @@ -114,7 +114,7 @@ ExifEntry_t ExifReader::getTag(const ExifTagName tag) /** - * @brief Get exif directory structure contained in jpeg file (if any) + * @brief Get exif directory structure contained in file (if any) * This is internal function and is not exposed to client * * @return Map where key is tag number and value is ExifEntry_t structure diff --git a/modules/imgcodecs/src/jpeg_exif.hpp b/modules/imgcodecs/src/exif.hpp similarity index 90% rename from modules/imgcodecs/src/jpeg_exif.hpp rename to modules/imgcodecs/src/exif.hpp index c8502c5c87..43c2857f3c 100644 --- a/modules/imgcodecs/src/jpeg_exif.hpp +++ b/modules/imgcodecs/src/exif.hpp @@ -41,8 +41,8 @@ //M*/ -#ifndef _OPENCV_JPEG_EXIF_HPP_ -#define _OPENCV_JPEG_EXIF_HPP_ +#ifndef _OPENCV_EXIF_HPP_ +#define _OPENCV_EXIF_HPP_ #include #include @@ -131,21 +131,21 @@ struct ExifEntry_t }; /** - * @brief Picture orientation which may be taken from JPEG's EXIF + * @brief Picture orientation which may be taken from EXIF * Orientation usually matters when the picture is taken by * smartphone or other camera with orientation sensor support * Corresponds to EXIF 2.3 Specification */ -enum JpegOrientation +enum ImageOrientation { - JPEG_ORIENTATION_TL = 1, ///< 0th row == visual top, 0th column == visual left-hand side - JPEG_ORIENTATION_TR = 2, ///< 0th row == visual top, 0th column == visual right-hand side - JPEG_ORIENTATION_BR = 3, ///< 0th row == visual bottom, 0th column == visual right-hand side - JPEG_ORIENTATION_BL = 4, ///< 0th row == visual bottom, 0th column == visual left-hand side - JPEG_ORIENTATION_LT = 5, ///< 0th row == visual left-hand side, 0th column == visual top - JPEG_ORIENTATION_RT = 6, ///< 0th row == visual right-hand side, 0th column == visual top - JPEG_ORIENTATION_RB = 7, ///< 0th row == visual right-hand side, 0th column == visual bottom - JPEG_ORIENTATION_LB = 8 ///< 0th row == visual left-hand side, 0th column == visual bottom + IMAGE_ORIENTATION_TL = 1, ///< Horizontal (normal) + IMAGE_ORIENTATION_TR = 2, ///< Mirrored horizontal + IMAGE_ORIENTATION_BR = 3, ///< Rotate 180 + IMAGE_ORIENTATION_BL = 4, ///< Mirrored vertical + IMAGE_ORIENTATION_LT = 5, ///< Mirrored horizontal & rotate 270 CW + IMAGE_ORIENTATION_RT = 6, ///< Rotate 90 CW + IMAGE_ORIENTATION_RB = 7, ///< Mirrored horizontal & rotate 90 CW + IMAGE_ORIENTATION_LB = 8 ///< Rotate 270 CW }; /** @@ -250,4 +250,4 @@ private: } -#endif /* JPEG_EXIF_HPP_ */ +#endif /* _OPENCV_EXIF_HPP_ */ diff --git a/modules/imgcodecs/src/grfmt_jpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg.cpp index 093b88c400..1f5f1e896c 100644 --- a/modules/imgcodecs/src/grfmt_jpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg.cpp @@ -41,7 +41,6 @@ #include "precomp.hpp" #include "grfmt_jpeg.hpp" -#include "jpeg_exif.hpp" #ifdef HAVE_JPEG @@ -178,7 +177,6 @@ JpegDecoder::JpegDecoder() m_state = 0; m_f = 0; m_buf_supported = true; - m_orientation = JPEG_ORIENTATION_TL; } @@ -255,71 +253,12 @@ bool JpegDecoder::readHeader() } } - m_orientation = getOrientation(); - if( !result ) close(); return result; } -int JpegDecoder::getOrientation() -{ - int orientation = JPEG_ORIENTATION_TL; - - if (m_filename.size() > 0) - { - ExifReader reader( m_filename ); - if( reader.parse() ) - { - ExifEntry_t entry = reader.getTag( ORIENTATION ); - if (entry.tag != INVALID_TAG) - { - orientation = entry.field_u16; //orientation is unsigned short, so check field_u16 - } - } - } - - return orientation; -} - -void JpegDecoder::setOrientation(Mat& img) -{ - switch( m_orientation ) - { - case JPEG_ORIENTATION_TL: //0th row == visual top, 0th column == visual left-hand side - //do nothing, the image already has proper orientation - break; - case JPEG_ORIENTATION_TR: //0th row == visual top, 0th column == visual right-hand side - flip(img, img, 1); //flip horizontally - break; - case JPEG_ORIENTATION_BR: //0th row == visual bottom, 0th column == visual right-hand side - flip(img, img, -1);//flip both horizontally and vertically - break; - case JPEG_ORIENTATION_BL: //0th row == visual bottom, 0th column == visual left-hand side - flip(img, img, 0); //flip vertically - break; - case JPEG_ORIENTATION_LT: //0th row == visual left-hand side, 0th column == visual top - transpose(img, img); - break; - case JPEG_ORIENTATION_RT: //0th row == visual right-hand side, 0th column == visual top - transpose(img, img); - flip(img, img, 1); //flip horizontally - break; - case JPEG_ORIENTATION_RB: //0th row == visual right-hand side, 0th column == visual bottom - transpose(img, img); - flip(img, img, -1); //flip both horizontally and vertically - break; - case JPEG_ORIENTATION_LB: //0th row == visual left-hand side, 0th column == visual bottom - transpose(img, img); - flip(img, img, 0); //flip vertically - break; - default: - //by default the image read has normal (JPEG_ORIENTATION_TL) orientation - break; - } -} - /*************************************************************************** * following code is for supporting MJPEG image files * based on a message of Laurent Pinchart on the video4linux mailing list @@ -536,7 +475,6 @@ bool JpegDecoder::readData( Mat& img ) result = true; jpeg_finish_decompress( cinfo ); - setOrientation( img ); } } diff --git a/modules/imgcodecs/src/grfmt_jpeg.hpp b/modules/imgcodecs/src/grfmt_jpeg.hpp index d0a0991c59..8455b197b0 100644 --- a/modules/imgcodecs/src/grfmt_jpeg.hpp +++ b/modules/imgcodecs/src/grfmt_jpeg.hpp @@ -70,12 +70,6 @@ protected: FILE* m_f; void* m_state; - -private: - //Support for handling exif orientation tag in Jpeg file - int m_orientation; - int getOrientation(); - void setOrientation(Mat& img); }; diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 6cd3106583..8c639333b2 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -45,6 +45,8 @@ #include "precomp.hpp" #include "grfmts.hpp" +#include "utils.hpp" +#include "exif.hpp" #undef min #undef max #include @@ -227,8 +229,11 @@ static ImageEncoder findEncoder( const String& _ext ) return ImageEncoder(); } + enum { LOAD_CVMAT=0, LOAD_IMAGE=1, LOAD_MAT=2 }; +void RotateImage(const String& filename, Mat& img); + /** * Read an image into memory and return the information * @@ -403,6 +408,12 @@ imreadmulti_(const String& filename, int flags, std::vector& mats) Mat mat(decoder->height(), decoder->width(), type); if (!decoder->readData(mat)) { + // optionally rotate the data if EXIF' orientation flag says so + if( (flags & IMREAD_IGNORE_ORIENTATION) == 0 ) + { + RotateImage(filename, mat); + } + break; } @@ -416,6 +427,59 @@ imreadmulti_(const String& filename, int flags, std::vector& mats) return !mats.empty(); } +void RotateImage(const String& filename, Mat& img) +{ + int orientation = IMAGE_ORIENTATION_TL; + + if (filename.size() > 0) + { + ExifReader reader( filename ); + if( reader.parse() ) + { + ExifEntry_t entry = reader.getTag( ORIENTATION ); + if (entry.tag != INVALID_TAG) + { + orientation = entry.field_u16; //orientation is unsigned short, so check field_u16 + } + } + } + + switch( orientation ) + { + case IMAGE_ORIENTATION_TL: //0th row == visual top, 0th column == visual left-hand side + //do nothing, the image already has proper orientation + break; + case IMAGE_ORIENTATION_TR: //0th row == visual top, 0th column == visual right-hand side + flip(img, img, 1); //flip horizontally + break; + case IMAGE_ORIENTATION_BR: //0th row == visual bottom, 0th column == visual right-hand side + flip(img, img, -1);//flip both horizontally and vertically + break; + case IMAGE_ORIENTATION_BL: //0th row == visual bottom, 0th column == visual left-hand side + flip(img, img, 0); //flip vertically + break; + case IMAGE_ORIENTATION_LT: //0th row == visual left-hand side, 0th column == visual top + transpose(img, img); + break; + case IMAGE_ORIENTATION_RT: //0th row == visual right-hand side, 0th column == visual top + transpose(img, img); + flip(img, img, 1); //flip horizontally + break; + case IMAGE_ORIENTATION_RB: //0th row == visual right-hand side, 0th column == visual bottom + transpose(img, img); + flip(img, img, -1); //flip both horizontally and vertically + break; + case IMAGE_ORIENTATION_LB: //0th row == visual left-hand side, 0th column == visual bottom + transpose(img, img); + flip(img, img, 0); //flip vertically + break; + default: + //by default the image read has normal (JPEG_ORIENTATION_TL) orientation + break; + } +} + + /** * Read an image * @@ -432,6 +496,12 @@ Mat imread( const String& filename, int flags ) /// load the data imread_( filename, flags, LOAD_MAT, &img ); + /// optionally rotate the data if EXIF' orientation flag says so + if( (flags & IMREAD_IGNORE_ORIENTATION) == 0 ) + { + RotateImage(filename, img); + } + /// return a reference to the data return img; }