1 // -*- c-basic-offset: 4 -*-
2 /** @file Exiv2Helper.cpp
3  *
4  *  @brief helper functions to work with Exif data via the exiv2 library
5  *
6  *
7  *  @author Pablo d'Angelo, T. Modes
8  *
9  */
10 
11 /*
12  *  This is free software; you can redistribute it and/or
13  *  modify it under the terms of the GNU General Public
14  *  License as published by the Free Software Foundation; either
15  *  version 2 of the License, or (at your option) any later version.
16  *
17  *  This software is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  *  Lesser General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public
23  *  License along with this software. If not, see
24  *  <http://www.gnu.org/licenses/>.
25  *
26  */
27 
28 #include "Exiv2Helper.h"
29 #include "hugin_math/hugin_math.h"
30 #include "hugin_utils/utils.h"
31 #include <exiv2/exiv2.hpp>
32 
33 namespace HuginBase
34 {
35     namespace Exiv2Helper
36     {
37 
_getExiv2Value(Exiv2::ExifData & exifData,std::string keyName,long & value)38         bool _getExiv2Value(Exiv2::ExifData& exifData, std::string keyName, long & value)
39         {
40             Exiv2::ExifData::iterator itr = exifData.findKey(Exiv2::ExifKey(keyName));
41             if (itr != exifData.end() && itr->count())
42             {
43                 value = itr->toLong();
44                 return true;
45             }
46             else
47             {
48                 return false;
49             };
50         };
51 
_getExiv2Value(Exiv2::ExifData & exifData,std::string keyName,float & value)52         bool _getExiv2Value(Exiv2::ExifData& exifData, std::string keyName, float & value)
53         {
54             Exiv2::ExifData::iterator itr = exifData.findKey(Exiv2::ExifKey(keyName));
55             if (itr != exifData.end() && itr->count())
56             {
57                 value = itr->toFloat();
58                 return true;
59             }
60             else
61             {
62                 return false;
63             };
64         };
65 
_getExiv2Value(Exiv2::ExifData & exifData,std::string keyName,std::string & value)66         bool _getExiv2Value(Exiv2::ExifData& exifData, std::string keyName, std::string & value)
67         {
68             Exiv2::ExifData::iterator itr = exifData.findKey(Exiv2::ExifKey(keyName));
69             if (itr != exifData.end() && itr->count())
70             {
71                 value = itr->toString();
72                 return true;
73             }
74             else
75             {
76                 return false;
77             };
78         }
79 
_getExiv2Value(Exiv2::ExifData & exifData,std::string keyName,std::vector<float> & values)80         bool _getExiv2Value(Exiv2::ExifData& exifData, std::string keyName, std::vector<float> & values)
81         {
82             values.clear();
83             Exiv2::ExifData::iterator itr = exifData.findKey(Exiv2::ExifKey(keyName));
84             if (itr != exifData.end() && itr->count())
85             {
86                 for(long i=0; i<itr->count(); i++)
87                 {
88                     values.push_back(itr->toFloat(i));
89                 };
90                 return true;
91             }
92             else
93             {
94                 return false;
95             }
96         }
97 
_getExiv2Value(Exiv2::ExifData & exifData,uint16_t tagID,std::string groupName,std::string & value)98         bool _getExiv2Value(Exiv2::ExifData& exifData, uint16_t tagID, std::string groupName, std::string & value)
99         {
100             Exiv2::ExifData::iterator itr = exifData.findKey(Exiv2::ExifKey(tagID, groupName));
101             if (itr != exifData.end() && itr->count())
102             {
103                 value = itr->toString();
104                 return true;
105             }
106             else
107             {
108                 return false;
109             };
110         };
111 
_getExiv2Value(Exiv2::ExifData & exifData,uint16_t tagID,std::string groupName,double & value)112         bool _getExiv2Value(Exiv2::ExifData& exifData, uint16_t tagID, std::string groupName, double & value)
113         {
114             Exiv2::ExifData::iterator itr = exifData.findKey(Exiv2::ExifKey(tagID, groupName));
115             if (itr != exifData.end() && itr->count())
116             {
117                 value = itr->toFloat();
118                 return true;
119             }
120             else
121             {
122                 return false;
123             }
124         }
125 
getExiv2ValueDouble(Exiv2::ExifData & exifData,Exiv2::ExifData::const_iterator it)126         const double getExiv2ValueDouble(Exiv2::ExifData& exifData, Exiv2::ExifData::const_iterator it)
127         {
128             if(it!=exifData.end() && it->count())
129             {
130                 return it->toFloat();
131             }
132             return 0;
133         };
134 
getExiv2ValueDouble(Exiv2::ExifData & exifData,std::string keyName)135         const double getExiv2ValueDouble(Exiv2::ExifData& exifData, std::string keyName)
136         {
137             float d;
138             if(_getExiv2Value(exifData, keyName, d))
139             {
140                 return d;
141             }
142             return 0;
143         };
144 
getExiv2ValueString(Exiv2::ExifData & exifData,Exiv2::ExifData::const_iterator it)145         const std::string getExiv2ValueString(Exiv2::ExifData& exifData,Exiv2::ExifData::const_iterator it)
146         {
147             if(it!=exifData.end() && it->count())
148             {
149                 return hugin_utils::StrTrim(it->toString());
150             };
151             return std::string("");
152         };
153 
getExiv2ValueString(Exiv2::ExifData & exifData,std::string keyName)154         const std::string getExiv2ValueString(Exiv2::ExifData& exifData, std::string keyName)
155         {
156             std::string s;
157             if(_getExiv2Value(exifData, keyName, s))
158             {
159                 return hugin_utils::StrTrim(s);
160             }
161             return std::string("");
162         };
163 
getExiv2ValueLong(Exiv2::ExifData & exifData,Exiv2::ExifData::const_iterator it)164         const long getExiv2ValueLong(Exiv2::ExifData& exifData, Exiv2::ExifData::const_iterator it)
165         {
166             if(it!=exifData.end() && it->count())
167             {
168                 return it->toLong();
169             }
170             return 0;
171         };
172 
getExiv2ValueLong(Exiv2::ExifData & exifData,std::string keyName)173         const long getExiv2ValueLong(Exiv2::ExifData& exifData, std::string keyName)
174         {
175             long l;
176             if(_getExiv2Value(exifData, keyName, l))
177             {
178                 return l;
179             }
180             return 0;
181         };
182 
183         //for diagnostic
PrintTag(Exiv2::ExifData::iterator itr)184         void PrintTag(Exiv2::ExifData::iterator itr)
185         {
186             std::cout << itr->value() << " (" << itr->typeName() << ", size: " << itr->count() << ")" << std::endl;
187             if(itr->count()>1)
188             {
189                 std::cout << "[";
190                 for(long i=0; i<itr->count(); i++)
191                 {
192                     std::cout << itr->toFloat(i) << ",";
193                 }
194                 std::cout << "]" << std::endl;
195             };
196         };
197 
readRedBlueBalance(Exiv2::ExifData & exifData,double & redBalance,double & blueBalance)198         bool readRedBlueBalance(Exiv2::ExifData &exifData, double & redBalance, double & blueBalance)
199         {
200             redBalance=1.0;
201             blueBalance=1.0;
202             //Panasonic makernotes (also some Leica cams)
203             float val1=0, val2=0, val3=0;
204             std::vector<float> values;
205             if(_getExiv2Value(exifData, "Exif.Panasonic.WBRedLevel", val1) &&
206                _getExiv2Value(exifData, "Exif.Panasonic.WBGreenLevel", val2) &&
207                _getExiv2Value(exifData, "Exif.Panasonic.WBBlueLevel", val3))
208             {
209                 if(val1!=0 && val2!=0 && val3!=0)
210                 {
211                     redBalance=val1 / val2;;
212                     blueBalance=val3 / val2;
213                     return true;
214                 }
215                 else
216                 {
217                     return false;
218                 };
219             };
220             // Pentax makernotes
221             if (_getExiv2Value(exifData, "Exif.Pentax.RedBalance", val1) &&
222                 _getExiv2Value(exifData, "Exif.Pentax.BlueBalance", val2))
223             {
224                 if(val1!=0 && val2!=0)
225                 {
226                     redBalance=val1 / 8192.0;
227                     blueBalance=val2 / 8192.0;
228                     return true;
229                 }
230                 else
231                 {
232                     return false;
233                 };
234             };
235 #if defined EXIV2_VERSION && EXIV2_VERSION >= EXIV2_MAKE_VERSION(0,23,0)
236             if (_getExiv2Value(exifData, "Exif.PentaxDng.RedBalance", val1) &&
237                 _getExiv2Value(exifData, "Exif.PentaxDng.BlueBalance", val2))
238             {
239                 if(val1!=0 && val2!=0)
240                 {
241                     redBalance=val1 / 256.0;
242                     blueBalance=val2 / 256.0;
243                     return true;
244                 }
245                 else
246                 {
247                     return false;
248                 };
249             };
250 #endif
251             //Olympus makernotes
252             if (_getExiv2Value(exifData, "Exif.Olympus.RedBalance", val1) &&
253                 _getExiv2Value(exifData, "Exif.Olympus.BlueBalance", val2))
254             {
255                 if(val1!=0 && val2!=0)
256                 {
257                     redBalance=val1 / 256.0;
258                     blueBalance=val2 / 256.0;
259                     return true;
260                 }
261                 else
262                 {
263                     return false;
264                 };
265             };
266             if(_getExiv2Value(exifData, "Exif.OlympusIp.WB_RBLevels", values))
267             {
268                 if(values.size()>=2)
269                 {
270                     if(values[0]!=0 && values[1]!=0)
271                     {
272                         redBalance=values[0]/256.0;
273                         blueBalance=values[1]/256.0;
274                         return true;
275                     }
276                     else
277                     {
278                         return false;
279                     };
280                 }
281                 else
282                 {
283                     return false;
284                 };
285             };
286             // Nikon makernotes
287             if(_getExiv2Value(exifData, "Exif.Nikon3.WB_RBLevels", values))
288             {
289                 if(values.size()>=2)
290                 {
291                     if(values[0]!=0 && values[1]!=0)
292                     {
293                         redBalance=values[0];
294                         blueBalance=values[1];
295                         return true;
296                     }
297                     else
298                     {
299                         return false;
300                     };
301                 }
302                 else
303                 {
304                     return false;
305                 };
306             };
307             if(_getExiv2Value(exifData, "Exif.NikonCb1.WB_RBGGLevels", values))
308             {
309                 if(values.size()==4)
310                 {
311                     if(values[0]!=0 && values[1]!=0 && values[2]!=0 && values[3]!=0)
312                     {
313                         redBalance=values[0] / values[2];
314                         blueBalance=values[1] / values[2];
315                         return true;
316                     }
317                     else
318                     {
319                         return false;
320                     };
321                 }
322                 else
323                 {
324                     return false;
325                 };
326             };
327             if(_getExiv2Value(exifData, "Exif.NikonCb2.WB_RGGBLevels", values))
328             {
329                 if(values.size()==4)
330                 {
331                     if(values[0]!=0 && values[1]!=0 && values[2]!=0 && values[3]!=0)
332                     {
333                         redBalance=values[0] / values[1];
334                         blueBalance=values[3] / values[1];
335                         return true;
336                     }
337                     else
338                     {
339                         return false;
340                     };
341                 }
342                 else
343                 {
344                     return false;
345                 };
346             };
347             if(_getExiv2Value(exifData, "Exif.NikonCb2a.WB_RGGBLevels", values))
348             {
349                 if(values.size()==4)
350                 {
351                     if(values[0]!=0 && values[1]!=0 && values[2]!=0 && values[3]!=0)
352                     {
353                         redBalance=values[0] / values[1];
354                         blueBalance=values[3] / values[1];
355                         return true;
356                     }
357                     else
358                     {
359                         return false;
360                     };
361                 }
362                 else
363                 {
364                     return false;
365                 };
366             };
367             if(_getExiv2Value(exifData, "Exif.NikonCb2b.WB_RGGBLevels", values))
368             {
369                 if(values.size()==4)
370                 {
371                     if(values[0]!=0 && values[1]!=0 && values[2]!=0 && values[3]!=0)
372                     {
373                         redBalance=values[0] / values[1];
374                         blueBalance=values[3] / values[1];
375                         return true;
376                     }
377                     else
378                     {
379                         return false;
380                     };
381                 }
382                 else
383                 {
384                     return false;
385                 };
386             };
387             if(_getExiv2Value(exifData, "Exif.NikonCb3.WB_RGBGLevels", values))
388             {
389                 if(values.size()==4)
390                 {
391                     if(values[0]!=0 && values[1]!=0 && values[2]!=0 && values[3]!=0)
392                     {
393                         redBalance=values[0] / values[1];
394                         blueBalance=values[2] / values[3];
395                         return true;
396                     }
397                     else
398                     {
399                         return false;
400                     };
401                 }
402                 else
403                 {
404                     return false;
405                 };
406             };
407 
408             return false;
409         };
410 
getCropFactor(Exiv2::ExifData & exifData,long width,long height)411         const double getCropFactor(Exiv2::ExifData &exifData, long width, long height)
412         {
413             double cropFactor=0;
414             // some cameras do not provide Exif.Image.ImageWidth / Length
415             // notably some Olympus
416             long eWidth = 0;
417             _getExiv2Value(exifData,"Exif.Image.ImageWidth",eWidth);
418 
419             long eLength = 0;
420             _getExiv2Value(exifData,"Exif.Image.ImageLength",eLength);
421 
422             double sensorPixelWidth = 0;
423             double sensorPixelHeight = 0;
424             if (eWidth > 0 && eLength > 0)
425             {
426                 sensorPixelHeight = (double)eLength;
427                 sensorPixelWidth = (double)eWidth;
428             }
429             else
430             {
431                 // No EXIF information, use number of pixels in image
432                 sensorPixelWidth = width;
433                 sensorPixelHeight = height;
434             }
435 
436             // force landscape sensor orientation
437             if (sensorPixelWidth < sensorPixelHeight )
438             {
439                 double t = sensorPixelWidth;
440                 sensorPixelWidth = sensorPixelHeight;
441                 sensorPixelHeight = t;
442             }
443 
444             DEBUG_DEBUG("sensorPixelWidth: " << sensorPixelWidth);
445             DEBUG_DEBUG("sensorPixelHeight: " << sensorPixelHeight);
446 
447             // some cameras do not provide Exif.Photo.FocalPlaneResolutionUnit
448             // notably some Olympus
449 
450             long exifResolutionUnits = 0;
451             _getExiv2Value(exifData,"Exif.Photo.FocalPlaneResolutionUnit",exifResolutionUnits);
452 
453             float resolutionUnits= 0;
454             switch (exifResolutionUnits)
455             {
456                 case 3: resolutionUnits = 10.0f; break;  //centimeter
457                 case 4: resolutionUnits = 1.0f; break;   //millimeter
458                 case 5: resolutionUnits = .001f; break;  //micrometer
459                 default: resolutionUnits = 25.4f; break; //inches
460             }
461 
462             DEBUG_DEBUG("Resolution Units: " << resolutionUnits);
463 
464             // some cameras do not provide Exif.Photo.FocalPlaneXResolution and
465             // Exif.Photo.FocalPlaneYResolution, notably some Olympus
466             float fplaneXresolution = 0;
467             _getExiv2Value(exifData,"Exif.Photo.FocalPlaneXResolution",fplaneXresolution);
468 
469             float fplaneYresolution = 0;
470             _getExiv2Value(exifData,"Exif.Photo.FocalPlaneYResolution",fplaneYresolution);
471 
472             float CCDWidth = 0;
473             if (fplaneXresolution != 0)
474             {
475                 CCDWidth = (float)(sensorPixelWidth / ( fplaneXresolution / resolutionUnits));
476             }
477 
478             float CCDHeight = 0;
479             if (fplaneYresolution != 0)
480             {
481                 CCDHeight = (float)(sensorPixelHeight / ( fplaneYresolution / resolutionUnits));
482             }
483 
484             DEBUG_DEBUG("CCDHeight:" << CCDHeight);
485             DEBUG_DEBUG("CCDWidth: " << CCDWidth);
486 
487             // calc sensor dimensions if not set and 35mm focal length is available
488             hugin_utils::FDiff2D sensorSize;
489             if (CCDHeight > 0 && CCDWidth > 0)
490             {
491                 // read sensor size directly.
492                 sensorSize.x = CCDWidth;
493                 sensorSize.y = CCDHeight;
494                 std::string exifModel;
495                 if(_getExiv2Value(exifData, "Exif.Image.Model", exifModel))
496                 {
497                     if (exifModel == "Canon EOS 20D")
498                     {
499                         // special case for buggy 20D camera
500                         sensorSize.x = 22.5;
501                         sensorSize.y = 15;
502                     }
503                 };
504                 // check if sensor size ratio and image size fit together
505                 double rsensor = (double)sensorSize.x / sensorSize.y;
506                 double rimg = (double) width / height;
507                 if ( (rsensor > 1 && rimg < 1) || (rsensor < 1 && rimg > 1) )
508                 {
509                     // image and sensor ratio do not match
510                     // swap sensor sizes
511                     float t;
512                     t = sensorSize.y;
513                     sensorSize.y = sensorSize.x;
514                     sensorSize.x = t;
515                 }
516 
517                 DEBUG_DEBUG("sensorSize.y: " << sensorSize.y);
518                 DEBUG_DEBUG("sensorSize.x: " << sensorSize.x);
519 
520                 cropFactor = sqrt(36.0*36.0+24.0*24.0) /
521                     sqrt(sensorSize.x*sensorSize.x + sensorSize.y*sensorSize.y);
522                 // FIXME: HACK guard against invalid image focal plane definition in EXIF metadata with arbitrarly chosen limits for the crop factor ( 1/100 < crop < 100)
523                 if (cropFactor < 0.1 || cropFactor > 100)
524                 {
525                     cropFactor = 0;
526                 }
527             }
528             else
529             {
530                 // alternative way to calculate the crop factor for Olympus cameras
531 
532                 // Windows debug stuff
533                 // left in as example on how to get "console output"
534                 // written to a log file
535                 // freopen ("oly.log","a",stdout);
536                 // fprintf (stdout,"Starting Alternative crop determination\n");
537 
538                 float olyFPD = 0;
539                 _getExiv2Value(exifData,"Exif.Olympus.FocalPlaneDiagonal",olyFPD);
540                 if (olyFPD > 0.0)
541                 {
542                     // Windows debug stuff
543                     // fprintf(stdout,"Oly_FPD:");
544                     // fprintf(stdout,"%f",olyFPD);
545                     cropFactor = sqrt(36.0*36.0+24.0*24.0) / olyFPD;
546                 }
547                 else
548                 {
549                     // for newer Olympus cameras the FocalPlaneDiagonal tag was moved into
550                     // equipment (sub?)-directory, so check also there
551                     _getExiv2Value(exifData,"Exif.OlympusEq.FocalPlaneDiagonal",olyFPD);
552                     if (olyFPD > 0.0)
553                     {
554                         cropFactor = sqrt(36.0*36.0+24.0*24.0) / olyFPD;
555                     };
556                 };
557             };
558             return cropFactor;
559         };
560 
getLensName(Exiv2::ExifData & exifData)561         const std::string getLensName(Exiv2::ExifData &exifData)
562         {
563             std::string lensName;
564             // first we are reading LensModel in Exif section, this is only available
565             // with EXIF >= 2.3
566 #if defined EXIV2_VERSION && EXIV2_VERSION >= EXIV2_MAKE_VERSION(0,22,0)
567             //the string "Exif.Photo.LensModel" is only defined in exiv2 0.22.0 and above
568             if(_getExiv2Value(exifData, "Exif.Photo.LensModel", lensName))
569 #else
570             if(_getExiv2Value(exifData, 0xa434, "Photo", lensName))
571 #endif
572             {
573                 if(lensName.length()>0)
574                 {
575                     return lensName;
576                 };
577             }
578             else
579             {
580                 //no lens in Exif found, now look in makernotes
581                 Exiv2::ExifData::const_iterator itr2 = Exiv2::lensName(exifData);
582                 if (itr2!=exifData.end() && itr2->count())
583                 {
584                     //we are using prettyPrint function to get string of lens name
585                     //it2->toString returns for many cameras only an ID number
586                     lensName=itr2->print(&exifData);
587                     //check returned lens name
588                     if(lensName.length()>0)
589                     {
590                         //for Canon it can contain (65535) or (0) for unknown lenses
591                         //for Pentax it can contain Unknown (0xHEX)
592                         if(lensName.compare(0, 1, "(")!=0 && lensName.compare(0, 7, "Unknown")!=0)
593                         {
594                             return lensName;
595                         }
596                     };
597                 };
598             };
599             return std::string("");
600         };
601 
602     }; //namespace Exiv2Helper
603 }; //namespace HuginBase