Skip to content

Commit 9e054d3

Browse files
brian-armstrong-discordalalek
authored andcommitted
Merge pull request opencv#8492 from brian-armstrong-discord:exif_inmemory
autorotate in-memory jpegs (opencv#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
1 parent b37f0c5 commit 9e054d3

File tree

4 files changed

+128
-52
lines changed

4 files changed

+128
-52
lines changed

modules/imgcodecs/src/exif.cpp

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ ExifEntry_t::ExifEntry_t() :
6161
/**
6262
* @brief ExifReader constructor
6363
*/
64-
ExifReader::ExifReader(std::string filename) : m_filename(filename), m_format(NONE)
64+
ExifReader::ExifReader(std::istream& stream) : m_stream(stream), m_format(NONE)
6565
{
6666
}
6767

@@ -121,29 +121,18 @@ ExifEntry_t ExifReader::getTag(const ExifTagName tag)
121121
*/
122122
std::map<int, ExifEntry_t > ExifReader::getExif()
123123
{
124-
const size_t markerSize = 2;
125-
const size_t offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header
124+
const std::streamsize markerSize = 2;
125+
const std::streamsize offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header
126126
unsigned char appMarker[markerSize];
127127
m_exif.erase( m_exif.begin(), m_exif.end() );
128128

129-
size_t count;
130-
131-
if (m_filename.size() == 0)
132-
{
133-
return m_exif;
134-
}
135-
136-
FILE* f = fopen( m_filename.c_str(), "rb" );
137-
138-
if( !f )
139-
{
140-
return m_exif; //Until this moment the map is empty
141-
}
129+
std::streamsize count;
142130

143131
bool exifFound = false, stopSearch = false;
144-
while( ( !feof( f ) ) && !exifFound && !stopSearch )
132+
while( ( !m_stream.eof() ) && !exifFound && !stopSearch )
145133
{
146-
count = fread( appMarker, sizeof(unsigned char), markerSize, f );
134+
m_stream.read( reinterpret_cast<char*>(appMarker), markerSize );
135+
count = m_stream.gcount();
147136
if( count < markerSize )
148137
{
149138
break;
@@ -159,27 +148,32 @@ std::map<int, ExifEntry_t > ExifReader::getExif()
159148
case APP0: case APP2: case APP3: case APP4: case APP5: case APP6: case APP7: case APP8:
160149
case APP9: case APP10: case APP11: case APP12: case APP13: case APP14: case APP15:
161150
case COM:
162-
bytesToSkip = getFieldSize( f );
151+
bytesToSkip = getFieldSize();
163152
if (bytesToSkip < markerSize) {
164-
fclose(f);
165153
throw ExifParsingError();
166154
}
167-
fseek( f, static_cast<long>( bytesToSkip - markerSize ), SEEK_CUR );
155+
m_stream.seekg( static_cast<long>( bytesToSkip - markerSize ), m_stream.cur );
156+
if ( m_stream.fail() ) {
157+
throw ExifParsingError();
158+
}
168159
break;
169160

170161
//SOI and EOI don't have the size field after the marker
171162
case SOI: case EOI:
172163
break;
173164

174165
case APP1: //actual Exif Marker
175-
exifSize = getFieldSize(f);
166+
exifSize = getFieldSize();
176167
if (exifSize <= offsetToTiffHeader) {
177-
fclose(f);
178168
throw ExifParsingError();
179169
}
180170
m_data.resize( exifSize - offsetToTiffHeader );
181-
fseek(f, static_cast<long>( offsetToTiffHeader ), SEEK_CUR);
182-
count = fread( &m_data[0], sizeof( unsigned char ), exifSize - offsetToTiffHeader, f );
171+
m_stream.seekg( static_cast<long>( offsetToTiffHeader ), m_stream.cur );
172+
if ( m_stream.fail() ) {
173+
throw ExifParsingError();
174+
}
175+
m_stream.read( reinterpret_cast<char*>(&m_data[0]), exifSize - offsetToTiffHeader );
176+
count = m_stream.gcount();
183177
exifFound = true;
184178
break;
185179

@@ -189,8 +183,6 @@ std::map<int, ExifEntry_t > ExifReader::getExif()
189183
}
190184
}
191185

192-
fclose(f);
193-
194186
if( !exifFound )
195187
{
196188
return m_exif;
@@ -207,10 +199,11 @@ std::map<int, ExifEntry_t > ExifReader::getExif()
207199
*
208200
* @return size of exif field in the file
209201
*/
210-
size_t ExifReader::getFieldSize (FILE* f) const
202+
size_t ExifReader::getFieldSize ()
211203
{
212204
unsigned char fieldSize[2];
213-
size_t count = fread ( fieldSize, sizeof( char ), 2, f );
205+
m_stream.read( reinterpret_cast<char*>(fieldSize), 2 );
206+
std::streamsize count = m_stream.gcount();
214207
if (count < 2)
215208
{
216209
return 0;

modules/imgcodecs/src/exif.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include <stdint.h>
5252
#include <string>
5353
#include <vector>
54+
#include <iostream>
5455

5556
namespace cv
5657
{
@@ -168,9 +169,9 @@ class ExifReader
168169
/**
169170
* @brief ExifReader constructor. Constructs an object of exif reader
170171
*
171-
* @param [in]filename The name of file to look exif info in
172+
* @param [in]stream An istream to look for EXIF bytes from
172173
*/
173-
explicit ExifReader( std::string filename );
174+
explicit ExifReader( std::istream& stream );
174175
~ExifReader();
175176

176177

@@ -190,15 +191,15 @@ class ExifReader
190191
ExifEntry_t getTag( const ExifTagName tag );
191192

192193
private:
193-
std::string m_filename;
194+
std::istream& m_stream;
194195
std::vector<unsigned char> m_data;
195196
std::map<int, ExifEntry_t > m_exif;
196197
Endianess_t m_format;
197198

198199
void parseExif();
199200
bool checkTagMark() const;
200201

201-
size_t getFieldSize ( FILE* f ) const;
202+
size_t getFieldSize ();
202203
size_t getNumDirEntry() const;
203204
uint32_t getStartOffset() const;
204205
uint16_t getExifTag( const size_t offset ) const;
@@ -247,7 +248,6 @@ class ExifReader
247248
};
248249

249250

250-
251251
}
252252

253253
#endif /* _OPENCV_EXIF_HPP_ */

modules/imgcodecs/src/loadsave.cpp

Lines changed: 100 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,50 @@
5050
#undef min
5151
#undef max
5252
#include <iostream>
53+
#include <fstream>
5354

5455
/****************************************************************************************\
5556
* Image Codecs *
5657
\****************************************************************************************/
58+
namespace {
59+
60+
class ByteStreamBuffer: public std::streambuf
61+
{
62+
public:
63+
ByteStreamBuffer(char* base, size_t length)
64+
{
65+
setg(base, base, base + length);
66+
}
67+
68+
protected:
69+
virtual pos_type seekoff( off_type offset,
70+
std::ios_base::seekdir dir,
71+
std::ios_base::openmode )
72+
{
73+
// get absolute offset
74+
off_type off = offset;
75+
if (dir == std::ios_base::cur)
76+
{
77+
off += gptr() - eback();
78+
}
79+
else if (dir == std::ios_base::end)
80+
{
81+
off += egptr() - eback();
82+
}
83+
84+
// check limits
85+
if (off >= (off_type)0 && off <= egptr() - eback())
86+
{
87+
setg(eback(), gptr() + off, egptr());
88+
return gptr() - eback();
89+
}
90+
91+
return -1;
92+
}
93+
};
94+
95+
}
96+
5797
namespace cv
5898
{
5999

@@ -232,23 +272,8 @@ static ImageEncoder findEncoder( const String& _ext )
232272

233273
enum { LOAD_CVMAT=0, LOAD_IMAGE=1, LOAD_MAT=2 };
234274

235-
static void ApplyExifOrientation(const String& filename, Mat& img)
275+
static void ExifTransform(int orientation, Mat& img)
236276
{
237-
int orientation = IMAGE_ORIENTATION_TL;
238-
239-
if (filename.size() > 0)
240-
{
241-
ExifReader reader( filename );
242-
if( reader.parse() )
243-
{
244-
ExifEntry_t entry = reader.getTag( ORIENTATION );
245-
if (entry.tag != INVALID_TAG)
246-
{
247-
orientation = entry.field_u16; //orientation is unsigned short, so check field_u16
248-
}
249-
}
250-
}
251-
252277
switch( orientation )
253278
{
254279
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)
284309
}
285310
}
286311

312+
static void ApplyExifOrientation(const String& filename, Mat& img)
313+
{
314+
int orientation = IMAGE_ORIENTATION_TL;
315+
316+
if (filename.size() > 0)
317+
{
318+
std::ifstream stream( filename.c_str(), std::ios_base::in | std::ios_base::binary );
319+
ExifReader reader( stream );
320+
if( reader.parse() )
321+
{
322+
ExifEntry_t entry = reader.getTag( ORIENTATION );
323+
if (entry.tag != INVALID_TAG)
324+
{
325+
orientation = entry.field_u16; //orientation is unsigned short, so check field_u16
326+
}
327+
}
328+
stream.close();
329+
}
330+
331+
ExifTransform(orientation, img);
332+
}
333+
334+
static void ApplyExifOrientation(const Mat& buf, Mat& img)
335+
{
336+
int orientation = IMAGE_ORIENTATION_TL;
337+
338+
if( buf.isContinuous() )
339+
{
340+
ByteStreamBuffer bsb( reinterpret_cast<char*>(buf.data), buf.total() * buf.elemSize() );
341+
std::istream stream( &bsb );
342+
ExifReader reader( stream );
343+
if( reader.parse() )
344+
{
345+
ExifEntry_t entry = reader.getTag( ORIENTATION );
346+
if (entry.tag != INVALID_TAG)
347+
{
348+
orientation = entry.field_u16; //orientation is unsigned short, so check field_u16
349+
}
350+
}
351+
}
352+
353+
ExifTransform(orientation, img);
354+
}
355+
287356
/**
288357
* Read an image into memory and return the information
289358
*
@@ -494,7 +563,7 @@ Mat imread( const String& filename, int flags )
494563
imread_( filename, flags, LOAD_MAT, &img );
495564

496565
/// optionally rotate the data if EXIF' orientation flag says so
497-
if( (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED )
566+
if( !img.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED )
498567
{
499568
ApplyExifOrientation(filename, img);
500569
}
@@ -658,6 +727,13 @@ Mat imdecode( InputArray _buf, int flags )
658727
{
659728
Mat buf = _buf.getMat(), img;
660729
imdecode_( buf, flags, LOAD_MAT, &img );
730+
731+
/// optionally rotate the data if EXIF' orientation flag says so
732+
if( !img.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED )
733+
{
734+
ApplyExifOrientation(buf, img);
735+
}
736+
661737
return img;
662738
}
663739

@@ -666,6 +742,13 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst )
666742
Mat buf = _buf.getMat(), img;
667743
dst = dst ? dst : &img;
668744
imdecode_( buf, flags, LOAD_MAT, dst );
745+
746+
/// optionally rotate the data if EXIF' orientation flag says so
747+
if( !dst->empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED )
748+
{
749+
ApplyExifOrientation(buf, *dst);
750+
}
751+
669752
return *dst;
670753
}
671754

modules/videoio/src/cap_mjpeg_decoder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ bool MotionJpegCapture::retrieveFrame(int, OutputArray output_frame)
821821

822822
if(data.size())
823823
{
824-
m_current_frame = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_COLOR);
824+
m_current_frame = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_COLOR | IMREAD_IGNORE_ORIENTATION);
825825
}
826826

827827
m_current_frame.copyTo(output_frame);

0 commit comments

Comments
 (0)