Add support for Exif.Canon.AF tags to be read from images

This PR (and fix981_canonAutoFocus2) use a decoder listed in TiffMappingInfo to
decode Exif.Canon.AFInfo. The decoding function "manufactures" Exif tags such as
Exif.Canon.AFNumPoints from the data in Exif.Canon.AFInfo. These tags must never
be written to file and are removed from the metadata in
exif.cpp/ExifParser::encode().

Three of the tags created (AFPointsInFocus,AFPointsSelected, AFPrimaryPoint) are
bitmasks. As the camera can have up to 64 focus points, the tags are a 64 bit
mask to say which points are active. The function printBitmask() reports data
such as 1,2,3 or (none).

This decoding function decodeCanonAFInfo() added to TiffMappingInfo manufactures
the new tags. Normally, tags are processed by the binary tag decoder and that
approach was taken in branch fix981_canonAf. However, the binary tag decoder
cannot deal with AFInfo because the size of some metadata arrays cannot be
determined at compile time.
v0.27.3
clanmills 6 years ago committed by Dan Čermák
parent db70528190
commit 90f9f0bc19
No known key found for this signature in database
GPG Key ID: E632C3380610D1C5

@ -406,6 +406,23 @@ namespace Exiv2 {
{ 2, N_("Adobe RGB") } { 2, N_("Adobe RGB") }
}; };
extern const TagDetails canonAFAreaMode[] = {
{ 0, N_("Off (Manual Focus)") },
{ 1, N_("AF Point Expansion (surround)")},
{ 2, N_("Single-point AF") },
{ 4, N_("Multi-point AF") },
{ 5, N_("Face Detect AF") },
{ 6, N_("Face + Tracking") },
{ 7, N_("Zone AF") },
{ 8, N_("AF Point Expansion (4 point)") },
{ 9, N_("Spot AF") },
{ 10, N_("AF Point Expansion (8 point)") },
{ 11, N_("Flexizone Multi (49 point)") },
{ 12, N_("Flexizone Multi (9 point)") },
{ 13, N_("Flexizone Single") },
{ 14, N_("Large Zone AF") },
};
// Canon MakerNote Tag Info // Canon MakerNote Tag Info
const TagInfo CanonMakerNote::tagInfo_[] = { const TagInfo CanonMakerNote::tagInfo_[] = {
TagInfo(0x0000, "0x0000", "0x0000", N_("Unknown"), canonId, makerTags, unsignedShort, -1, printValue), TagInfo(0x0000, "0x0000", "0x0000", N_("Unknown"), canonId, makerTags, unsignedShort, -1, printValue),
@ -442,6 +459,21 @@ namespace Exiv2 {
TagInfo(0x00c1, "0x00c1", "0x00c1", N_("Unknown"), canonId, makerTags, unsignedShort, -1, printValue), TagInfo(0x00c1, "0x00c1", "0x00c1", N_("Unknown"), canonId, makerTags, unsignedShort, -1, printValue),
TagInfo(0x00d0, "VRDOffset", N_("VRD Offset"), N_("VRD offset"), canonId, makerTags, unsignedLong, -1, printValue), TagInfo(0x00d0, "VRDOffset", N_("VRD Offset"), N_("VRD offset"), canonId, makerTags, unsignedLong, -1, printValue),
TagInfo(0x00e0, "SensorInfo", N_("Sensor Info"), N_("Sensor info"), canonId, makerTags, unsignedShort, -1, printValue), TagInfo(0x00e0, "SensorInfo", N_("Sensor Info"), N_("Sensor info"), canonId, makerTags, unsignedShort, -1, printValue),
TagInfo(0x2600, "AFInfoSize", N_("AF InfoSize"), N_("AF InfoSize"), canonId, makerTags, signedShort, -1, printValue),
TagInfo(0x2601, "AFAreaMode", N_("AF Area Mode"), N_("AF Area Mode"), canonId, makerTags, signedShort, -1, EXV_PRINT_TAG(canonAFAreaMode)),
TagInfo(0x2602, "AFNumPoints", N_("AF NumPoints"), N_("AF NumPoints"), canonId, makerTags, signedShort, -1, printValue),
TagInfo(0x2603, "AFValidPoints", N_("AF ValidPoints"), N_("AF ValidPoints"), canonId, makerTags, signedShort, -1, printValue),
TagInfo(0x2604, "AFCanonImageWidth", N_("AF ImageWidth"), N_("AF ImageWidth"), canonId, makerTags, signedShort, -1, printValue),
TagInfo(0x2605, "AFCanonImageHeight", N_("AF ImageHeight"), N_("AF ImageHeight"), canonId, makerTags, signedShort, -1, printValue),
TagInfo(0x2606, "AFImageWidth", N_("AF Width"), N_("AF Width"), canonId, makerTags, signedShort, -1, printValue),
TagInfo(0x2607, "AFImageHeight", N_("AF Height"), N_("AF Height"), canonId, makerTags, signedShort, -1, printValue),
TagInfo(0x2608, "AFAreaWidths", N_("AF Area Widths"), N_("AF Area Widths"), canonId, makerTags, signedShort, -1, printValue),
TagInfo(0x2609, "AFAreaHeights", N_("AF Area Heights"), N_("AF Area Heights"), canonId, makerTags, signedShort, -1, printValue),
TagInfo(0x260a, "AFXPositions", N_("AF X Positions"), N_("AF X Positions"), canonId, makerTags, signedShort, -1, printValue),
TagInfo(0x260b, "AFYPositions", N_("AF Y Positions"), N_("AF Y Positions"), canonId, makerTags, signedShort, -1, printValue),
TagInfo(0x260c, "AFPointsInFocus", N_("AF Points in Focus"), N_("AF Points in Focus"), canonId, makerTags, signedShort, -1,printBitmask),
TagInfo(0x260d, "AFPointsSelected", N_("AF Points Selected"), N_("AF Points Selected"), canonId, makerTags, signedShort, -1, printBitmask),
TagInfo(0x260e, "AFPrimaryPoint", N_("AF Primary Point"), N_("AF Primary Point"), canonId, makerTags, signedShort, -1, printBitmask),
TagInfo(0x4001, "ColorData", N_("Color Data"), N_("Color data"), canonId, makerTags, unsignedShort, -1, printValue), TagInfo(0x4001, "ColorData", N_("Color Data"), N_("Color data"), canonId, makerTags, unsignedShort, -1, printValue),
// End of list marker // End of list marker
TagInfo(0xffff, "(UnknownCanonMakerNoteTag)", "(UnknownCanonMakerNoteTag)", N_("Unknown CanonMakerNote tag"), canonId, makerTags, asciiString, -1, printValue) TagInfo(0xffff, "(UnknownCanonMakerNoteTag)", "(UnknownCanonMakerNoteTag)", N_("Unknown CanonMakerNote tag"), canonId, makerTags, asciiString, -1, printValue)

@ -660,7 +660,23 @@ namespace Exiv2 {
"Exif.Image.StripByteCounts", "Exif.Image.StripByteCounts",
"Exif.Image.JPEGInterchangeFormat", "Exif.Image.JPEGInterchangeFormat",
"Exif.Image.JPEGInterchangeFormatLength", "Exif.Image.JPEGInterchangeFormatLength",
"Exif.Image.SubIFDs" "Exif.Image.SubIFDs",
// Issue 981. Never allow manufactured data to be written
"Exif.Canon.AFInfoSize",
"Exif.Canon.AFAreaMode",
"Exif.Canon.AFNumPoints",
"Exif.Canon.AFValidPoints",
"Exif.Canon.AFCanonImageWidth",
"Exif.Canon.AFCanonImageHeight",
"Exif.Canon.AFImageWidth",
"Exif.Canon.AFImageHeight",
"Exif.Canon.AFAreaWidths",
"Exif.Canon.AFAreaHeights",
"Exif.Canon.AFXPositions",
"Exif.Canon.AFYPositions",
"Exif.Canon.AFPointsInFocus",
"Exif.Canon.AFPointsSelected",
"Exif.Canon.AFPrimaryPoint",
}; };
for (unsigned int i = 0; i < EXV_COUNTOF(filteredIfd0Tags); ++i) { for (unsigned int i = 0; i < EXV_COUNTOF(filteredIfd0Tags); ++i) {
ExifData::iterator pos = ed.findKey(ExifKey(filteredIfd0Tags[i])); ExifData::iterator pos = ed.findKey(ExifKey(filteredIfd0Tags[i]));

@ -1944,7 +1944,7 @@ namespace Exiv2 {
TagInfo(0xb002, "MPFImageList", N_("MPFImageList"), TagInfo(0xb002, "MPFImageList", N_("MPFImageList"),
N_("MPF Image List"), N_("MPF Image List"),
mpfId, mpfTags, asciiString, 0, printValue), mpfId, mpfTags, asciiString, 0, printValue),
TagInfo(0xb003, "MPFImageUIDList", N_("MPFImageUIDList "), TagInfo(0xb003, "MPFImageUIDList", N_("MPFImageUIDList "),
N_("MPF Image UID List"), N_("MPF Image UID List"),
mpfId, mpfTags, unsignedLong, 1, printValue), mpfId, mpfTags, unsignedLong, 1, printValue),
TagInfo(0xb004, "MPFTotalFrames", N_("MPFTotalFrames"), TagInfo(0xb004, "MPFTotalFrames", N_("MPFTotalFrames"),
@ -2164,6 +2164,32 @@ namespace Exiv2 {
return os << value; return os << value;
} }
std::ostream& printBitmask(std::ostream& os, const Value& value, const ExifData* metadata)
{
if (value.typeId() == Exiv2::unsignedShort || value.typeId() == Exiv2::signedShort)
{
uint16_t bit = 0;
uint16_t comma = 0;
for (uint16_t i = 0; i < value.count(); i++ ) { // for each element in value array
uint16_t bits = static_cast<uint16_t>(value.toLong(i));
for (uint16_t b = 0; b < 16; ++b) { // for every bit
if (bits & (1 << b)) {
if ( comma++ ) {
os << ",";
}
os << bit;
}
bit++ ;
}
}
// if no bits are set, print "(none)"
if ( !comma ) os << N_("(none)");
} else {
printValue(os,value,metadata);
}
return os;
}
float fnumber(float apertureValue) float fnumber(float apertureValue)
{ {
return std::exp(std::log(2.0f) * apertureValue / 2.f); return std::exp(std::log(2.0f) * apertureValue / 2.f);

@ -441,6 +441,8 @@ namespace Exiv2 {
std::ostream& printXmpVersion(std::ostream& os, const Value& value, const ExifData*); std::ostream& printXmpVersion(std::ostream& os, const Value& value, const ExifData*);
//! Print a date following the format YYYY-MM-DDTHH:MM:SSZ //! Print a date following the format YYYY-MM-DDTHH:MM:SSZ
std::ostream& printXmpDate(std::ostream& os, const Value& value, const ExifData*); std::ostream& printXmpDate(std::ostream& os, const Value& value, const ExifData*);
//! Print a bitmask as (none) | n | n,m... where: (none) = no bits set | n = bit n from left (0=left-most) | n,m.. = multiple bits "
std::ostream& printBitmask(std::ostream& os, const Value& value, const ExifData*);
//@} //@}
//! Calculate F number from an APEX aperture value //! Calculate F number from an APEX aperture value

@ -1514,7 +1514,8 @@ namespace Exiv2 {
{ "*", Tag::all, ignoreId, 0, 0 }, // Do not decode tags with group == ignoreId { "*", Tag::all, ignoreId, 0, 0 }, // Do not decode tags with group == ignoreId
{ "*", 0x02bc, ifd0Id, &TiffDecoder::decodeXmp, 0 /*done before the tree is traversed*/ }, { "*", 0x02bc, ifd0Id, &TiffDecoder::decodeXmp, 0 /*done before the tree is traversed*/ },
{ "*", 0x83bb, ifd0Id, &TiffDecoder::decodeIptc, 0 /*done before the tree is traversed*/ }, { "*", 0x83bb, ifd0Id, &TiffDecoder::decodeIptc, 0 /*done before the tree is traversed*/ },
{ "*", 0x8649, ifd0Id, &TiffDecoder::decodeIptc, 0 /*done before the tree is traversed*/ } { "*", 0x8649, ifd0Id, &TiffDecoder::decodeIptc, 0 /*done before the tree is traversed*/ },
{ "*", 0x0026, canonId, &TiffDecoder::decodeCanonAFInfo, 0 /* Exiv2.Canon.AFInfo is read-only */ },
}; };
DecoderFct TiffMapping::findDecoder(const std::string& make, DecoderFct TiffMapping::findDecoder(const std::string& make,

@ -45,6 +45,7 @@
#include <iomanip> #include <iomanip>
#include <cassert> #include <cassert>
#include <limits> #include <limits>
#include <ostream>
// ***************************************************************************** // *****************************************************************************
namespace { namespace {
@ -462,6 +463,78 @@ namespace Exiv2 {
} }
} // TiffMetadataDecoder::decodeIptc } // TiffMetadataDecoder::decodeIptc
static const TagInfo* findTag(const TagInfo* pList,uint16_t tag)
{
while ( pList->tag_ != 0xffff && pList->tag_ != tag ) pList++;
return pList->tag_ != 0xffff ? pList : NULL;
}
void TiffDecoder::decodeCanonAFInfo(const TiffEntryBase* object) {
// report Exif.Canon.AFInfo as usual
TiffDecoder::decodeStdTiffEntry(object);
if ( object->pValue()->count() < 3 || object->pValue()->typeId() != unsignedShort ) return; // insufficient data
// create vector of signedShorts from unsignedShorts in Exif.Canon.AFInfo
std::vector<int16_t> ints;
std::vector<uint16_t> uint;
for (int i = 0; i < object->pValue()->count(); i++) {
ints.push_back((int16_t) object->pValue()->toLong(i));
uint.push_back((uint16_t) object->pValue()->toLong(i));
}
// Check this is AFInfo2 (ints[0] = bytes in object)
if ( ints[0] != object->pValue()->count()*2 ) return ;
std::string familyGroup(std::string("Exif.") + groupName(object->group()) + ".");
const uint16_t nPoints = uint.at(2);
const uint16_t nMasks = (nPoints+15)/(sizeof(uint16_t) * 8);
int nStart = 0;
struct {
uint16_t tag ;
uint16_t size ;
bool bSigned;
} records[] = {
{ 0x2600 , 1 , true }, // AFInfoSize
{ 0x2601 , 1 , true }, // AFAreaMode
{ 0x2602 , 1 , true }, // AFNumPoints
{ 0x2603 , 1 , true }, // AFValidPoints
{ 0x2604 , 1 , true }, // AFCanonImageWidth
{ 0x2605 , 1 , true }, // AFCanonImageHeight
{ 0x2606 , 1 , true }, // AFImageWidth"
{ 0x2607 , 1 , true }, // AFImageHeight
{ 0x2608 , nPoints , true }, // AFAreaWidths
{ 0x2609 , nPoints , true }, // AFAreaHeights
{ 0x260a , nPoints , true }, // AFXPositions
{ 0x260b , nPoints , true }, // AFYPositions
{ 0x260c , nMasks , false }, // AFPointsInFocus
{ 0x260d , nMasks , false }, // AFPointsSelected
{ 0x260e , nMasks , false }, // AFPrimaryPoint
{ 0xffff , 0 , true } // end marker
};
// check we have enough data!
uint16_t count = 0;
for ( uint16_t i = 0; records[i].tag != 0xffff ; i++) count += records[i].size ;
if ( count > ints.size() ) return ;
for ( uint16_t i = 0; records[i].tag != 0xffff ; i++) {
const TagInfo* pTags = ExifTags::tagList("Canon") ;
const TagInfo* pTag = findTag(pTags,records[i].tag);
if ( pTag ) {
Exiv2::Value::AutoPtr v = Exiv2::Value::create(records[i].bSigned?Exiv2::signedShort:Exiv2::unsignedShort);
std::ostringstream s;
if ( records[i].bSigned ) {
for ( int16_t k = 0 ; k < records[i].size ; k++ ) s << " " << ints.at(nStart++);
} else {
for ( int16_t k = 0 ; k < records[i].size ; k++ ) s << " " << uint.at(nStart++);
}
v->read(s.str());
exifData_[familyGroup + pTag->name_] = *v;
}
}
}
void TiffDecoder::decodeTiffEntry(const TiffEntryBase* object) void TiffDecoder::decodeTiffEntry(const TiffEntryBase* object)
{ {
assert(object != 0); assert(object != 0);

@ -332,6 +332,8 @@ namespace Exiv2 {
void decodeIptc(const TiffEntryBase* object); void decodeIptc(const TiffEntryBase* object);
//! Decode XMP packet from an XMLPacket tag //! Decode XMP packet from an XMLPacket tag
void decodeXmp(const TiffEntryBase* object); void decodeXmp(const TiffEntryBase* object);
//! Decode Exif.Canon.AFInfo
void decodeCanonAFInfo(const TiffEntryBase* object);
//@} //@}
private: private:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
from system_tests import CaseMeta, path
class CanonAfInfoTest(metaclass=CaseMeta):
filenameA = path("$data_path/test_issue_981a.exv")
filenameB = path("$data_path/test_issue_981b.exv")
filenameC = path("$data_path/test_issue_981c.exv")
filenameC = path("$data_path/test_issue_981c.exv")
filenameD = path("$data_path/test_issue_981d.exv")
commands = ["$exiv2 -pa --grep Canon.AF $filenameA",
"$exiv2 -pa --grep Canon.AF $filenameB",
"$exiv2 -pv --grep Points $filenameC",
"$exiv2 -pt --grep Points $filenameC",
"$exiv2 -pv --grep Primary $filenameD",
"$exiv2 -pt --grep Primary $filenameD",
]
stdout = ["""Exif.Canon.AFInfo Short 48 96 2 9 9 4752 3168 4272 2848 115 115 115 162 200 162 115 115 115 153 153 153 105 199 105 153 153 153 64409 64862 64862 0 0 0 674 674 1127 0 321 65215 603 0 64933 321 65215 0 16 256 0 65535
Exif.Canon.AFInfoSize SShort 1 96
Exif.Canon.AFAreaMode SShort 1 Single-point AF
Exif.Canon.AFNumPoints SShort 1 9
Exif.Canon.AFValidPoints SShort 1 9
Exif.Canon.AFCanonImageWidth SShort 1 4752
Exif.Canon.AFCanonImageHeight SShort 1 3168
Exif.Canon.AFImageWidth SShort 1 4272
Exif.Canon.AFImageHeight SShort 1 2848
Exif.Canon.AFAreaWidths SShort 9 115 115 115 162 200 162 115 115 115
Exif.Canon.AFAreaHeights SShort 9 153 153 153 105 199 105 153 153 153
Exif.Canon.AFXPositions SShort 9 -1127 -674 -674 0 0 0 674 674 1127
Exif.Canon.AFYPositions SShort 9 0 321 -321 603 0 -603 321 -321 0
Exif.Canon.AFPointsInFocus Short 1 4
Exif.Canon.AFPointsSelected Short 1 8
Exif.Canon.AFPrimaryPoint Short 1 (none)
""" , """Exif.Canon.AFInfo Short 273 546 2 63 61 6720 4480 6720 4480 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 0 0 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 0 0 65200 64790 64435 64099 63764 336 0 65200 64099 63764 1772 1437 1101 746 336 0 1437 1101 746 336 0 65200 64790 64435 336 0 65200 64790 64435 64099 63764 1772 64790 64435 64099 63764 1772 1437 1101 746 63764 1772 1437 1101 746 336 0 65200 1101 746 336 0 65200 64790 64435 64099 336 0 65200 1772 1437 0 0 547 625 625 625 625 821 821 821 308 308 625 625 625 625 547 547 308 308 308 274 274 274 308 308 0 0 0 0 0 0 0 308 65228 65228 65228 65228 0 0 0 0 64911 65228 65228 65228 65228 65262 65262 65262 64911 64911 64989 64989 64989 64911 64911 64911 64715 64715 64715 64911 64911 0 0 0 512 0 0 0 512 0 0 0 0 0 0 65535
Exif.Canon.AFInfoSize SShort 1 546
Exif.Canon.AFAreaMode SShort 1 Single-point AF
Exif.Canon.AFNumPoints SShort 1 63
Exif.Canon.AFValidPoints SShort 1 61
Exif.Canon.AFCanonImageWidth SShort 1 6720
Exif.Canon.AFCanonImageHeight SShort 1 4480
Exif.Canon.AFImageWidth SShort 1 6720
Exif.Canon.AFImageHeight SShort 1 4480
Exif.Canon.AFAreaWidths SShort 63 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 0 0
Exif.Canon.AFAreaHeights SShort 63 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 218 0 0
Exif.Canon.AFXPositions SShort 63 -336 -746 -1101 -1437 -1772 336 0 -336 -1437 -1772 1772 1437 1101 746 336 0 1437 1101 746 336 0 -336 -746 -1101 336 0 -336 -746 -1101 -1437 -1772 1772 -746 -1101 -1437 -1772 1772 1437 1101 746 -1772 1772 1437 1101 746 336 0 -336 1101 746 336 0 -336 -746 -1101 -1437 336 0 -336 1772 1437 0 0
Exif.Canon.AFYPositions SShort 63 547 625 625 625 625 821 821 821 308 308 625 625 625 625 547 547 308 308 308 274 274 274 308 308 0 0 0 0 0 0 0 308 -308 -308 -308 -308 0 0 0 0 -625 -308 -308 -308 -308 -274 -274 -274 -625 -625 -547 -547 -547 -625 -625 -625 -821 -821 -821 -625 -625 0 0
Exif.Canon.AFPointsInFocus Short 4 25
Exif.Canon.AFPointsSelected Short 4 25
Exif.Canon.AFPrimaryPoint Short 4 (none)
""","""0x2602 Canon AFNumPoints SShort 1 63
0x2603 Canon AFValidPoints SShort 1 61
0x260c Canon AFPointsInFocus Short 4 0 560 57344 0
0x260d Canon AFPointsSelected Short 4 0 1848 57344 0
""","""Exif.Canon.AFNumPoints SShort 1 63
Exif.Canon.AFValidPoints SShort 1 61
Exif.Canon.AFPointsInFocus Short 4 20,21,25,45,46,47
Exif.Canon.AFPointsSelected Short 4 19,20,21,24,25,26,45,46,47
""","""0x260e Canon AFPrimaryPoint Short 4 3608 49152 792 6272
""","""Exif.Canon.AFPrimaryPoint Short 4 3,4,9,10,11,30,31,35,36,40,41,55,59,60
"""
]
stderr = [""]*len(commands)
retval = [ 0]*len(commands)
Loading…
Cancel
Save