提交 9e054d38 编写于 作者: B brian-armstrong-discord 提交者: Alexander Alekhin

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
上级 b37f0c5a
...@@ -61,7 +61,7 @@ ExifEntry_t::ExifEntry_t() : ...@@ -61,7 +61,7 @@ ExifEntry_t::ExifEntry_t() :
/** /**
* @brief ExifReader constructor * @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) ...@@ -121,29 +121,18 @@ ExifEntry_t ExifReader::getTag(const ExifTagName tag)
*/ */
std::map<int, ExifEntry_t > ExifReader::getExif() std::map<int, ExifEntry_t > ExifReader::getExif()
{ {
const size_t markerSize = 2; const std::streamsize markerSize = 2;
const size_t offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header const std::streamsize offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header
unsigned char appMarker[markerSize]; unsigned char appMarker[markerSize];
m_exif.erase( m_exif.begin(), m_exif.end() ); m_exif.erase( m_exif.begin(), m_exif.end() );
size_t count; std::streamsize 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
}
bool exifFound = false, stopSearch = false; 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<char*>(appMarker), markerSize );
count = m_stream.gcount();
if( count < markerSize ) if( count < markerSize )
{ {
break; break;
...@@ -159,12 +148,14 @@ std::map<int, ExifEntry_t > ExifReader::getExif() ...@@ -159,12 +148,14 @@ std::map<int, ExifEntry_t > ExifReader::getExif()
case APP0: case APP2: case APP3: case APP4: case APP5: case APP6: case APP7: case APP8: 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 APP9: case APP10: case APP11: case APP12: case APP13: case APP14: case APP15:
case COM: case COM:
bytesToSkip = getFieldSize( f ); bytesToSkip = getFieldSize();
if (bytesToSkip < markerSize) { if (bytesToSkip < markerSize) {
fclose(f);
throw ExifParsingError(); throw ExifParsingError();
} }
fseek( f, static_cast<long>( bytesToSkip - markerSize ), SEEK_CUR ); m_stream.seekg( static_cast<long>( bytesToSkip - markerSize ), m_stream.cur );
if ( m_stream.fail() ) {
throw ExifParsingError();
}
break; break;
//SOI and EOI don't have the size field after the marker //SOI and EOI don't have the size field after the marker
...@@ -172,14 +163,17 @@ std::map<int, ExifEntry_t > ExifReader::getExif() ...@@ -172,14 +163,17 @@ std::map<int, ExifEntry_t > ExifReader::getExif()
break; break;
case APP1: //actual Exif Marker case APP1: //actual Exif Marker
exifSize = getFieldSize(f); exifSize = getFieldSize();
if (exifSize <= offsetToTiffHeader) { if (exifSize <= offsetToTiffHeader) {
fclose(f);
throw ExifParsingError(); throw ExifParsingError();
} }
m_data.resize( exifSize - offsetToTiffHeader ); m_data.resize( exifSize - offsetToTiffHeader );
fseek(f, static_cast<long>( offsetToTiffHeader ), SEEK_CUR); m_stream.seekg( static_cast<long>( offsetToTiffHeader ), m_stream.cur );
count = fread( &m_data[0], sizeof( unsigned char ), exifSize - offsetToTiffHeader, f ); if ( m_stream.fail() ) {
throw ExifParsingError();
}
m_stream.read( reinterpret_cast<char*>(&m_data[0]), exifSize - offsetToTiffHeader );
count = m_stream.gcount();
exifFound = true; exifFound = true;
break; break;
...@@ -189,8 +183,6 @@ std::map<int, ExifEntry_t > ExifReader::getExif() ...@@ -189,8 +183,6 @@ std::map<int, ExifEntry_t > ExifReader::getExif()
} }
} }
fclose(f);
if( !exifFound ) if( !exifFound )
{ {
return m_exif; return m_exif;
...@@ -207,10 +199,11 @@ std::map<int, ExifEntry_t > ExifReader::getExif() ...@@ -207,10 +199,11 @@ std::map<int, ExifEntry_t > ExifReader::getExif()
* *
* @return size of exif field in the file * @return size of exif field in the file
*/ */
size_t ExifReader::getFieldSize (FILE* f) const size_t ExifReader::getFieldSize ()
{ {
unsigned char fieldSize[2]; unsigned char fieldSize[2];
size_t count = fread ( fieldSize, sizeof( char ), 2, f ); m_stream.read( reinterpret_cast<char*>(fieldSize), 2 );
std::streamsize count = m_stream.gcount();
if (count < 2) if (count < 2)
{ {
return 0; return 0;
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <iostream>
namespace cv namespace cv
{ {
...@@ -168,9 +169,9 @@ public: ...@@ -168,9 +169,9 @@ public:
/** /**
* @brief ExifReader constructor. Constructs an object of exif reader * @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(); ~ExifReader();
...@@ -190,7 +191,7 @@ public: ...@@ -190,7 +191,7 @@ public:
ExifEntry_t getTag( const ExifTagName tag ); ExifEntry_t getTag( const ExifTagName tag );
private: private:
std::string m_filename; std::istream& m_stream;
std::vector<unsigned char> m_data; std::vector<unsigned char> m_data;
std::map<int, ExifEntry_t > m_exif; std::map<int, ExifEntry_t > m_exif;
Endianess_t m_format; Endianess_t m_format;
...@@ -198,7 +199,7 @@ private: ...@@ -198,7 +199,7 @@ private:
void parseExif(); void parseExif();
bool checkTagMark() const; bool checkTagMark() const;
size_t getFieldSize ( FILE* f ) const; size_t getFieldSize ();
size_t getNumDirEntry() const; size_t getNumDirEntry() const;
uint32_t getStartOffset() const; uint32_t getStartOffset() const;
uint16_t getExifTag( const size_t offset ) const; uint16_t getExifTag( const size_t offset ) const;
...@@ -247,7 +248,6 @@ private: ...@@ -247,7 +248,6 @@ private:
}; };
} }
#endif /* _OPENCV_EXIF_HPP_ */ #endif /* _OPENCV_EXIF_HPP_ */
...@@ -50,10 +50,50 @@ ...@@ -50,10 +50,50 @@
#undef min #undef min
#undef max #undef max
#include <iostream> #include <iostream>
#include <fstream>
/****************************************************************************************\ /****************************************************************************************\
* Image Codecs * * 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 namespace cv
{ {
...@@ -232,23 +272,8 @@ static ImageEncoder findEncoder( const String& _ext ) ...@@ -232,23 +272,8 @@ static ImageEncoder findEncoder( const String& _ext )
enum { LOAD_CVMAT=0, LOAD_IMAGE=1, LOAD_MAT=2 }; 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 ) switch( orientation )
{ {
case IMAGE_ORIENTATION_TL: //0th row == visual top, 0th column == visual left-hand side 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) ...@@ -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<char*>(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 * Read an image into memory and return the information
* *
...@@ -494,7 +563,7 @@ Mat imread( const String& filename, int flags ) ...@@ -494,7 +563,7 @@ Mat imread( const String& filename, int flags )
imread_( filename, flags, LOAD_MAT, &img ); imread_( filename, flags, LOAD_MAT, &img );
/// optionally rotate the data if EXIF' orientation flag says so /// 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); ApplyExifOrientation(filename, img);
} }
...@@ -658,6 +727,13 @@ Mat imdecode( InputArray _buf, int flags ) ...@@ -658,6 +727,13 @@ Mat imdecode( InputArray _buf, int flags )
{ {
Mat buf = _buf.getMat(), img; Mat buf = _buf.getMat(), img;
imdecode_( buf, flags, LOAD_MAT, &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; return img;
} }
...@@ -666,6 +742,13 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst ) ...@@ -666,6 +742,13 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst )
Mat buf = _buf.getMat(), img; Mat buf = _buf.getMat(), img;
dst = dst ? dst : &img; dst = dst ? dst : &img;
imdecode_( buf, flags, LOAD_MAT, dst ); 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; return *dst;
} }
......
...@@ -821,7 +821,7 @@ bool MotionJpegCapture::retrieveFrame(int, OutputArray output_frame) ...@@ -821,7 +821,7 @@ bool MotionJpegCapture::retrieveFrame(int, OutputArray output_frame)
if(data.size()) 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); m_current_frame.copyTo(output_frame);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册