mirror of
https://github.com/opencv/opencv.git
synced 2026-09-12 21:37:09 -05:00
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
This commit is contained in:
committed by
Alexander Alekhin
parent
b37f0c5ac2
commit
9e054d3880
@@ -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)
|
|||||||
*/
|
*/
|
||||||
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()
|
|||||||
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()
|
|||||||
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()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
if( !exifFound )
|
if( !exifFound )
|
||||||
{
|
{
|
||||||
return m_exif;
|
return m_exif;
|
||||||
@@ -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 @@
|
|||||||
#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:
|
|||||||
/**
|
/**
|
||||||
* @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:
|
|||||||
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:
|
|||||||
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:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _OPENCV_EXIF_HPP_ */
|
#endif /* _OPENCV_EXIF_HPP_ */
|
||||||
|
|||||||
@@ -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 )
|
|||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 )
|
|||||||
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 )
|
|||||||
{
|
{
|
||||||
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 )
|
|||||||
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)
|
|||||||
|
|
||||||
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);
|
||||||
|
|||||||
Reference in New Issue
Block a user