Enhancements: parsing for Leaf exif data & Phase One orientation;

Fix for Time in reading exif:DateTimeOriginal - REQUIRES CLEARING CACHE to take an effect on images prviously viewed in RT;
 - on behalf of Torger (issue 766)
This commit is contained in:
Michael Ezra
2012-05-12 18:27:16 -04:00
parent 33424e7e1d
commit 927d796611
4 changed files with 264 additions and 3 deletions

View File

@@ -119,7 +119,7 @@ void ImageData::extractInfo () {
static const char *corp[] =
{ "Canon", "NIKON", "EPSON", "KODAK", "Kodak", "OLYMPUS", "PENTAX",
"MINOLTA", "Minolta", "Konica", "CASIO", "Sinar", "Phase One",
"SAMSUNG", "Mamiya", "MOTOROLA" };
"SAMSUNG", "Mamiya", "MOTOROLA", "Leaf" };
for (size_t i=0; i < (sizeof(corp)/sizeof(*corp)); i++)
if ( make.find( corp[i] ) != std::string::npos ){ /* Simplify company names */
make = corp[i];
@@ -175,6 +175,7 @@ void ImageData::extractInfo () {
if (sscanf ((const char*)exif->getTag("DateTimeOriginal")->getValue(), "%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);
}
}

View File

@@ -644,7 +644,7 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality) {
jpeg_start_compress(&cinfo, TRUE);
// buffer for exif and iptc markers
unsigned char* buffer = new unsigned char[165535]; //TODO: Is it really 165535... or 65535 ?
unsigned char* buffer = new unsigned char[165535]; //FIXME: no buffer size check so it can be overflowed in createJPEGMarker() for large tags, and then software will crash
unsigned int size;
// assemble and write exif marker
if (exifRoot) {

View File

@@ -1308,6 +1308,249 @@ void ExifManager::parseCIFF (FILE* f, int base, int length, TagDirectory* root)
}
}
static void
parse_leafdata(TagDirectory* root, ByteOrder order) {
Tag *leafdata = root->getTag("LeafData");
if (!leafdata) {
return;
}
unsigned char *value = leafdata->getValue();
int valuesize = leafdata->getValueSize();
// parse LeafData tag, a tag specific to Leaf digital backs, and has a custom
// format with 52 byte tag headers starting with "PKTS".
char *hdr;
int pos = 0;
// There are lots of sub-tags in here, but for now we only care about those directly
// useful to RT, which is ISO and rotation. Shutter speed and aperture is not
// available here.
int iso_speed = 0;
int rotation_angle = 0;
int found_count = 0;
while (pos + sizeof(hdr) <= valuesize && found_count < 2) {
hdr = (char *)&value[pos];
if (strncmp(hdr, "PKTS", 4) != 0) {
// in a few cases the header can be offset a few bytes, don't know why
// it does not seem to be some sort of alignment, it appears random,
// this check takes care of it, restart if we find an offset match.
int offset = 1;
for (; offset <= 3; offset++) {
if (strncmp(&hdr[offset], "PKTS", 4) == 0) {
pos += offset;
break;
}
}
if (offset <= 3) {
continue;
}
break;
}
int size = sget4((unsigned char *)&hdr[48], order);
if (pos + size > valuesize) {
break;
}
pos += 52;
char *val = (char *)&value[pos];
if (strncmp(&hdr[8], "CameraObj_ISO_speed", 19) == 0) {
iso_speed = 25 * (1 << (atoi(val) - 1));
found_count++;
} else if (strncmp(&hdr[8], "ImgProf_rotation_angle", 22) == 0) {
rotation_angle = atoi(val);
found_count++;
} else {
// check if this is a sub-directory, include test for that strange offset of next header
if (size >= 8 &&
(strncmp(val, "PKTS", 4) == 0 ||
strncmp(&val[1], "PKTS", 4) == 0 ||
strncmp(&val[2], "PKTS", 4) == 0 ||
strncmp(&val[3], "PKTS", 4) == 0))
{
// start of next hdr, this is a sub-directory, we skip those for now.
size = 0;
}
}
pos += size;
}
// create standard tags from the custom Leaf tags
Tag* exif = root->getTag ("Exif");
if (!exif) {
exif = new Tag (root, root->getAttrib ("Exif"));
exif->initSubDir();
root->addTagFront (exif);
}
if (!exif->getDirectory()->getTag("ISOSpeedRatings")) {
Tag *t = new Tag (exif->getDirectory(), exif->getDirectory()->getAttrib ("ISOSpeedRatings"));
t->initInt (iso_speed, LONG);
exif->getDirectory()->addTagFront (t);
}
if (!root->getTag("Orientation")) {
int orientation;
switch (rotation_angle) {
case 0: orientation = 1; break;
case 90: orientation = 6; break;
case 180: orientation = 3; break;
case 270: orientation = 8; break;
default: orientation = 1; break;
}
Tag *t = new Tag (root, root->getAttrib ("Orientation"));
t->initInt (orientation, SHORT);
root->addTagFront (t);
}
// now look in ApplicationNotes tag for additional information
Tag *appnotes = root->getTag("ApplicationNotes");
if (!appnotes) {
return;
}
char *xmp = (char *)appnotes->getValue();
char *end, *p;
// Quick-and-dirty value extractor, no real xml parsing.
// We could make it more generic, but we just get most important
// values we know use to be in there.
if ((p = strstr(xmp, "xmlns:tiff")) != NULL &&
(end = strstr(p, "</rdf:Description>")) != NULL)
{
*end = '\0';
while ((p = strstr(p, "<tiff:")) != NULL) {
char *tag = &p[6], *tagend;
if ((tagend = strchr(tag, '>')) == NULL) {
break;
}
*tagend = '\0';
char *val = &tagend[1];
if ((p = strstr(val, "</tiff:")) == NULL) {
*tagend = '>';
break;
}
*p = '\0';
if (root->getAttrib (tag) && !root->getTag (tag)) {
Tag *t = new Tag (root, root->getAttrib (tag));
if (strcmp(tag, "Make") == 0 ||
strcmp(tag, "Model") == 0)
{
t->initString (val);
root->addTagFront (t);
} else {
delete t;
}
}
*p = '<';
*tagend = '>';
}
*end = '<';
}
if ((p = strstr(xmp, "xmlns:exif")) != NULL &&
(end = strstr(p, "</rdf:Description>")) != NULL)
{
*end = '\0';
while ((p = strstr(p, "<exif:")) != NULL) {
char *tag = &p[6], *tagend;
if ((tagend = strchr(tag, '>')) == NULL) {
break;
}
*tagend = '\0';
char *val = &tagend[1];
if ((p = strstr(val, "</exif:")) == NULL) {
*tagend = '>';
break;
}
*p = '\0';
if (exif->getDirectory()->getAttrib (tag) && !exif->getDirectory()->getTag (tag)) {
Tag *t = new Tag (exif->getDirectory(), exif->getDirectory()->getAttrib (tag));
int num, denom;
struct tm tm;
if (strcmp(tag, "ApertureValue") == 0 && sscanf(val, "%d/%d", &num, &denom) == 2) {
t->initRational (num, denom);
exif->getDirectory()->addTagFront (t);
// we also make an "FNumber" tag since many tools don't interpret ApertureValue
// according to Exif standard
t = new Tag (exif->getDirectory(), lookupAttrib(exifAttribs,"FNumber"));
double f = pow(sqrt(2.0), ((double)num / denom));
if (f > 10.0) {
t->initRational ((int)floor(f), 1);
} else {
t->initRational ((int)floor(f * 10.0), 10);
}
exif->getDirectory()->addTagFront (t);
} else if (strcmp(tag, "ShutterSpeedValue") == 0 && sscanf(val, "%d/%d", &num, &denom) == 2) {
t->initRational (num, denom);
exif->getDirectory()->addTagFront (t);
// we also make an "ExposureTime" tag since many tools don't interpret ShutterSpeedValue
// according to Exif standard
t = new Tag (exif->getDirectory(), lookupAttrib(exifAttribs,"ExposureTime"));
double f = 1.0 / pow(2.0, ((double)num / denom));
if (f > 10.0) {
t->initRational ((int)floor(f), 1);
} else if (f > 1.0) {
t->initRational ((int)floor(f * 10.0), 10);
} else if (f == 1.0) {
t->initRational (1, 1);
} else {
f = 1.0 / f;
static const double etimes[] = {
10000, 8000, 6400, 6000, 5000,
4000, 3200, 3000, 2500,
2000, 1600, 1500, 1250,
1000, 800, 750, 640,
500, 400, 350, 320,
250, 200, 180, 160,
125, 100, 90, 80,
60, 50, 45, 40,
30, 25, 22, 20,
15, 13, 11, 10,
8, 6, 5,
4, 3, 2.5,
2, 1.6, 1.5, 1.3,
1, -1 };
double diff = etimes[0];
int idx = -1;
for (int i = 1; etimes[i] > 0; i++) {
if (abs(etimes[i] - f) < diff) {
idx = i;
diff = abs(etimes[i] - f);
}
}
if (idx != -1 && f < etimes[0]) {
f = etimes[idx];
}
if (f < 2) {
t->initRational (10, (int)(10 * f));
} else {
t->initRational (1, (int)f);
}
}
exif->getDirectory()->addTagFront (t);
} else if (strcmp(tag, "FocalLength") == 0 && sscanf(val, "%d/%d", &num, &denom) == 2) {
t->initRational (num, denom);
exif->getDirectory()->addTagFront (t);
} else if (strcmp(tag, "ISOSpeedRatings") == 0) {
t->initInt (atoi(val), LONG);
exif->getDirectory()->addTagFront (t);
} else if (strcmp(tag, "DateTimeOriginal") == 0 &&
sscanf(val, "%d-%d-%dT%d:%d:%dZ",
&tm.tm_year, &tm.tm_mon,
&tm.tm_mday, &tm.tm_hour,
&tm.tm_min, &tm.tm_sec) == 6)
{
char tstr[64];
sprintf(tstr, "%02d:%02d:%02d %02d:%02d:%02d", tm.tm_year, tm.tm_mon,
tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
t->initString (tstr);
exif->getDirectory()->addTagFront (t);
} else {
delete t;
}
}
*p = '<';
*tagend = '>';
}
*end = '<';
}
}
TagDirectory* ExifManager::parse (FILE* f, int base, bool skipIgnored) {
setlocale(LC_NUMERIC, "C"); // to set decimal point in sscanf
// read tiff header
@@ -1325,9 +1568,9 @@ TagDirectory* ExifManager::parse (FILE* f, int base, bool skipIgnored) {
TagDirectory* root = new TagDirectory (NULL, f, base, ifdAttribs, order, skipIgnored);
// fix ISO issue with nikon and panasonic cameras
Tag* make = root->getTag ("Make");
Tag* exif = root->getTag ("Exif");
if (exif && !exif->getDirectory()->getTag("ISOSpeedRatings")) {
Tag* make = root->getTag ("Make");
if (make && !strncmp((char*)make->getValue(), "NIKON", 5)) {
Tag* mn = exif->getDirectory()->getTag("MakerNote");
if (mn) {
@@ -1351,6 +1594,22 @@ TagDirectory* ExifManager::parse (FILE* f, int base, bool skipIgnored) {
}
}
parse_leafdata(root, order);
if (!root->getTag("Orientation")) {
if (make && !strncmp((char*)make->getValue(), "Phase One", 9)) {
int orientation = 0;
Tag *iw = root->getTag("ImageWidth");
if (iw) {
// from dcraw, derive orientation from image width
orientation = "0653"[iw->toInt()&3]-'0';
}
Tag *t = new Tag (root, root->getAttrib ("Orientation"));
t->initInt (orientation, SHORT);
root->addTagFront (t);
}
}
// root->printAll ();
return root;

View File

@@ -540,6 +540,7 @@ const TagAttrib iopAttribs[] = {
{0, 1, 0, 0, 0x828d, "CFAPatternDim", &stdInterpreter},
{0, 1, 0, 0, 0x828e, "CFAPattern", &cfaInterpreter},
{0, 1, 1, 0, 0x8298, "Copyright", &stdInterpreter},
{0, 0, 0, 0, 0x8606, "LeafData", &stdInterpreter}, // is actually a subdir, but a proprietary format
{0, 1, 0, exifAttribs, 0x8769, "Exif", &stdInterpreter},
{0, 2, 0, 0, 0x8773, "ICCProfile", &stdInterpreter},
{0, 2, 0, 0, 0x83BB, "IPTCData", &stdInterpreter},