Skip to content

Commit

Permalink
Merge pull request #12714 from sgotti/lens_embedded_metadata
Browse files Browse the repository at this point in the history
iop/lens: add embedded lens metadata correction
  • Loading branch information
TurboGit committed Nov 1, 2022
2 parents e4cfb25 + 8e8fac9 commit 1e819b6
Show file tree
Hide file tree
Showing 5 changed files with 1,307 additions and 398 deletions.
119 changes: 97 additions & 22 deletions src/common/exif.cc
Original file line number Diff line number Diff line change
Expand Up @@ -692,10 +692,25 @@ static bool _exif_decode_iptc_data(dt_image_t *img, Exiv2::IptcData &iptcData)
}
}

static bool _exif_read_exif_tag(Exiv2::ExifData &exifData, Exiv2::ExifData::const_iterator *pos, string key)
{
try
{
return (*pos = exifData.findKey(Exiv2::ExifKey(key))) != exifData.end() && (*pos)->size();
}
catch(Exiv2::AnyError &e)
{
std::string s(e.what());
std::cerr << "[exiv2 read_exif_tag] " << s << std::endl;
return false;
}
}
#define FIND_EXIF_TAG(key) _exif_read_exif_tag(exifData, &pos, key)

// Support DefaultUserCrop, what is the safe exif tag?
// Magic-nr taken from dng specs, the specs also say it has 4 floats (top,left,bottom,right
// We only take them if a) we find a value != the default *and* b) data are plausible
static bool dt_check_usercrop(Exiv2::ExifData &exifData, dt_image_t *img)
static bool _check_usercrop(Exiv2::ExifData &exifData, dt_image_t *img)
{
Exiv2::ExifData::const_iterator pos = exifData.findKey(Exiv2::ExifKey("Exif.SubImage1.0xc7b5"));
if(pos != exifData.end() && pos->count() == 4 && pos->size())
Expand All @@ -711,7 +726,7 @@ static bool dt_check_usercrop(Exiv2::ExifData &exifData, dt_image_t *img)
return FALSE;
}

static gboolean dt_check_dng_opcodes(Exiv2::ExifData &exifData, dt_image_t *img)
static gboolean _check_dng_opcodes(Exiv2::ExifData &exifData, dt_image_t *img)
{
gboolean has_opcodes = FALSE;
Exiv2::ExifData::const_iterator pos = exifData.findKey(Exiv2::ExifKey("Exif.SubImage1.OpcodeList2"));
Expand All @@ -733,6 +748,75 @@ static gboolean dt_check_dng_opcodes(Exiv2::ExifData &exifData, dt_image_t *img)
return has_opcodes;
}

static gboolean _check_lens_correction_data(Exiv2::ExifData &exifData, dt_image_t *img)
{
Exiv2::ExifData::const_iterator pos, posd, posc, posv;

/*
* Sony lens correction data
*/
if(Exiv2::versionNumber() >= EXIV2_MAKE_VERSION(0, 27, 4)
&& _exif_read_exif_tag(exifData, &posd, "Exif.SubImage1.DistortionCorrParams")
&& _exif_read_exif_tag(exifData, &posc, "Exif.SubImage1.ChromaticAberrationCorrParams")
&& _exif_read_exif_tag(exifData, &posv, "Exif.SubImage1.VignettingCorrParams"))
{
// Validate
const int nc = posd->toLong(0);
if(nc <= 16 && 2*nc == posc->toLong(0) && nc == posv->toLong(0))
{
img->exif_correction_type = CORRECTION_TYPE_SONY;
img->exif_correction_data.sony.nc = nc;
for(int i = 0; i < nc; i++)
{
img->exif_correction_data.sony.distortion[i] = posd->toLong(i + 1);
img->exif_correction_data.sony.ca_r[i] = posc->toLong(i + 1);
img->exif_correction_data.sony.ca_b[i] = posc->toLong(nc + i + 1);
img->exif_correction_data.sony.vignetting[i] = posv->toLong(i + 1);
}
}
}

/*
* Fuji lens correction data
*/
if(Exiv2::versionNumber() >= EXIV2_MAKE_VERSION(0, 27, 4)
&& _exif_read_exif_tag(exifData, &posd, "Exif.Fujifilm.GeometricDistortionParams")
&& _exif_read_exif_tag(exifData, &posc, "Exif.Fujifilm.ChromaticAberrationParams")
&& _exif_read_exif_tag(exifData, &posv, "Exif.Fujifilm.VignettingParams"))
{
// Validate
if(posd->count() == 19 && posc->count() == 29 && posv->count() == 19)
{
const int nc = 9;
img->exif_correction_type = CORRECTION_TYPE_FUJI;
img->exif_correction_data.fuji.nc = nc;
for(int i = 0; i < nc; i++)
{
float kd = posd->toFloat(i + 1), kc = posc->toFloat(i + 1), kv = posv->toFloat(i + 1);
if (kd != kc || kd != kv)
{
img->exif_correction_type = CORRECTION_TYPE_NONE;
break;
}

img->exif_correction_data.fuji.knots[i] = kd;
img->exif_correction_data.fuji.distortion[i] = posd->toFloat(i + 10);
img->exif_correction_data.fuji.ca_r[i] = posc->toFloat(i + 10);
img->exif_correction_data.fuji.ca_b[i] = posc->toFloat(i + 19);
img->exif_correction_data.fuji.vignetting[i] = posv->toFloat(i + 10);
}

// Account for the 1.25x crop modes in some Fuji cameras
if(FIND_EXIF_TAG("Exif.Fujifilm.CropMode") && (pos->toLong() == 2 || pos->toLong() == 4))
img->exif_correction_data.fuji.cropf = 1.25f;
else
img->exif_correction_data.fuji.cropf = 1;
}
}

return img->exif_correction_type != CORRECTION_TYPE_NONE;
}

