From 8c52d6e3d7e937b0818c97d21d7a8552062da014 Mon Sep 17 00:00:00 2001 From: torger Date: Sat, 10 Jan 2015 14:16:01 +0100 Subject: [PATCH] Added Hasselblad model identification in rtexif --- rtexif/rtexif.cc | 109 +++++++++++++++++++++++++++++++++++++++++++ rtexif/stdattribs.cc | 1 + 2 files changed, 110 insertions(+) diff --git a/rtexif/rtexif.cc b/rtexif/rtexif.cc index 6c90c3188..a3251d61c 100644 --- a/rtexif/rtexif.cc +++ b/rtexif/rtexif.cc @@ -2021,6 +2021,115 @@ TagDirectory* ExifManager::parse (FILE* f, int base, bool skipIgnored) { parse_leafdata(root, order); + if (make && !strncmp((char*)make->getValue(), "Hasselblad", 10)) { + /* + Figuring out the Hasselblad model is a mess. Hasselblad raw data comes in four slightly + different containers, 3FR (directly from CF card), FFF (same as 3FR but filtered through + Phocus, calibration data applied and a bit different tags), Adobe-generated DNGs and + Phocus-generated DNGs. + + FFF usually has a sane model name in Model (and is used as reference for what we shall + call the different Hasselblad models), but 3FR only says like "Hasselblad H3D" for + all H3D models, or "Flash Sync" if the back has been used on a mechanical camera body. + V-mount backs may have the model name of the V body instead of the back model. Etc... + as said it's a mess. + + This code is supposed to handle all raw containers and end up with the same model + regardless of container. + + We don't differ between single shot and multi-shot models, and probably there's no use + of doing so. You need Hasselblad's own software to shoot multi-shot and can only do that + tethered. In single-shot mode they should be exactly the same as the single-shot models. + */ + Tag *subd = root->getTag(0x14a); + Tag *iw = (subd) ? subd->getDirectory()->getTag("ImageWidth") : 0; + int sensorWidth = (iw) ? iw->toInt() : 0; + Tag* tmodel = root->getTag ("Model"); + const char *model = (tmodel) ? (const char *)tmodel->getValue() : ""; + if (strstr(model, "Hasselblad ") == model) { + model += 11; + } else { + // if HxD is used in flash sync mode for example, we need to fetch model from this tag + Tag* tmodel3 = root->getTag("UniqueCameraModel"); + const char *model3 = (tmodel3) ? (const char *)tmodel3->getValue() : ""; + if (strstr(model3, "Hasselblad ") == model3) { + model = model3 + 11; + } + } + // FIXME: due to lack of test files this Hasselblad model identification is not 100% complete + // This needs checking out: CFV-39/CFV-50 3FR, H3DII vs H3D, old CF/CFH models + + if (!strcmp(model, "H3D")) { + // We can't differ between H3D and H3DII for the 22, 31 and 39 models. There's was no H3D-50 so we know that is a + // H3DII-50. At the time of writing I have no test files for the H3D vs H3DII models, so there still may be a chance + // to differ between them. AFAIK Adobe's DNG converter don't differ between them, and actually call the H3DII-50 + // H3D-50 although Hasselblad never released such a model. + switch (sensorWidth) { + case 4096: tmodel->initString("H3D-22"); break; + case 6542: tmodel->initString("H3D-31"); break; + case 7262: tmodel->initString("H3D-39"); break; + case 8282: tmodel->initString("H3DII-50"); break; + } + } else if (!strcmp(model, "H4D")) { + switch (sensorWidth) { + case 6542: tmodel->initString("H4D-31"); break; + case 7410: tmodel->initString("H4D-40"); break; + case 8282: tmodel->initString("H4D-50"); break; + case 9044: tmodel->initString("H4D-60"); break; + } + } else if (!strcmp(model, "H5D")) { + switch (sensorWidth) { + case 7410: tmodel->initString("H5D-40"); break; + case 8282: tmodel->initString("H5D-50"); break; + case 8374: tmodel->initString("H5D-50c"); break; + case 9044: tmodel->initString("H5D-60"); break; + } + } else if (!strcmp(model, "CFV")) { + switch (sensorWidth) { + case 7262: tmodel->initString("CFV-39"); break; + case 8282: tmodel->initString("CFV-50"); break; + case 8374: tmodel->initString("CFV-50c"); break; + } + } + + // and a few special cases + Tag* tmodel3 = root->getTag("UniqueCameraModel"); + const char *model3 = (tmodel3) ? (const char *)tmodel3->getValue() : ""; + if (strstr(model3, "Hasselblad ") == model3) { + model3 = model3 + 11; + } + if (!strcmp(model3, "ixpressCF132")) { + tmodel->initString("CF-22"); + } else if (!strcmp(model3, "Hasselblad96")) { + tmodel->initString("CFV"); // popularly called CFV-16, but the official name is CFV + } else if (!strcmp(model3, "Hasselblad234")) { + tmodel->initString("CFV-39"); + } else if (sensorWidth == 4090) { + tmodel->initString("V96C"); + } + + // and yet some, this is for Adobe-generated DNG files + Tag* tmodel4 = root->getTag("LocalizedCameraModel"); + if (tmodel4) { + const char *model4 = (tmodel4) ? (const char *)tmodel4->getValue() : ""; + if (strstr(model4, "Hasselblad ") == model4) { + model4 = model4 + 11; + } + if (!strcmp(model4, "ixpressCF132-22")) { + tmodel->initString("CF-22"); + } else if (!strcmp(model4, "Hasselblad96-16")) { + tmodel->initString("CFV"); + } else if (!strcmp(model4, "Hasselblad234-39")) { + tmodel->initString("CFV-39"); + } else if (!strcmp(model4, "H3D-50")) { + // Adobe names H3DII-50 incorrectly as H3D-50 + tmodel->initString("H3DII-50"); + } else if (strstr(model4, "H3D-") == model4 || strstr(model4, "H4D-") == model4 || strstr(model4, "H5D-") == model4) { + tmodel->initString(model4); + } + } + } + if (!root->getTag("Orientation")) { if (make && !strncmp((char*)make->getValue(), "Phase One", 9)) { int orientation = 0; diff --git a/rtexif/stdattribs.cc b/rtexif/stdattribs.cc index a66a64cd9..0990d3a4c 100644 --- a/rtexif/stdattribs.cc +++ b/rtexif/stdattribs.cc @@ -705,6 +705,7 @@ const TagAttrib iopAttribs[] = { {0, AC_DONTWRITE, 0, 0, 0xC612, AUTO, "DNGVersion", &stdInterpreter}, {0, AC_DONTWRITE, 0, 0, 0xC613, AUTO, "DNGBackwardVersion", &stdInterpreter}, {0, AC_WRITE, 0, 0, 0xC614, AUTO, "UniqueCameraModel", &stdInterpreter}, + {0, AC_WRITE, 0, 0, 0xC615, AUTO, "LocalizedCameraModel", &stdInterpreter}, {0, AC_WRITE, 0, 0, 0xc62f, AUTO, "CameraSerialNumber", &stdInterpreter}, {0, AC_SYSTEM, 0, 0, 0xc630, AUTO, "DNGLensInfo", &stdInterpreter}, {0, AC_DONTWRITE, 0, 0, 0xC634, AUTO, "MakerNote", &stdInterpreter}, //DNGPrivateData