Skip to content

Commit 650836d

Browse files
committed
Exif parsing for PNG files to support Exif orientation tag. Moved decoder specific Exif parsing to JPEG and PNG decoders, respectively. Issue 16579
1 parent 2d2d72a commit 650836d

File tree

9 files changed

+216
-190
lines changed

9 files changed

+216
-190
lines changed

modules/imgcodecs/src/exif.cpp

Lines changed: 25 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ ExifEntry_t::ExifEntry_t() :
6262
/**
6363
* @brief ExifReader constructor
6464
*/
65-
ExifReader::ExifReader(std::istream& stream) : m_stream(stream), m_format(NONE)
65+
ExifReader::ExifReader() : m_format(NONE)
6666
{
6767
}
6868

@@ -73,25 +73,6 @@ ExifReader::~ExifReader()
7373
{
7474
}
7575

76-
/**
77-
* @brief Parsing the file and prepare (internally) exif directory structure
78-
* @return true if parsing was successful and exif information exists in JpegReader object
79-
* false in case of unsuccessful parsing
80-
*/
81-
bool ExifReader::parse()
82-
{
83-
try {
84-
m_exif = getExif();
85-
if( !m_exif.empty() )
86-
{
87-
return true;
88-
}
89-
return false;
90-
} catch (ExifParsingError&) {
91-
return false;
92-
}
93-
}
94-
9576