void dt_exif_img_check_additional_tags(dt_image_t *img, const char *filename)
{
try
Expand All @@ -743,8 +827,9 @@ void dt_exif_img_check_additional_tags(dt_image_t *img, const char *filename)
Exiv2::ExifData &exifData = image->exifData();
if(!exifData.empty())
{
dt_check_usercrop(exifData, img);
dt_check_dng_opcodes(exifData, img);
_check_usercrop(exifData, img);
_check_dng_opcodes(exifData, img);
_check_lens_correction_data(exifData, img);
}
return;
}
Expand All @@ -756,21 +841,6 @@ void dt_exif_img_check_additional_tags(dt_image_t *img, const char *filename)
}
}

static bool dt_exif_read_exif_tag(Exiv2::ExifData &exifData, Exiv2::ExifData::const_iterator *pos, string key)
{
try
{
return (*pos = exifData.findKey(Exiv2::ExifKey(key))) != exifData.end() && (*pos)->size();
}
catch(Exiv2::AnyError &e)
{
std::string s(e.what());
std::cerr << "[exiv2 read_exif_tag] " << s << std::endl;
return false;
}
}
#define FIND_EXIF_TAG(key) dt_exif_read_exif_tag(exifData, &pos, key)

static void _find_datetime_taken(Exiv2::ExifData &exifData, Exiv2::ExifData::const_iterator pos,
char *exif_datetime_taken)
{
Expand Down Expand Up @@ -940,7 +1010,7 @@ static bool _exif_decode_exif_data(dt_image_t *img, Exiv2::ExifData &exifData)
img->exif_crop = 1.0f;
}

if(dt_check_usercrop(exifData, img))
if(_check_usercrop(exifData, img))
{
img->flags |= DT_IMAGE_HAS_ADDITIONAL_DNG_TAGS;
guint tagid = 0;
Expand All @@ -950,7 +1020,12 @@ static bool _exif_decode_exif_data(dt_image_t *img, Exiv2::ExifData &exifData)
dt_tag_attach(tagid, img->id, FALSE, FALSE);
}

if(dt_check_dng_opcodes(exifData, img))
if(_check_dng_opcodes(exifData, img))
{
img->flags |= DT_IMAGE_HAS_ADDITIONAL_DNG_TAGS;
}

if(_check_lens_correction_data(exifData, img))
{
img->flags |= DT_IMAGE_HAS_ADDITIONAL_DNG_TAGS;
}
Expand Down Expand Up @@ -4188,7 +4263,7 @@ int dt_exif_xmp_attach_export(const int imgid, const char *filename, void *metad
{
// remove this specific exif
Exiv2::ExifData::const_iterator pos;
if(dt_exif_read_exif_tag(exifOldData, &pos, tagname))
if(_exif_read_exif_tag(exifOldData, &pos, tagname))
{
exifData[tagname] = pos->value();
}
Expand Down
1 change: 1 addition & 0 deletions src/common/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -1829,6 +1829,7 @@ void dt_image_init(dt_image_t *img)
img->usercrop[0] = img->usercrop[1] = 0;
img->usercrop[2] = img->usercrop[3] = 1;
img->dng_gain_maps = NULL;
img->exif_correction_type = CORRECTION_TYPE_NONE;
img->cache_entry = 0;

for(int k=0; k<4; k++)
Expand Down
23 changes: 23 additions & 0 deletions src/common/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,26 @@ typedef enum dt_image_orientation_t
ORIENTATION_TRANSVERSE = ORIENTATION_FLIP_Y | ORIENTATION_FLIP_X | ORIENTATION_SWAP_XY // 7
} dt_image_orientation_t;

typedef enum dt_image_correction_type_t
{
CORRECTION_TYPE_NONE,
CORRECTION_TYPE_SONY,
CORRECTION_TYPE_FUJI
} dt_image_correction_type_t;

typedef union dt_image_correction_data_t
{
struct {
int nc;
short distortion[16], ca_r[16], ca_b[16], vignetting[16];
} sony;
struct {
int nc;
float cropf;
float knots[9], distortion[9], ca_r[9], ca_b[9], vignetting[9];
} fuji;
} dt_image_correction_data_t;

typedef enum dt_image_loader_t
{
LOADER_UNKNOWN = 0,
Expand Down Expand Up @@ -211,6 +231,9 @@ typedef struct dt_image_t
char exif_lens[128];
GTimeSpan exif_datetime_taken;

dt_image_correction_type_t exif_correction_type;
dt_image_correction_data_t exif_correction_data;

char camera_maker[64];
char camera_model[64];
char camera_alias[64];
Expand Down
4 changes: 1 addition & 3 deletions src/iop/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,7 @@ if(Rsvg2_FOUND)
add_iop(watermark "watermark.c")
endif(Rsvg2_FOUND)

if(LensFun_FOUND)
add_iop(lens "lens.cc" DEFAULT_VISIBLE)
endif(LensFun_FOUND)
add_iop(lens "lens.cc" DEFAULT_VISIBLE)

# fix for Mac when OpenMP is only available in C compiler
if(APPLE)
Expand Down

0 comments on commit 1e819b6

Please sign in to comment.