Skip to content

Commit 93783df

Browse files
committed
Merge pull request opencv#19439 from raaldrid:Exif_support_for_PNG_issue_16579
2 parents 7079e7e + 650836d commit 93783df

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)