9677
/**
9778
* @brief Get tag value by tag number
@@ -101,10 +82,10 @@ bool ExifReader::parse()
10182
* @return ExifEntru_t structure. Caller has to know what tag it calls in order to extract proper field from the structure ExifEntry_t
10283
*
10384
*/
104-
ExifEntry_t ExifReader::getTag(const ExifTagName tag)
85+
ExifEntry_t ExifReader::getTag(const ExifTagName tag) const
10586
{
10687
ExifEntry_t entry;
107-
std::map<int, ExifEntry_t>::iterator it = m_exif.find(tag);
88+
std::map<int, ExifEntry_t>::const_iterator it = m_exif.find(tag);
10889

10990
if( it != m_exif.end() )
11091
{
@@ -115,100 +96,37 @@ ExifEntry_t ExifReader::getTag(const ExifTagName tag)
11596

11697

11798
/**
118-
* @brief Get exif directory structure contained in file (if any)
119-
* This is internal function and is not exposed to client
99+
* @brief Parsing the exif data buffer and prepare (internal) exif directory
100+
*
101+
* @param [in] data The data buffer to read EXIF data starting with endianness
102+
* @param [in] size The size of the data buffer
120103
*
121-
* @return Map where key is tag number and value is ExifEntry_t structure
104+
* @return true if parsing was successful
105+
* false in case of unsuccessful parsing
122106
*/
123-
std::map<int, ExifEntry_t > ExifReader::getExif()
107+
bool ExifReader::parseExif(unsigned char* data, const size_t size)
124108
{
125-
const std::streamsize markerSize = 2;
126-
const std::streamsize offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header
127-
unsigned char appMarker[markerSize];
128-
m_exif.erase( m_exif.begin(), m_exif.end() );
129-
130-
std::streamsize count;
131-
132-
bool exifFound = false, stopSearch = false;
133-
while( ( !m_stream.eof() ) && !exifFound && !stopSearch )
109+
// Populate m_data, then call parseExif() (private)
110+
if( data && size > 0 )
134111
{
135-
m_stream.read( reinterpret_cast<char*>(appMarker), markerSize );
136-
count = m_stream.gcount();
137-
if( count < markerSize )
138-
{
139-
break;
140-
}
141-
unsigned char marker = appMarker[1];
142-
size_t bytesToSkip;
143-
size_t exifSize;
144-
switch( marker )
145-
{
146-
//For all the markers just skip bytes in file pointed by followed two bytes (field size)
147-
case SOF0: case SOF2: case DHT: case DQT: case DRI: case SOS:
148-
case RST0: case RST1: case RST2: case RST3: case RST4: case RST5: case RST6: case RST7:
149-
case APP0: case APP2: case APP3: case APP4: case APP5: case APP6: case APP7: case APP8:
150-
case APP9: case APP10: case APP11: case APP12: case APP13: case APP14: case APP15:
151-
case COM:
152-
bytesToSkip = getFieldSize();
153-
if (bytesToSkip < markerSize) {
154-
throw ExifParsingError();
155-
}
156-
m_stream.seekg( static_cast<long>( bytesToSkip - markerSize ), m_stream.cur );
157-
if ( m_stream.fail() ) {
158-
throw ExifParsingError();
159-
}
160-
break;
161-
162-
//SOI and EOI don't have the size field after the marker
163-
case SOI: case EOI:
164-
break;
165-
166-
case APP1: //actual Exif Marker
167-
exifSize = getFieldSize();
168-
if (exifSize <= offsetToTiffHeader) {
169-
throw ExifParsingError();
170-
}
171-
m_data.resize( exifSize - offsetToTiffHeader );
172-
m_stream.seekg( static_cast<long>( offsetToTiffHeader ), m_stream.cur );
173-
if ( m_stream.fail() ) {
174-
throw ExifParsingError();
175-
}
176-
m_stream.read( reinterpret_cast<char*>(&m_data[0]), exifSize - offsetToTiffHeader );
177-
exifFound = true;
178-
break;
179-
180-
default: //No other markers are expected according to standard. May be a signal of error
181-
stopSearch = true;
182-
break;
183-
}
112+
m_data.assign(data, data + size);
184113
}
185-
186-
if( !exifFound )
114+
else
187115
{
188-
return m_exif;
116+
return false;
189117
}
190118

191-
parseExif();
192-
193-
return m_exif;
194-
}
195-
196-
/**
197-
* @brief Get the size of exif field (required to properly ready whole exif from the file)
198-
* This is internal function and is not exposed to client
199-
*
200-
* @return size of exif field in the file
201-
*/
202-
size_t ExifReader::getFieldSize ()
203-
{
204-
unsigned char fieldSize[2];
205-
m_stream.read( reinterpret_cast<char*>(fieldSize), 2 );
206-
std::streamsize count = m_stream.gcount();
207-
if (count < 2)
208-
{
209-
return 0;
119+
try {
120+
parseExif();
121+
if( !m_exif.empty() )
122+
{
123+
return true;
124+
}
125+
return false;
126+
}
127+
catch( ExifParsingError& ) {
128+
return false;
210129
}
211-
return ( fieldSize[0] << 8 ) + fieldSize[1];
212130
}
213131

214132
/**

modules/imgcodecs/src/exif.hpp

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -54,24 +54,6 @@
5454

5555
namespace cv
5656
{
57-
/**
58-
* @brief Jpeg markers that can encounter in Jpeg file
59-
*/
60-
enum AppMarkerTypes
61-
{
62-
SOI = 0xD8, SOF0 = 0xC0, SOF2 = 0xC2, DHT = 0xC4,
63-
DQT = 0xDB, DRI = 0xDD, SOS = 0xDA,
64-
65-
RST0 = 0xD0, RST1 = 0xD1, RST2 = 0xD2, RST3 = 0xD3,
66-
RST4 = 0xD4, RST5 = 0xD5, RST6 = 0xD6, RST7 = 0xD7,
67-
68-
APP0 = 0xE0, APP1 = 0xE1, APP2 = 0xE2, APP3 = 0xE3,
69-
APP4 = 0xE4, APP5 = 0xE5, APP6 = 0xE6, APP7 = 0xE7,
70-
APP8 = 0xE8, APP9 = 0xE9, APP10 = 0xEA, APP11 = 0xEB,
71-
APP12 = 0xEC, APP13 = 0xED, APP14 = 0xEE, APP15 = 0xEF,
72-
73-
COM = 0xFE, EOI = 0xD9
74-
};
7557

7658
/**
7759
* @brief Base Exif tags used by IFD0 (main image)
@@ -168,38 +150,40 @@ class ExifReader
168150
public:
169151
/**
170152
* @brief ExifReader constructor. Constructs an object of exif reader
171-
*
172-
* @param [in]stream An istream to look for EXIF bytes from
173153
*/
174-
explicit ExifReader( std::istream& stream );
154+
ExifReader();
175155
~ExifReader();
176156

177157

178158
/**
179159
* @brief Parse the file with exif info
180160
*
181-
* @return true if parsing was successful and exif information exists in JpegReader object
161+
* @param [in] data The data buffer to read EXIF data starting with endianness
162+
* @param [in] size The size of the data buffer
163+
*
164+
* @return true if successful parsing
165+
* false if parsing error
182166
*/
183-
bool parse();
167+
168+
bool parseExif(unsigned char* data, const size_t size);
184169

185170
/**
186171
* @brief Get tag info by tag number
187172
*
188173
* @param [in] tag The tag number
189174
* @return ExifEntru_t structure. Caller has to know what tag it calls in order to extract proper field from the structure ExifEntry_t
190175
*/
191-
ExifEntry_t getTag( const ExifTagName tag );
176+
ExifEntry_t getTag( const ExifTagName tag ) const;
177+
192178

193179
private:
194-
std::istream& m_stream;
195180
std::vector<unsigned char> m_data;
196181
std::map<int, ExifEntry_t > m_exif;
197182
Endianess_t m_format;
198183

199184
void parseExif();
200185
bool checkTagMark() const;
201186

202-
size_t getFieldSize ();
203187
size_t getNumDirEntry( const size_t offsetNumDir ) const;
204188
uint32_t getStartOffset() const;
205189
uint16_t getExifTag( const size_t offset ) const;
@@ -215,7 +199,6 @@ class ExifReader
215199

216200
u_rational_t getURational( const size_t offset ) const;
217201

218-
std::map<int, ExifEntry_t > getExif();
219202
std::string getString( const size_t offset ) const;
220203
std::vector<u_rational_t> getResolution( const size_t offset ) const;
221204
std::vector<u_rational_t> getWhitePoint( const size_t offset ) const;

modules/imgcodecs/src/grfmt_base.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ BaseImageDecoder::BaseImageDecoder()
5555
m_scale_denom = 1;
5656
}
5757

58+
59+
ExifEntry_t BaseImageDecoder::getExifTag(const ExifTagName tag) const
60+
{
61+
return m_exif.getTag(tag);
62+
}
5863
bool BaseImageDecoder::setSource( const String& filename )
5964
{
6065
m_filename = filename;

modules/imgcodecs/src/grfmt_base.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545

4646
#include "utils.hpp"
4747
#include "bitstrm.hpp"
48+
#include "exif.hpp"
4849

4950
namespace cv
5051
{
@@ -65,6 +66,7 @@ class BaseImageDecoder
6566
int height() const { return m_height; }
6667
virtual int type() const { return m_type; }
6768

69+
ExifEntry_t getExifTag(const ExifTagName tag) const;
6870
virtual bool setSource( const String& filename );
6971
virtual bool setSource( const Mat& buf );
7072
virtual int setScale( const int& scale_denom );
@@ -87,6 +89,7 @@ class BaseImageDecoder
8789
String m_signature;
8890
Mat m_buf;
8991
bool m_buf_supported;
92+
ExifReader m_exif;
9093
};
9194

9295

modules/imgcodecs/src/grfmt_jpeg.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ bool JpegDecoder::readHeader()
244244

245245
if (state->cinfo.src != 0)
246246
{
247+
jpeg_save_markers(&state->cinfo, APP1, 0xffff);
247248
jpeg_read_header( &state->cinfo, TRUE );
248249

249250
state->cinfo.scale_num=1;
@@ -456,6 +457,29 @@ bool JpegDecoder::readData( Mat& img )
456457
}
457458
}
458459

460+
// Check for Exif marker APP1
461+
jpeg_saved_marker_ptr exif_marker = NULL;
462+
jpeg_saved_marker_ptr cmarker = cinfo->marker_list;
463+
while( cmarker && exif_marker == NULL )
464+
{
465+
if (cmarker->marker == APP1)
466+
exif_marker = cmarker;
467+
468+
cmarker = cmarker->next;
469+
}
470+
471+
// Parse Exif data
472+
if( exif_marker )
473+
{
474+
const std::streamsize offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header
475+
476+
if (exif_marker->data_length > offsetToTiffHeader)
477+
{
478+
m_exif.parseExif(exif_marker->data + offsetToTiffHeader, exif_marker->data_length - offsetToTiffHeader);
479+
}
480+
}
481+
482+
459483
jpeg_start_decompress( cinfo );
460484

461485
buffer = (*cinfo->mem->alloc_sarray)((j_common_ptr)cinfo,

modules/imgcodecs/src/grfmt_jpeg.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,25 @@
5252

5353
namespace cv
5454
{
55+
/**
56+
* @brief Jpeg markers that can be encountered in a Jpeg file
57+
*/
58+
enum AppMarkerTypes
59+
{
60+
SOI = 0xD8, SOF0 = 0xC0, SOF2 = 0xC2, DHT = 0xC4,
61+
DQT = 0xDB, DRI = 0xDD, SOS = 0xDA,
62+
63+
RST0 = 0xD0, RST1 = 0xD1, RST2 = 0xD2, RST3 = 0xD3,
64+
RST4 = 0xD4, RST5 = 0xD5, RST6 = 0xD6, RST7 = 0xD7,
65+
66+
APP0 = 0xE0, APP1 = 0xE1, APP2 = 0xE2, APP3 = 0xE3,
67+
APP4 = 0xE4, APP5 = 0xE5, APP6 = 0xE6, APP7 = 0xE7,
68+
APP8 = 0xE8, APP9 = 0xE9, APP10 = 0xEA, APP11 = 0xEB,
69+
APP12 = 0xEC, APP13 = 0xED, APP14 = 0xEE, APP15 = 0xEF,
70+
71+
COM = 0xFE, EOI = 0xD9
72+
};
73+
5574

5675
class JpegDecoder CV_FINAL : public BaseImageDecoder
5776
{

modules/imgcodecs/src/grfmt_png.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,22 @@ bool PngDecoder::readData( Mat& img )
284284
png_read_image( png_ptr, buffer );
285285
png_read_end( png_ptr, end_info );
286286

287+
#ifdef PNG_eXIf_SUPPORTED
288+
png_uint_32 num_exif = 0;
289+
png_bytep exif = 0;
290+
291+
// Exif info could be in info_ptr (intro_info) or end_info per specification
292+
if( png_get_valid(png_ptr, info_ptr, PNG_INFO_eXIf) )
293+
png_get_eXIf_1(png_ptr, info_ptr, &num_exif, &exif);
294+
else if( png_get_valid(png_ptr, end_info, PNG_INFO_eXIf) )
295+
png_get_eXIf_1(png_ptr, end_info, &num_exif, &exif);
296+
297+
if( exif && num_exif > 0 )
298+
{
299+
m_exif.parseExif(exif, num_exif);
300+
}
301+
#endif
302+
287303
result = true;
288304
}
289305
}

0 commit comments

Comments
 (0)