mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 07:43:03 +04:00
Merge remote-tracking branch 'upstream/3.4' into merge-3.4
This commit is contained in:
+28
-110
@@ -62,7 +62,7 @@ ExifEntry_t::ExifEntry_t() :
|
||||
/**
|
||||
* @brief ExifReader constructor
|
||||
*/
|
||||
ExifReader::ExifReader(std::istream& stream) : m_stream(stream), m_format(NONE)
|
||||
ExifReader::ExifReader() : m_format(NONE)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -73,25 +73,6 @@ ExifReader::~ExifReader()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parsing the file and prepare (internally) exif directory structure
|
||||
* @return true if parsing was successful and exif information exists in JpegReader object
|
||||
* false in case of unsuccessful parsing
|
||||
*/
|
||||
bool ExifReader::parse()
|
||||
{
|
||||
try {
|
||||
m_exif = getExif();
|
||||
if( !m_exif.empty() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (ExifParsingError&) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get tag value by tag number
|
||||
@@ -101,10 +82,10 @@ bool ExifReader::parse()
|
||||
* @return ExifEntru_t structure. Caller has to know what tag it calls in order to extract proper field from the structure ExifEntry_t
|
||||
*
|
||||
*/
|
||||
ExifEntry_t ExifReader::getTag(const ExifTagName tag)
|
||||
ExifEntry_t ExifReader::getTag(const ExifTagName tag) const
|
||||
{
|
||||
ExifEntry_t entry;
|
||||
std::map<int, ExifEntry_t>::iterator it = m_exif.find(tag);
|
||||
std::map<int, ExifEntry_t>::const_iterator it = m_exif.find(tag);
|
||||
|
||||
if( it != m_exif.end() )
|
||||
{
|
||||
@@ -115,100 +96,37 @@ ExifEntry_t ExifReader::getTag(const ExifTagName tag)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get exif directory structure contained in file (if any)
|
||||
* This is internal function and is not exposed to client
|
||||
* @brief Parsing the exif data buffer and prepare (internal) exif directory
|
||||
*
|
||||
* @return Map where key is tag number and value is ExifEntry_t structure
|
||||
*/
|
||||
std::map<int, ExifEntry_t > ExifReader::getExif()
|
||||
{
|
||||
const std::streamsize markerSize = 2;
|
||||
const std::streamsize offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header
|
||||
unsigned char appMarker[markerSize];
|
||||
m_exif.erase( m_exif.begin(), m_exif.end() );
|
||||
|
||||
std::streamsize count;
|
||||
|
||||
bool exifFound = false, stopSearch = false;
|
||||
while( ( !m_stream.eof() ) && !exifFound && !stopSearch )
|
||||
{
|
||||
m_stream.read( reinterpret_cast<char*>(appMarker), markerSize );
|
||||
count = m_stream.gcount();
|
||||
if( count < markerSize )
|
||||
{
|
||||
break;
|
||||
}
|
||||
unsigned char marker = appMarker[1];
|
||||
size_t bytesToSkip;
|
||||
size_t exifSize;
|
||||
switch( marker )
|
||||
{
|
||||
//For all the markers just skip bytes in file pointed by followed two bytes (field size)
|
||||
case SOF0: case SOF2: case DHT: case DQT: case DRI: case SOS:
|
||||
case RST0: case RST1: case RST2: case RST3: case RST4: case RST5: case RST6: case RST7:
|
||||
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 COM:
|
||||
bytesToSkip = getFieldSize();
|
||||
if (bytesToSkip < markerSize) {
|
||||
throw ExifParsingError();
|
||||
}
|
||||
m_stream.seekg( static_cast<long>( bytesToSkip - markerSize ), m_stream.cur );
|
||||
if ( m_stream.fail() ) {
|
||||
throw ExifParsingError();
|
||||
}
|
||||
break;
|
||||
|
||||
//SOI and EOI don't have the size field after the marker
|
||||
case SOI: case EOI:
|
||||
break;
|
||||
|
||||
case APP1: //actual Exif Marker
|
||||
exifSize = getFieldSize();
|
||||
if (exifSize <= offsetToTiffHeader) {
|
||||
throw ExifParsingError();
|
||||
}
|
||||
m_data.resize( exifSize - offsetToTiffHeader );
|
||||
m_stream.seekg( static_cast<long>( offsetToTiffHeader ), m_stream.cur );
|
||||
if ( m_stream.fail() ) {
|
||||
throw ExifParsingError();
|
||||
}
|
||||
m_stream.read( reinterpret_cast<char*>(&m_data[0]), exifSize - offsetToTiffHeader );
|
||||
exifFound = true;
|
||||
break;
|
||||
|
||||
default: //No other markers are expected according to standard. May be a signal of error
|
||||
stopSearch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !exifFound )
|
||||
{
|
||||
return m_exif;
|
||||
}
|
||||
|
||||
parseExif();
|
||||
|
||||
return m_exif;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the size of exif field (required to properly ready whole exif from the file)
|
||||
* This is internal function and is not exposed to client
|
||||
* @param [in] data The data buffer to read EXIF data starting with endianness
|
||||
* @param [in] size The size of the data buffer
|
||||
*
|
||||
* @return size of exif field in the file
|
||||
* @return true if parsing was successful
|
||||
* false in case of unsuccessful parsing
|
||||
*/
|
||||
size_t ExifReader::getFieldSize ()
|
||||
bool ExifReader::parseExif(unsigned char* data, const size_t size)
|
||||
{
|
||||
unsigned char fieldSize[2];
|
||||
m_stream.read( reinterpret_cast<char*>(fieldSize), 2 );
|
||||
std::streamsize count = m_stream.gcount();
|
||||
if (count < 2)
|
||||
// Populate m_data, then call parseExif() (private)
|
||||
if( data && size > 0 )
|
||||
{
|
||||
return 0;
|
||||
m_data.assign(data, data + size);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
parseExif();
|
||||
if( !m_exif.empty() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch( ExifParsingError& ) {
|
||||
return false;
|
||||
}
|
||||
return ( fieldSize[0] << 8 ) + fieldSize[1];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -54,24 +54,6 @@
|
||||
|
||||
namespace cv
|
||||
{
|
||||
/**
|
||||
* @brief Jpeg markers that can encounter in Jpeg file
|
||||
*/
|
||||
enum AppMarkerTypes
|
||||
{
|
||||
SOI = 0xD8, SOF0 = 0xC0, SOF2 = 0xC2, DHT = 0xC4,
|
||||
DQT = 0xDB, DRI = 0xDD, SOS = 0xDA,
|
||||
|
||||
RST0 = 0xD0, RST1 = 0xD1, RST2 = 0xD2, RST3 = 0xD3,
|
||||
RST4 = 0xD4, RST5 = 0xD5, RST6 = 0xD6, RST7 = 0xD7,
|
||||
|
||||
APP0 = 0xE0, APP1 = 0xE1, APP2 = 0xE2, APP3 = 0xE3,
|
||||
APP4 = 0xE4, APP5 = 0xE5, APP6 = 0xE6, APP7 = 0xE7,
|
||||
APP8 = 0xE8, APP9 = 0xE9, APP10 = 0xEA, APP11 = 0xEB,
|
||||
APP12 = 0xEC, APP13 = 0xED, APP14 = 0xEE, APP15 = 0xEF,
|
||||
|
||||
COM = 0xFE, EOI = 0xD9
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Base Exif tags used by IFD0 (main image)
|
||||
@@ -168,19 +150,22 @@ class ExifReader
|
||||
public:
|
||||
/**
|
||||
* @brief ExifReader constructor. Constructs an object of exif reader
|
||||
*
|
||||
* @param [in]stream An istream to look for EXIF bytes from
|
||||
*/
|
||||
explicit ExifReader( std::istream& stream );
|
||||
ExifReader();
|
||||
~ExifReader();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Parse the file with exif info
|
||||
*
|
||||
* @return true if parsing was successful and exif information exists in JpegReader object
|
||||
* @param [in] data The data buffer to read EXIF data starting with endianness
|
||||
* @param [in] size The size of the data buffer
|
||||
*
|
||||
* @return true if successful parsing
|
||||
* false if parsing error
|
||||
*/
|
||||
bool parse();
|
||||
|
||||
bool parseExif(unsigned char* data, const size_t size);
|
||||
|
||||
/**
|
||||
* @brief Get tag info by tag number
|
||||
@@ -188,10 +173,10 @@ public:
|
||||
* @param [in] tag The tag number
|
||||
* @return ExifEntru_t structure. Caller has to know what tag it calls in order to extract proper field from the structure ExifEntry_t
|
||||
*/
|
||||
ExifEntry_t getTag( const ExifTagName tag );
|
||||
ExifEntry_t getTag( const ExifTagName tag ) const;
|
||||
|
||||
|
||||
private:
|
||||
std::istream& m_stream;
|
||||
std::vector<unsigned char> m_data;
|
||||
std::map<int, ExifEntry_t > m_exif;
|
||||
Endianess_t m_format;
|
||||
@@ -199,7 +184,6 @@ private:
|
||||
void parseExif();
|
||||
bool checkTagMark() const;
|
||||
|
||||
size_t getFieldSize ();
|
||||
size_t getNumDirEntry( const size_t offsetNumDir ) const;
|
||||
uint32_t getStartOffset() const;
|
||||
uint16_t getExifTag( const size_t offset ) const;
|
||||
@@ -215,7 +199,6 @@ private:
|
||||
|
||||
u_rational_t getURational( const size_t offset ) const;
|
||||
|
||||
std::map<int, ExifEntry_t > getExif();
|
||||
std::string getString( const size_t offset ) const;
|
||||
std::vector<u_rational_t> getResolution( const size_t offset ) const;
|
||||
std::vector<u_rational_t> getWhitePoint( const size_t offset ) const;
|
||||
|
||||
@@ -55,6 +55,11 @@ BaseImageDecoder::BaseImageDecoder()
|
||||
m_scale_denom = 1;
|
||||
}
|
||||
|
||||
|
||||
ExifEntry_t BaseImageDecoder::getExifTag(const ExifTagName tag) const
|
||||
{
|
||||
return m_exif.getTag(tag);
|
||||
}
|
||||
bool BaseImageDecoder::setSource( const String& filename )
|
||||
{
|
||||
m_filename = filename;
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "bitstrm.hpp"
|
||||
#include "exif.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
@@ -65,6 +66,7 @@ public:
|
||||
int height() const { return m_height; }
|
||||
virtual int type() const { return m_type; }
|
||||
|
||||
ExifEntry_t getExifTag(const ExifTagName tag) const;
|
||||
virtual bool setSource( const String& filename );
|
||||
virtual bool setSource( const Mat& buf );
|
||||
virtual int setScale( const int& scale_denom );
|
||||
@@ -87,6 +89,7 @@ protected:
|
||||
String m_signature;
|
||||
Mat m_buf;
|
||||
bool m_buf_supported;
|
||||
ExifReader m_exif;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -244,6 +244,7 @@ bool JpegDecoder::readHeader()
|
||||
|
||||
if (state->cinfo.src != 0)
|
||||
{
|
||||
jpeg_save_markers(&state->cinfo, APP1, 0xffff);
|
||||
jpeg_read_header( &state->cinfo, TRUE );
|
||||
|
||||
state->cinfo.scale_num=1;
|
||||
@@ -456,6 +457,29 @@ bool JpegDecoder::readData( Mat& img )
|
||||
}
|
||||
}
|
||||
|
||||
// Check for Exif marker APP1
|
||||
jpeg_saved_marker_ptr exif_marker = NULL;
|
||||
jpeg_saved_marker_ptr cmarker = cinfo->marker_list;
|
||||
while( cmarker && exif_marker == NULL )
|
||||
{
|
||||
if (cmarker->marker == APP1)
|
||||
exif_marker = cmarker;
|
||||
|
||||
cmarker = cmarker->next;
|
||||
}
|
||||
|
||||
// Parse Exif data
|
||||
if( exif_marker )
|
||||
{
|
||||
const std::streamsize offsetToTiffHeader = 6; //bytes from Exif size field to the first TIFF header
|
||||
|
||||
if (exif_marker->data_length > offsetToTiffHeader)
|
||||
{
|
||||
m_exif.parseExif(exif_marker->data + offsetToTiffHeader, exif_marker->data_length - offsetToTiffHeader);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
jpeg_start_decompress( cinfo );
|
||||
|
||||
buffer = (*cinfo->mem->alloc_sarray)((j_common_ptr)cinfo,
|
||||
|
||||
@@ -52,6 +52,25 @@
|
||||
|
||||
namespace cv
|
||||
{
|
||||
/**
|
||||
* @brief Jpeg markers that can be encountered in a Jpeg file
|
||||
*/
|
||||
enum AppMarkerTypes
|
||||
{
|
||||
SOI = 0xD8, SOF0 = 0xC0, SOF2 = 0xC2, DHT = 0xC4,
|
||||
DQT = 0xDB, DRI = 0xDD, SOS = 0xDA,
|
||||
|
||||
RST0 = 0xD0, RST1 = 0xD1, RST2 = 0xD2, RST3 = 0xD3,
|
||||
RST4 = 0xD4, RST5 = 0xD5, RST6 = 0xD6, RST7 = 0xD7,
|
||||
|
||||
APP0 = 0xE0, APP1 = 0xE1, APP2 = 0xE2, APP3 = 0xE3,
|
||||
APP4 = 0xE4, APP5 = 0xE5, APP6 = 0xE6, APP7 = 0xE7,
|
||||
APP8 = 0xE8, APP9 = 0xE9, APP10 = 0xEA, APP11 = 0xEB,
|
||||
APP12 = 0xEC, APP13 = 0xED, APP14 = 0xEE, APP15 = 0xEF,
|
||||
|
||||
COM = 0xFE, EOI = 0xD9
|
||||
};
|
||||
|
||||
|
||||
class JpegDecoder CV_FINAL : public BaseImageDecoder
|
||||
{
|
||||
|
||||
@@ -284,6 +284,22 @@ bool PngDecoder::readData( Mat& img )
|
||||
png_read_image( png_ptr, buffer );
|
||||
png_read_end( png_ptr, end_info );
|
||||
|
||||
#ifdef PNG_eXIf_SUPPORTED
|
||||
png_uint_32 num_exif = 0;
|
||||
png_bytep exif = 0;
|
||||
|
||||
// Exif info could be in info_ptr (intro_info) or end_info per specification
|
||||
if( png_get_valid(png_ptr, info_ptr, PNG_INFO_eXIf) )
|
||||
png_get_eXIf_1(png_ptr, info_ptr, &num_exif, &exif);
|
||||
else if( png_get_valid(png_ptr, end_info, PNG_INFO_eXIf) )
|
||||
png_get_eXIf_1(png_ptr, end_info, &num_exif, &exif);
|
||||
|
||||
if( exif && num_exif > 0 )
|
||||
{
|
||||
m_exif.parseExif(exif, num_exif);
|
||||
}
|
||||
#endif
|
||||
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,48 +361,15 @@ static void ExifTransform(int orientation, Mat& img)
|
||||
}
|
||||
}
|
||||
|
||||
static void ApplyExifOrientation(const String& filename, Mat& img)
|
||||
static void ApplyExifOrientation(ExifEntry_t orientationTag, Mat& img)
|
||||
{
|
||||
int orientation = IMAGE_ORIENTATION_TL;
|
||||
|
||||
if (filename.size() > 0)
|
||||
if (orientationTag.tag != INVALID_TAG)
|
||||
{
|
||||
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();
|
||||
orientation = orientationTag.field_u16; //orientation is unsigned short, so check field_u16
|
||||
ExifTransform(orientation, img);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -518,6 +485,12 @@ imread_( const String& filename, int flags, Mat& mat )
|
||||
resize( mat, mat, Size( size.width / scale_denom, size.height / scale_denom ), 0, 0, INTER_LINEAR_EXACT);
|
||||
}
|
||||
|
||||
/// optionally rotate the data if EXIF orientation flag says so
|
||||
if (!mat.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED )
|
||||
{
|
||||
ApplyExifOrientation(decoder->getExifTag(ORIENTATION), mat);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -614,7 +587,7 @@ imreadmulti_(const String& filename, int flags, std::vector<Mat>& mats)
|
||||
// optionally rotate the data if EXIF' orientation flag says so
|
||||
if( (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED )
|
||||
{
|
||||
ApplyExifOrientation(filename, mat);
|
||||
ApplyExifOrientation(decoder->getExifTag(ORIENTATION), mat);
|
||||
}
|
||||
|
||||
mats.push_back(mat);
|
||||
@@ -645,12 +618,6 @@ Mat imread( const String& filename, int flags )
|
||||
/// load the data
|
||||
imread_( filename, flags, img );
|
||||
|
||||
/// optionally rotate the data if EXIF' orientation flag says so
|
||||
if( !img.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED )
|
||||
{
|
||||
ApplyExifOrientation(filename, img);
|
||||
}
|
||||
|
||||
/// return a reference to the data
|
||||
return img;
|
||||
}
|
||||
@@ -889,6 +856,12 @@ imdecode_( const Mat& buf, int flags, Mat& mat )
|
||||
resize(mat, mat, Size( size.width / scale_denom, size.height / scale_denom ), 0, 0, INTER_LINEAR_EXACT);
|
||||
}
|
||||
|
||||
/// optionally rotate the data if EXIF' orientation flag says so
|
||||
if (!mat.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED)
|
||||
{
|
||||
ApplyExifOrientation(decoder->getExifTag(ORIENTATION), mat);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -900,12 +873,6 @@ Mat imdecode( InputArray _buf, int flags )
|
||||
Mat buf = _buf.getMat(), img;
|
||||
imdecode_( buf, flags, 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;
|
||||
}
|
||||
|
||||
@@ -917,12 +884,6 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst )
|
||||
dst = dst ? dst : &img;
|
||||
imdecode_( buf, flags, *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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user