rawTherapee/rtengine/imagedata.cc
2023-11-11 14:57:24 -08:00

1017 lines
34 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
*/
#include <functional>
#include <iostream>
#include <regex>
#include <sstream>
#include <strings.h>
#include <time.h>
#include <tiff.h>
#include <glib/gstdio.h>
#include <glibmm/convert.h>
#include "imagedata.h"
#include "imagesource.h"
#include "metadata.h"
#include "rt_math.h"
#include "utils.h"
#pragma GCC diagnostic warning "-Wextra"
#define PRINT_HDR_PS_DETECTION 0
using namespace rtengine;
namespace
{
const std::string& validateUft8(const std::string& str, const std::string& on_error = "???")
{
if (Glib::ustring(str).validate()) {
return str;
}
return on_error;
}
template <typename Iterator, typename Integer = std::size_t>
auto to_long(const Iterator &iter, Integer n = Integer{0}) -> decltype(
#if EXIV2_TEST_VERSION(0,28,0)
iter->toInt64()
) {
return iter->toInt64(n);
#else
iter->toLong()
) {
return iter->toLong(n);
#endif
}
}
namespace rtengine {
extern const Settings *settings;
} // namespace rtengine
FramesMetaData* FramesMetaData::fromFile(const Glib::ustring& fname)
{
return new FramesData(fname);
}
static struct tm timeFromTS(const time_t ts)
{
#if !defined(_WIN32)
struct tm tm;
return *gmtime_r(&ts, &tm);
#else
return *gmtime(&ts);
#endif
}
FramesData::FramesData(const Glib::ustring &fname, time_t ts) :
ok_(false),
fname_(fname),
dcrawFrameCount(0),
time{timeFromTS(ts)},
timeStamp{ts},
iso_speed(0),
aperture(0.),
focal_len(0.),
focal_len35mm(0.),
focus_dist(0.f),
shutter(0.),
expcomp(0.),
make("Unknown"),
model("Unknown"),
orientation("Unknown"),
rating(0),
lens("Unknown"),
sampleFormat(IIOSF_UNKNOWN),
isPixelShift(false),
isHDR(false),
w_(-1),
h_(-1)
{
GStatBuf statbuf = {};
g_stat(fname.c_str(), &statbuf);
modTimeStamp = statbuf.st_mtime;
modTime = timeFromTS(modTimeStamp);
make.clear();
model.clear();
serial.clear();
orientation.clear();
lens.clear();
try {
Exiv2Metadata meta(fname);
meta.load();
const auto& exif = meta.exifData();
ok_ = true;
// taken and adapted from darktable (src/common/exif.cc)
/*
This file is part of darktable,
copyright (c) 2009--2013 johannes hanika.
copyright (c) 2011 henrik andersson.
copyright (c) 2012-2017 tobias ellinghaus.
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see <http://www.gnu.org/licenses/>.
*/
Exiv2::ExifData::const_iterator pos;
const auto find_exif_tag =
[&exif, &pos](const std::string &name) -> bool
{
try {
pos = exif.findKey(Exiv2::ExifKey(name));
return pos != exif.end() && pos->size();
} catch (std::exception &e) {
if (settings->verbose) {
std::cerr << "Exiv2 WARNING -- error finding tag " << name << ": " << e.what() << std::endl;
}
return false;
}
};
const auto find_tag =
[&exif, &pos](decltype(Exiv2::make) func) -> bool
{
pos = func(exif);
return pos != exif.end() && pos->size();
};
// List of tag names taken from exiv2's printSummary() in actions.cpp
if (find_tag(Exiv2::make)) {
make = validateUft8(pos->print(&exif));
}
if (find_tag(Exiv2::model)) {
model = validateUft8(pos->print(&exif));
}
if (make.size() > 0) {
for (const auto& corp : {
"Canon",
"NIKON",
"EPSON",
"KODAK",
"Kodak",
"OLYMPUS",
"PENTAX",
"RICOH",
"MINOLTA",
"Minolta",
"Konica",
"CASIO",
"Sinar",
"Phase One",
"SAMSUNG",
"Mamiya",
"MOTOROLA",
"Leaf",
"Panasonic"
}) {
if (make.find(corp) != std::string::npos) { // Simplify company names
make = corp;
break;
}
}
}
std::string::size_type nonspace_pos = make.find_last_not_of(' ');
if (nonspace_pos != std::string::npos && nonspace_pos + 1 < make.size()) {
make.erase(nonspace_pos + 1);
}
nonspace_pos = model.find_last_not_of(' ');
if (nonspace_pos != std::string::npos && nonspace_pos + 1 < model.size()) {
model.erase(nonspace_pos + 1);
}
if (!make.empty() && model.find(make + ' ') == 0) {
model.erase(0, make.size() + 1);
}
if (find_tag(Exiv2::exposureTime)) {
shutter = pos->toFloat();
}
if (find_tag(Exiv2::fNumber)) {
aperture = pos->toFloat();
}
// Read ISO speed - Nikon happens to return a pair for Lo and Hi modes
if (find_tag(Exiv2::isoSpeed)) {
// If standard exif iso tag, use the old way of interpreting the return value to be more regression-save
if (pos->key() == "Exif.Photo.ISOSpeedRatings") {
const long isofield = pos->count() > 1 ? 1 : 0;
iso_speed = pos->toFloat(isofield);
} else {
iso_speed = std::atof(pos->print().c_str());
}
}
// Some newer cameras support iso settings that exceed the 16 bit of exif's ISOSpeedRatings
if (iso_speed == 65535 || iso_speed == 0) {
if (find_exif_tag("Exif.PentaxDng.ISO") || find_exif_tag("Exif.Pentax.ISO")) {
iso_speed = std::atof(pos->print().c_str());
}
else if (
(
make == "SONY"
|| make == "Canon"
)
&& find_exif_tag("Exif.Photo.RecommendedExposureIndex")
) {
iso_speed = pos->toFloat();
}
}
if (find_tag(Exiv2::serialNumber)) {
serial = validateUft8(pos->toString());
} else {
const std::vector<std::string> serial_number_tags{
"Exif.Photo.BodySerialNumber",
"Exif.Canon.SerialNumber",
"Exif.Fujifilm.SerialNumber",
"Exif.Nikon3.SerialNumber",
"Exif.Nikon3.SerialNO",
"Exif.Olympus.SerialNumber2",
"Exif.OlympusEq.SerialNumber",
"Exif.Pentax.SerialNumber",
"Exif.PentaxDng.SerialNumber",
"Exif.Sigma.SerialNumber",
"Exif.Canon.InternalSerialNumber",
"Exif.OlympusEq.InternalSerialNumber",
"Exif.Panasonic.InternalSerialNumber",
};
if (serial_number_tags.cend() != std::find_if(serial_number_tags.cbegin(), serial_number_tags.cend(), find_exif_tag)) {
serial = validateUft8(pos->toString());
} else if (find_exif_tag("Exif.Minolta.WBInfoA100") || find_exif_tag("Exif.SonyMinolta.WBInfoA100")) {
const long index = 18908;
const int length = 12;
if (pos->count() >= index + length) {
for (int i = 0; i < length; ++i) {
serial += static_cast<char>(to_long(pos, index + i));
}
serial = validateUft8(serial);
}
} else if (find_exif_tag("Exif.Pentax.CameraInfo") || find_exif_tag("Exif.PentaxDng.CameraInfo")) {
const long index = 4;
if (pos->count() >= index) {
serial = validateUft8(pos->toString(index));
}
}
// TODO: Serial number from tags not supported by Exiv2.
}
if (find_tag(Exiv2::focalLength)) {
// This works around a bug in exiv2 the developers refuse to fix
// For details see http://dev.exiv2.org/issues/1083
if (pos->key() == "Exif.Canon.FocalLength" && pos->count() == 4) {
focal_len = pos->toFloat(1);
} else {
focal_len = pos->toFloat();
}
}
if (find_exif_tag("Exif.Photo.FocalLengthIn35mmFilm")) {
focal_len35mm = pos->toFloat();
}
// if (find_tag(Exiv2::subjectDistance)) {
// focus_dist = pos->toFloat();
// }
/*
* Get the focus distance in meters.
*/
if (Exiv2::testVersion(0, 27, 4) && find_exif_tag("Exif.NikonLd4.LensID") && to_long(pos) != 0) {
// Z lens, need to specifically look for the second instance of
// Exif.NikonLd4.FocusDistance unless using Exiv2 0.28.x and later
// (also expanded to 2 bytes of precision since 0.28.1).
#if EXIV2_TEST_VERSION(0, 28, 0)
if (find_exif_tag("Exif.NikonLd4.FocusDistance2")) {
float value = pos->toFloat();
if (Exiv2::testVersion(0, 28, 1)) {
value /= 256.f;
}
#else
pos = exif.end();
for (auto it = exif.begin(); it != exif.end(); it++) {
if (it->key() == "Exif.NikonLd4.FocusDistance") {
pos = it;
}
}
if (pos != exif.end() && pos->size()) {
float value = pos->toFloat();
#endif
focus_dist = 0.01 * std::pow(10, value / 40);
}
} else if (find_exif_tag("Exif.NikonLd2.FocusDistance")
|| find_exif_tag("Exif.NikonLd3.FocusDistance")
|| (Exiv2::testVersion(0, 27, 4)
&& find_exif_tag("Exif.NikonLd4.FocusDistance"))) {
float value = pos->toFloat();
focus_dist = (0.01 * std::pow(10, value / 40));
} else if (find_exif_tag("Exif.OlympusFi.FocusDistance")) {
/* the distance is stored as a rational (fraction). according to
* http://www.dpreview.com/forums/thread/1173960?page=4
* some Olympus cameras have a wrong denominator of 10 in there
* while the nominator is always in mm. thus we ignore the
* denominator and divide with 1000.
* "I've checked a number of E-1 and E-300 images, and I agree
* that the FocusDistance looks like it is in mm for the
* E-1. However, it looks more like cm for the E-300.
* For both cameras, this value is stored as a rational. With
* the E-1, the denominator is always 1, while for the E-300 it
* is 10.
* Therefore, it looks like the numerator in both cases is in mm
* (which makes a bit of sense, in an odd sort of way). So I
* think what I will do in ExifTool is to take the numerator and
* divide by 1000 to display the focus distance in meters." --
* Boardhead, dpreview forums in 2005
*/
int nominator = pos->toRational(0).first;
focus_dist = std::max(0.0, (0.001 * nominator));
} else if (find_exif_tag("Exif.CanonFi.FocusDistanceUpper")) {
const float FocusDistanceUpper = pos->toFloat();
if (FocusDistanceUpper <= 0.0f
|| (int)FocusDistanceUpper >= 0xffff) {
focus_dist = 0.0f;
} else {
focus_dist = FocusDistanceUpper / 100.0;
if (find_exif_tag("Exif.CanonFi.FocusDistanceLower")) {
const float FocusDistanceLower = pos->toFloat();
if (FocusDistanceLower > 0.0f && (int)FocusDistanceLower < 0xffff) {
focus_dist += FocusDistanceLower / 100.0;
focus_dist /= 2.0;
}
}
}
} else if (find_exif_tag("Exif.CanonSi.SubjectDistance")) {
focus_dist = pos->toFloat() / 100.0;
} else if (find_tag(Exiv2::subjectDistance)) {
focus_dist = pos->toFloat();
} else if (Exiv2::testVersion(0,27,2) && find_exif_tag("Exif.Sony2Fp.FocusPosition2")) {
const float focus_position = pos->toFloat();
if (focus_position && find_exif_tag("Exif.Photo.FocalLengthIn35mmFilm")) {
const float focal_length_35mm = pos->toFloat();
/* http://u88.n24.queensu.ca/exiftool/forum/index.php/topic,3688.msg29653.html#msg29653 */
focus_dist =
(std::pow(2, focus_position / 16 - 5) + 1) * focal_length_35mm / 1000;
}
}
if (find_tag(Exiv2::orientation)) {
static const std::vector<std::string> ormap = {
"Unknown",
"Horizontal (normal)",
"Mirror horizontal",
"Rotate 180",
"Mirror vertical",
"Mirror horizontal and rotate 270 CW",
"Rotate 90 CW",
"Mirror horizontal and rotate 90 CW",
"Rotate 270 CW",
"Unknown"
};
auto idx = to_long(pos);
if (idx >= 0 && idx < long(ormap.size())) {
orientation = ormap[idx];
}
//orientation = pos->print(&exif);
}
if (!make.compare(0, 5, "NIKON")) {
if (find_exif_tag("Exif.NikonLd4.LensID")) {
if (!to_long(pos)) { // No data, look in LensIDNumber.
const auto p = pos;
if (!find_exif_tag("Exif.NikonLd4.LensIDNumber")) {
pos = p; // Tag not found, so reset pos.
}
}
lens = pos->print(&exif);
if (lens == std::to_string(to_long(pos))) { // Not known to Exiv2.
lens.clear();
} else {
lens = validateUft8(lens);
}
}
} else if (!make.compare(0, 4, "SONY")) {
// ExifTool prefers LensType2 over LensType (called
// Exif.Sony2.LensID by Exiv2). Exiv2 doesn't support LensType2 yet,
// so we let Exiv2 try it's best. For non ILCE/NEX cameras which
// likely don't have LensType2, we use Exif.Sony2.LensID because
// Exif.Photo.LensModel may be incorrect (see
// https://discuss.pixls.us/t/call-for-testing-rawtherapee-metadata-handling-with-exiv2-includes-cr3-support/36240/36).
if (
// Camera model is neither a ILCE, ILME, nor NEX.
(!find_exif_tag("Exif.Image.Model") ||
(pos->toString().compare(0, 4, "ILCE") && pos->toString().compare(0, 4, "ILME") && pos->toString().compare(0, 3, "NEX"))) &&
// LensID exists. 0xFFFF could be one of many lenses.
find_exif_tag("Exif.Sony2.LensID") && to_long(pos) && to_long(pos) != 0xFFFF) {
lens = pos->print(&exif);
if (lens == std::to_string(to_long(pos))) { // Not known to Exiv2.
lens.clear();
} else {
lens = validateUft8(lens);
}
}
}
if (!lens.empty()) {
// Already found the lens name.
} else if (find_tag(Exiv2::lensName)) {
lens = validateUft8(pos->print(&exif));
auto p = pos;
if (find_exif_tag("Exif.CanonFi.RFLensType") && find_exif_tag("Exif.Canon.LensModel")) {
lens = validateUft8(pos->print(&exif));
} else if (p->count() == 1 && lens == std::to_string(to_long(p))) {
if (find_exif_tag("Exif.Canon.LensModel")) {
lens = validateUft8(pos->print(&exif));
} else if (find_exif_tag("Exif.Photo.LensModel")) {
lens = validateUft8(p->print(&exif));
}
}
} else if (find_exif_tag("Exif.Photo.LensSpecification") && pos->count() == 4) {
const auto round =
[](float f) -> float
{
return int(f * 10.f + 0.5f) / 10.f;
};
float fl_lo = round(pos->toFloat(0));
float fl_hi = round(pos->toFloat(1));
float fn_lo = round(pos->toFloat(2));
float fn_hi = round(pos->toFloat(3));
std::ostringstream buf;
buf << fl_lo;
if (fl_lo < fl_hi) {
buf << "-" << fl_hi;
}
buf << "mm F" << fn_lo;
if (fn_lo < fn_hi) {
buf << "-" << fn_hi;
}
lens = validateUft8(buf.str());
}
if (lens.empty() || lens.find_first_not_of('-') == std::string::npos) {
lens = "Unknown";
}
std::string datetime_taken;
if (find_exif_tag("Exif.Image.DateTimeOriginal")) {
datetime_taken = pos->print(&exif);
}
else if (find_exif_tag("Exif.Photo.DateTimeOriginal")) {
datetime_taken = pos->print(&exif);
}
else if (find_exif_tag("Exif.Photo.DateTimeDigitized")) {
datetime_taken = pos->print(&exif);
} else if (find_exif_tag("Exif.Image.DateTime")) {
datetime_taken = validateUft8(pos->print(&exif));
}
if (sscanf(datetime_taken.c_str(), "%d:%d:%d %d:%d:%d", &time.tm_year, &time.tm_mon, &time.tm_mday, &time.tm_hour, &time.tm_min, &time.tm_sec) == 6) {
time.tm_year -= 1900;
time.tm_mon -= 1;
time.tm_isdst = -1;
timeStamp = mktime(&time);
}
if (find_exif_tag("Exif.Image.ExposureBiasValue")) {
expcomp = pos->toFloat();
} else if (find_exif_tag("Exif.Photo.ExposureBiasValue")) {
expcomp = pos->toFloat();
}
if (find_exif_tag("Exif.Image.Rating")) {
rating = to_long(pos);
} else {
auto it = meta.xmpData().findKey(Exiv2::XmpKey("Xmp.xmp.Rating"));
if (it != meta.xmpData().end() && it->size()) {
rating = to_long(it);
}
}
// try getting some metadata from ImageDescription
if (!make.compare(0, 5, "KODAK") && !getISOSpeed() && !getFNumber() && !getFocalLen() && !getShutterSpeed() &&
find_exif_tag("Exif.Image.ImageDescription")) {
std::string s = pos->toString();
std::string line;
std::smatch m;
const auto d =
[&m]() -> double {
std::string s = m[1];
return atof(s.c_str());
};
while (true) {
auto p = s.find('\r');
if (p == std::string::npos) {
break;
}
auto line = s.substr(0, p);
s = s.substr(p+1);
if (std::regex_match(line, m, std::regex("ISO: +([0-9]+) *"))) {
iso_speed = d();
} else if (std::regex_match(line, m, std::regex("Aperture: +F([0-9.]+) *"))) {
aperture = d();
} else if (std::regex_match(line, m, std::regex("Shutter: +([0-9.]+) *"))) {
shutter = d();
if (shutter) {
shutter = 1.0/shutter;
}
} else if (std::regex_match(line, m, std::regex("Lens \\(mm\\): +([0-9.]+) *"))) {
focal_len = d();
} else if (std::regex_match(line, m, std::regex("Exp Comp: +([0-9.]+) *"))) {
expcomp = d();
}
}
}
meta.getDimensions(w_, h_);
// -----------------------
// Special file type detection (HDR, PixelShift)
// ------------------------
uint16 bitspersample = 0, samplesperpixel = 0, sampleformat = 0, photometric = 0, compression = 0;
const auto bps = exif.findKey(Exiv2::ExifKey("Exif.Image.BitsPerSample"));
const auto spp = exif.findKey(Exiv2::ExifKey("Exif.Image.SamplesPerPixel"));
const auto sf = exif.findKey(Exiv2::ExifKey("Exif.Image.SampleFormat"));
const auto pi = exif.findKey(Exiv2::ExifKey("Exif.Image.PhotometricInterpretation"));
const auto c = exif.findKey(Exiv2::ExifKey("Exif.Image.Compression"));
if (
!make.compare(0, 6, "PENTAX")
|| (
!make.compare(0, 5, "RICOH")
&& !model.compare (0, 6, "PENTAX")
)
) {
if (find_exif_tag("Exif.Pentax.DriveMode")) {
std::string buf = pos->toString(3);
if (buf == "HDR") {
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> DriveMode = \"HDR\"\n");
#endif
}
}
if (
!isHDR
&& (
find_exif_tag("Exif.Pentax.Quality")
|| find_exif_tag("Exif.PentaxDng.Quality")
)
&& (
to_long(pos) == 7
|| to_long(pos) == 8
)
) {
isPixelShift = true;
#if PRINT_HDR_PS_DETECTION
printf("PixelShift detected ! -> \"Quality\" = 7\n");
#endif
}
}
if (make == "SONY") {
if (find_exif_tag("Exif.SubImage1.BitsPerSample") && to_long(pos) == 14) {
if (find_exif_tag("Exif.SubImage1.SamplesPerPixel") && to_long(pos) == 4 &&
find_exif_tag("Exif.SubImage1.PhotometricInterpretation") && to_long(pos) == 32892 &&
find_exif_tag("Exif.SubImage1.Compression") && to_long(pos) == 1) {
isPixelShift = true;
}
} else if (bps != exif.end() && to_long(bps) == 14 &&
spp != exif.end() && to_long(spp) == 4 &&
c != exif.end() && to_long(c) == 1 &&
find_exif_tag("Exif.Image.Software") &&
pos->toString() == "make_arq") {
isPixelShift = true;
}
} else if (make == "FUJIFILM") {
if (bps != exif.end() && to_long(bps) == 16 &&
spp != exif.end() && to_long(spp) == 4 &&
c != exif.end() && to_long(c) == 1 &&
find_exif_tag("Exif.Image.Software") &&
pos->toString() == "make_arq") {
isPixelShift = true;
}
}
sampleFormat = IIOSF_UNKNOWN;
if (sf == exif.end())
/*
* WARNING: This is a dirty hack!
* We assume that files which doesn't contain the TIFFTAG_SAMPLEFORMAT tag
* (which is the case with uncompressed TIFFs produced by RT!) are RGB files,
* but that may be not true. --- Hombre
*/
{
sampleformat = SAMPLEFORMAT_UINT;
} else {
sampleformat = to_long(sf);
}
if (bps == exif.end() || spp == exif.end() || pi == exif.end()) {
return;
}
bitspersample = to_long(bps);
samplesperpixel = to_long(spp);
photometric = to_long(pi);
if (photometric == PHOTOMETRIC_LOGLUV) {
if (c == exif.end()) {
compression = COMPRESSION_NONE;
} else {
compression = to_long(c);
}
}
if (photometric == PHOTOMETRIC_RGB || photometric == PHOTOMETRIC_MINISBLACK) {
if (sampleformat == SAMPLEFORMAT_INT || sampleformat == SAMPLEFORMAT_UINT) {
if (bitspersample == 8) {
sampleFormat = IIOSF_UNSIGNED_CHAR;
} else if (bitspersample <= 16) {
sampleFormat = IIOSF_UNSIGNED_SHORT;
}
} else if (sampleformat == SAMPLEFORMAT_IEEEFP) {
if (bitspersample==16) {
sampleFormat = IIOSF_FLOAT16;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d (16-bit)\n", sampleFormat);
#endif
}
else if (bitspersample == 24) {
sampleFormat = IIOSF_FLOAT24;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d (24-bit)\n", sampleFormat);
#endif
}
else if (bitspersample == 32) {
sampleFormat = IIOSF_FLOAT32;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d (32-bit)\n", sampleFormat);
#endif
}
}
} else if (photometric == PHOTOMETRIC_CFA) {
if (sampleformat == SAMPLEFORMAT_IEEEFP) {
if (bitspersample == 16) {
sampleFormat = IIOSF_FLOAT16;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d (16-bit)\n", sampleFormat);
#endif
}
else if (bitspersample == 24) {
sampleFormat = IIOSF_FLOAT24;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d (24-bit)\n", sampleFormat);
#endif
}
else if (bitspersample == 32) {
sampleFormat = IIOSF_FLOAT32;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d (32-bit)\n", sampleFormat);
#endif
}
} else if (sampleformat == SAMPLEFORMAT_INT || sampleformat == SAMPLEFORMAT_UINT) {
if (bitspersample == 8) { // shouldn't occur...
sampleFormat = IIOSF_UNSIGNED_CHAR;
} else if (bitspersample <= 16) {
sampleFormat = IIOSF_UNSIGNED_SHORT;
}
}
} else if (photometric == 34892 || photometric == 32892 /* Linear RAW (see DNG spec ; 32892 seem to be a flaw from Sony's ARQ files) */) {
if (sampleformat == SAMPLEFORMAT_IEEEFP) {
sampleFormat = IIOSF_FLOAT32;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d\n", sampleFormat);
#endif
} else if (sampleformat == SAMPLEFORMAT_INT || sampleformat == SAMPLEFORMAT_UINT) {
if (bitspersample == 8) { // shouldn't occur...
sampleFormat = IIOSF_UNSIGNED_CHAR;
} else if (bitspersample <= 16) {
sampleFormat = IIOSF_UNSIGNED_SHORT;
if (find_exif_tag("Exif.Photo.MakerNote") && (!make.compare (0, 4, "SONY")) && bitspersample >= 12 && samplesperpixel == 4) {
isPixelShift = true;
#if PRINT_HDR_PS_DETECTION
printf("PixelShift detected ! -> \"Make\" = SONY, bitsPerPixel > 8, samplesPerPixel == 4\n");
#endif
}
}
}
} else if (photometric == PHOTOMETRIC_LOGLUV) {
if (compression == COMPRESSION_SGILOG24) {
sampleFormat = IIOSF_LOGLUV24;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d\n", sampleFormat);
#endif
} else if (compression == COMPRESSION_SGILOG) {
sampleFormat = IIOSF_LOGLUV32;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d\n", sampleFormat);
#endif
}
}
} catch (const std::exception& e) {
if (settings->verbose) {
std::cerr << "EXIV2 ERROR: " << e.what() << std::endl;
}
ok_ = false;
}
}
bool FramesData::getPixelShift() const
{
return isPixelShift;
}
bool FramesData::getHDR() const
{
return isHDR;
}
std::string FramesData::getImageType() const
{
return isPixelShift ? "PS" : isHDR ? "HDR" : "STD";
}
IIOSampleFormat FramesData::getSampleFormat() const
{
return sampleFormat;
}
bool FramesData::hasExif() const
{
return ok_;
}
tm FramesData::getDateTime() const
{
return time;
}
time_t FramesData::getDateTimeAsTS() const
{
return timeStamp;
}
int FramesData::getISOSpeed() const
{
return iso_speed;
}
double FramesData::getFNumber() const
{
return aperture;
}
double FramesData::getFocalLen() const
{
return focal_len;
}
double FramesData::getFocalLen35mm() const
{
return focal_len35mm;
}
float FramesData::getFocusDist() const
{
return focus_dist;
}
double FramesData::getShutterSpeed() const
{
return shutter;
}
double FramesData::getExpComp() const
{
return expcomp;
}
std::string FramesData::getMake() const
{
return make;
}
std::string FramesData::getModel() const
{
return model;
}
std::string FramesData::getLens() const
{
return lens;
}
std::string FramesData::getSerialNumber() const
{
return serial;
}
std::string FramesData::getOrientation() const
{
return orientation;
}
void FramesData::setDCRawFrameCount(unsigned int frameCount)
{
dcrawFrameCount = frameCount;
}
unsigned int FramesData::getFrameCount() const
{
return std::max(1U, dcrawFrameCount);
}
Glib::ustring FramesData::getFileName() const
{
return fname_;
}
int FramesData::getRating() const
{
return rating;
}
//------inherited functions--------------//
std::string FramesMetaData::apertureToString(double aperture)
{
// TODO: Replace sprintf()
char buffer[256];
snprintf(buffer, sizeof(buffer), "%0.1f", aperture);
return buffer;
}
std::string FramesMetaData::shutterToString(double shutter)
{
char buffer[256];
if (shutter > 0.0 && shutter <= 0.5) {
snprintf(buffer, sizeof(buffer), "1/%0.0f", 1.0 / shutter);
} else if (int(shutter) == shutter) {
snprintf(buffer, sizeof(buffer), "%d", int(shutter));
} else {
snprintf(buffer, sizeof(buffer), "%0.1f", shutter);
}
return buffer;
}
std::string FramesMetaData::expcompToString(double expcomp, bool maskZeroexpcomp)
{
char buffer[256];
if (maskZeroexpcomp) {
if (expcomp != 0.0) {
snprintf(buffer, sizeof(buffer), "%0.2f", expcomp);
return buffer;
} else {
return "";
}
} else {
snprintf(buffer, sizeof(buffer), "%0.2f", expcomp);
return buffer;
}
}
double FramesMetaData::shutterFromString(std::string s)
{
const std::string::size_type i = s.find_first_of('/');
if (i == std::string::npos) {
return std::atof(s.c_str());
} else {
const double denominator = std::atof(s.substr(i + 1).c_str());
return
denominator
? std::atof(s.substr(0, i).c_str()) / denominator
: 0.0;
}
}
double FramesMetaData::apertureFromString(std::string s)
{
return std::atof(s.c_str());
}
namespace {
template<class T>
void set_exif(Exiv2::ExifData &exif, const std::string &key, T val)
{
try {
exif[key] = val;
} catch (std::exception &exc) {
if (settings->verbose) {
std::cout << "Exif -- error setting " << key << " to " << val << ": " << exc.what() << std::endl;
}
}
}
} // namespace
void FramesData::fillBasicTags(Exiv2::ExifData &exif) const
{
if (!hasExif()) {
return;
}
set_exif(exif, "Exif.Photo.ISOSpeedRatings", getISOSpeed());
set_exif(exif, "Exif.Photo.FNumber", Exiv2::URationalValue(Exiv2::URational(round(getFNumber() * 10), 10)));
auto s = shutterToString(getShutterSpeed());
auto p = s.find('.');
if (p != std::string::npos) {
assert(p == s.length()-2);
s = s.substr(0, p) + s.substr(p+1) + "/10";
} else if (s.find('/') == std::string::npos) {
s += "/1";
}
set_exif(exif, "Exif.Photo.ExposureTime", s);
set_exif(exif, "Exif.Photo.FocalLength", Exiv2::URationalValue(Exiv2::URational(getFocalLen() * 10, 10)));
set_exif(exif, "Exif.Photo.ExposureBiasValue", Exiv2::RationalValue(Exiv2::Rational(round(getExpComp() * 100), 100)));
set_exif(exif, "Exif.Image.Make", getMake());
set_exif(exif, "Exif.Image.Model", getModel());
set_exif(exif, "Exif.Photo.LensModel", getLens());
char buf[256];
auto t = getDateTime();
strftime(buf, 256, "%Y:%m:%d %H:%M:%S", &t);
set_exif(exif, "Exif.Photo.DateTimeOriginal", buf);
}
void FramesData::getDimensions(int &w, int &h) const
{
w = w_;
h = h_;
}
void FramesData::setDimensions(int w, int h)
{
w_ = w;
h_ = h;
}