From 9e054d3880ff445aac6fa95a53e05f0dce7880d9 Mon Sep 17 00:00:00 2001 From: brian-armstrong-discord Date: Fri, 14 Apr 2017 13:12:07 -0700 Subject: [PATCH] Merge pull request #8492 from brian-armstrong-discord:exif_inmemory autorotate in-memory jpegs (#8492) * autorotate in-memory jpegs * correct indentation (4 spaces) * imgcodecs: don't apply EXIF rotation for unloaded images * videoio: don't try to rotate MJPEG stream * imgcodecs: ByteStreamBuffer::seekoff support all seek "dir" * imgcodecs: fix condition: "off == egptr() - eback()" is valid offset --- modules/imgcodecs/src/exif.cpp | 51 ++++------ modules/imgcodecs/src/exif.hpp | 10 +- modules/imgcodecs/src/loadsave.cpp | 117 ++++++++++++++++++---- modules/videoio/src/cap_mjpeg_decoder.cpp | 2 +- 4 files changed, 128 insertions(+), 52 deletions(-) diff --git a/modules/imgcodecs/src/exif.cpp b/modules/imgcodecs/src/exif.cpp index 8a4f3f4b86..e057f8ce53 100644 --- a/modules/imgcodecs/src/exif.cpp +++ b/modules/imgcodecs/src/exif.cpp @@ -61,7 +61,7 @@ ExifEntry_t::ExifEntry_t() : /** * @brief ExifReader constructor */ -ExifReader::ExifReader(std::string filename) : m_filename(filename), m_format(NONE) +ExifReader::ExifReader(std::istream& stream) : m_stream(stream), m_format(NONE) { } @@ -121,29 +121,18 @@ ExifEntry_t ExifReader::getTag(const ExifTagName tag) */ std::map ExifReader::getExif() { - const size_t markerSize = 2; - const size_t offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header + const std::streamsize markerSize = 2; + const std::streamsize offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header unsigned char appMarker[markerSize]; m_exif.erase( m_exif.begin(), m_exif.end() ); - size_t count; - - if (m_filename.size() == 0) - { - return m_exif; - } - - FILE* f = fopen( m_filename.c_str(), "rb" ); - - if( !f ) - { - return m_exif; //Until this moment the map is empty - } + std::streamsize count; bool exifFound = false, stopSearch = false; - while( ( !feof( f ) ) && !exifFound && !stopSearch ) + while( ( !m_stream.eof() ) && !exifFound && !stopSearch ) { - count = fread( appMarker, sizeof(unsigned char), markerSize, f ); + m_stream.read( reinterpret_cast(appMarker), markerSize ); + count = m_stream.gcount(); if( count < markerSize ) { break; @@ -159,12 +148,14 @@ std::map ExifReader::getExif() case APP0: case APP2: case APP3: case APP4: case APP5: case APP6: case APP7: case APP8: case APP9: case APP10: case APP11: case APP12: case APP13: case APP14: case APP15: case COM: - bytesToSkip = getFieldSize( f ); + bytesToSkip = getFieldSize(); if (bytesToSkip < markerSize) { - fclose(f); throw ExifParsingError(); } - fseek( f, static_cast( bytesToSkip - markerSize ), SEEK_CUR ); + m_stream.seekg( static_cast( bytesToSkip - markerSize ), m_stream.cur ); + if ( m_stream.fail() ) { + throw ExifParsingError(); + } break; //SOI and EOI don't have the size field after the marker @@ -172,14 +163,17 @@ std::map ExifReader::getExif() break; case APP1: //actual Exif Marker - exifSize = getFieldSize(f); + exifSize = getFieldSize(); if (exifSize <= offsetToTiffHeader) { - fclose(f); throw ExifParsingError(); } m_data.resize( exifSize - offsetToTiffHeader ); - fseek(f, static_cast( offsetToTiffHeader ), SEEK_CUR); - count = fread( &m_data[0], sizeof( unsigned char ), exifSize - offsetToTiffHeader, f ); + m_stream.seekg( static_cast( offsetToTiffHeader ), m_stream.cur ); + if ( m_stream.fail() ) { + throw ExifParsingError(); + } + m_stream.read( reinterpret_cast(&m_data[0]), exifSize - offsetToTiffHeader ); + count = m_stream.gcount(); exifFound = true; break; @@ -189,8 +183,6 @@ std::map ExifReader::getExif() } } - fclose(f); - if( !exifFound ) { return m_exif; @@ -207,10 +199,11 @@ std::map ExifReader::getExif() * * @return size of exif field in the file */ -size_t ExifReader::getFieldSize (FILE* f) const +size_t ExifReader::getFieldSize () { unsigned char fieldSize[2]; - size_t count = fread ( fieldSize, sizeof( char ), 2, f ); + m_stream.read( reinterpret_cast(fieldSize), 2 ); + std::streamsize count = m_stream.gcount(); if (count < 2) { return 0; diff --git a/modules/imgcodecs/src/exif.hpp b/modules/imgcodecs/src/exif.hpp index 43c2857f3c..094236be40 100644 --- a/modules/imgcodecs/src/exif.hpp +++ b/modules/imgcodecs/src/exif.hpp @@ -51,6 +51,7 @@ #include #include #include +#include namespace cv { @@ -168,9 +169,9 @@ public: /** * @brief ExifReader constructor. Constructs an object of exif reader * - * @param [in]filename The name of file to look exif info in + * @param [in]stream An istream to look for EXIF bytes from */ - explicit ExifReader( std::string filename ); + explicit ExifReader( std::istream& stream ); ~ExifReader(); @@ -190,7 +191,7 @@ public: ExifEntry_t getTag( const ExifTagName tag ); private: - std::string m_filename; + std::istream& m_stream; std::vector m_data; std::map m_exif; Endianess_t m_format; @@ -198,7 +199,7 @@ private: void parseExif(); bool checkTagMark() const; - size_t getFieldSize ( FILE* f ) const; + size_t getFieldSize (); size_t getNumDirEntry() const; uint32_t getStartOffset() const; uint16_t getExifTag( const size_t offset ) const; @@ -247,7 +248,6 @@ private: }; - } #endif /* _OPENCV_EXIF_HPP_ */ diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 493ccab457..4ceccd196e 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -50,10 +50,50 @@ #undef min #undef max #include +#include /****************************************************************************************\ * Image Codecs * \****************************************************************************************/ +namespace { + +class ByteStreamBuffer: public std::streambuf +{ +public: + ByteStreamBuffer(char* base, size_t length) + { + setg(base, base, base + length); + } + +protected: + virtual pos_type seekoff( off_type offset, + std::ios_base::seekdir dir, + std::ios_base::openmode ) + { + // get absolute offset + off_type off = offset; + if (dir == std::ios_base::cur) + { + off += gptr() - eback(); + } + else if (dir == std::ios_base::end) + { + off += egptr() - eback(); + } + + // check limits + if (off >= (off_type)0 && off <= egptr() - eback()) + { + setg(eback(), gptr() + off, egptr()); + return gptr() - eback(); + } + + return -1; + } +}; + +} + namespace cv { @@ -232,23 +272,8 @@ static ImageEncoder findEncoder( const String& _ext ) enum { LOAD_CVMAT=0, LOAD_IMAGE=1, LOAD_MAT=2 }; -static void ApplyExifOrientation(const String& filename, Mat& img) +static void ExifTransform(int orientation, 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 @@ -284,6 +309,50 @@ static void ApplyExifOrientation(const String& filename, Mat& img) } } +static void ApplyExifOrientation(const String& filename, Mat& img) +{ + int orientation = IMAGE_ORIENTATION_TL; + + if (filename.size() > 0) + { + std::ifstream stream( filename.c_str(), std::ios_base::in | std::ios_base::binary ); + ExifReader reader( stream ); + 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 + } + } + stream.close(); + } + + ExifTransform(orientation, img); +} + +static void ApplyExifOrientation(const Mat& buf, Mat& img) +{ + int orientation = IMAGE_ORIENTATION_TL; + + if( buf.isContinuous() ) + { + ByteStreamBuffer bsb( reinterpret_cast(buf.data), buf.total() * buf.elemSize() ); + std::istream stream( &bsb ); + ExifReader reader( stream ); + 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 + } + } + } + + ExifTransform(orientation, img); +} + /** * Read an image into memory and return the information * @@ -494,7 +563,7 @@ Mat imread( const String& filename, int flags ) imread_( filename, flags, LOAD_MAT, &img ); /// optionally rotate the data if EXIF' orientation flag says so - if( (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) + if( !img.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) { ApplyExifOrientation(filename, img); } @@ -658,6 +727,13 @@ Mat imdecode( InputArray _buf, int flags ) { Mat buf = _buf.getMat(), img; imdecode_( buf, flags, LOAD_MAT, &img ); + + /// optionally rotate the data if EXIF' orientation flag says so + if( !img.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) + { + ApplyExifOrientation(buf, img); + } + return img; } @@ -666,6 +742,13 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst ) Mat buf = _buf.getMat(), img; dst = dst ? dst : &img; imdecode_( buf, flags, LOAD_MAT, dst ); + + /// optionally rotate the data if EXIF' orientation flag says so + if( !dst->empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) + { + ApplyExifOrientation(buf, *dst); + } + return *dst; } diff --git a/modules/videoio/src/cap_mjpeg_decoder.cpp b/modules/videoio/src/cap_mjpeg_decoder.cpp index 7abce2004e..2ded72e72f 100644 --- a/modules/videoio/src/cap_mjpeg_decoder.cpp +++ b/modules/videoio/src/cap_mjpeg_decoder.cpp @@ -821,7 +821,7 @@ bool MotionJpegCapture::retrieveFrame(int, OutputArray output_frame) if(data.size()) { - m_current_frame = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_COLOR); + m_current_frame = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_COLOR | IMREAD_IGNORE_ORIENTATION); } m_current_frame.copyTo(output_frame); -- GitLab