Add Fujifilm metadata lens correction
This commit is contained in:
@@ -279,10 +279,215 @@ private:
|
|||||||
bool hasCACorrection() const override { return true; }
|
bool hasCACorrection() const override { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FujiMetadataLensCorrection : public CenterRadiusMetadataLensCorrection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FujiMetadataLensCorrection(const FramesMetaData *meta) :
|
||||||
|
CenterRadiusMetadataLensCorrection(meta)
|
||||||
|
{
|
||||||
|
parse();
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const static int MAXKNOTS = 16;
|
||||||
|
|
||||||
|
int nc;
|
||||||
|
double cropf;
|
||||||
|
|
||||||
|
std::array<float, MAXKNOTS> fuji_knots;
|
||||||
|
std::array<float, MAXKNOTS> fuji_distortion;
|
||||||
|
std::array<float, MAXKNOTS> fuji_ca_r;
|
||||||
|
std::array<float, MAXKNOTS> fuji_ca_b;
|
||||||
|
std::array<float, MAXKNOTS> fuji_vignetting;
|
||||||
|
|
||||||
|
std::vector<double> knots_dist;
|
||||||
|
std::vector<double> dist;
|
||||||
|
std::array<std::vector<double>, 3> ca;
|
||||||
|
std::vector<double> knots_vig;
|
||||||
|
std::vector<double> vig;
|
||||||
|
|
||||||
|
void parse()
|
||||||
|
{
|
||||||
|
if (Exiv2::versionNumber() < EXIV2_MAKE_VERSION(0, 27, 4)) {
|
||||||
|
throw std::runtime_error("cannot get Fuji correction data, too old exiv2 version " + Exiv2::versionString());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &exif = metadata.exifData();
|
||||||
|
|
||||||
|
/* FujiFilm metadata corrections parameters define some splines with N knots */
|
||||||
|
auto posd = exif.findKey(Exiv2::ExifKey("Exif.Fujifilm.GeometricDistortionParams"));
|
||||||
|
auto posc = exif.findKey(Exiv2::ExifKey("Exif.Fujifilm.ChromaticAberrationParams"));
|
||||||
|
auto posv = exif.findKey(Exiv2::ExifKey("Exif.Fujifilm.VignettingParams"));
|
||||||
|
|
||||||
|
// X-Trans IV/V
|
||||||
|
if (posd != exif.end() && posc != exif.end() && posv != exif.end() &&
|
||||||
|
posd->count() == 19 && posc->count() == 29 && posv->count() == 19) {
|
||||||
|
const int nc = 9;
|
||||||
|
this->nc = nc;
|
||||||
|
|
||||||
|
for (int i = 0; i < nc; i++) {
|
||||||
|
const float kd = posd->toFloat(i + 1);
|
||||||
|
const float kc = posc->toFloat(i + 1);
|
||||||
|
const float kv = posv->toFloat(i + 1);
|
||||||
|
|
||||||
|
// Check that the knots position is the same for distortion, ca and vignetting,
|
||||||
|
if (kd != kc || kd != kv) {
|
||||||
|
throw std::runtime_error("cannot get Fuji correction data: unexpected data");
|
||||||
|
}
|
||||||
|
|
||||||
|
fuji_knots[i] = kd;
|
||||||
|
fuji_distortion[i] = posd->toFloat(i + 10);
|
||||||
|
fuji_ca_r[i] = posc->toFloat(i + 10);
|
||||||
|
fuji_ca_b[i] = posc->toFloat(i + 19);
|
||||||
|
fuji_vignetting[i] = posv->toFloat(i + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account for the 1.25x crop modes in some Fuji cameras
|
||||||
|
auto it = exif.findKey(Exiv2::ExifKey("Exif.Fujifilm.CropMode"));
|
||||||
|
|
||||||
|
if (it != exif.end() && (to_long(it) == 2 || to_long(it) == 4)) {
|
||||||
|
cropf = 1.25f;
|
||||||
|
} else {
|
||||||
|
cropf = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// X-Trans I/II/III
|
||||||
|
else if (posd != exif.end() && posc != exif.end() && posv != exif.end() &&
|
||||||
|
posd->count() == 23 && posc->count() == 31 && posv->count() == 23) {
|
||||||
|
const int nc = 11;
|
||||||
|
this->nc = nc;
|
||||||
|
|
||||||
|
for (int i = 0; i < nc; i++) {
|
||||||
|
const float kd = posd->toFloat(i + 1);
|
||||||
|
float kc = 0;
|
||||||
|
// ca data doesn't provide first knot (0)
|
||||||
|
if (i != 0) kc = posc->toFloat(i);
|
||||||
|
const float kv = posv->toFloat(i + 1);
|
||||||
|
// check that the knots position is the same for distortion, ca and vignetting,
|
||||||
|
if (kd != kc || kd != kv) {
|
||||||
|
throw std::runtime_error("cannot get Fuji correction data: unexpected data");
|
||||||
|
}
|
||||||
|
|
||||||
|
fuji_knots[i] = kd;
|
||||||
|
fuji_distortion[i] = posd->toFloat(i + 12);
|
||||||
|
|
||||||
|
// ca data doesn't provide first knot (0)
|
||||||
|
if (i == 0) {
|
||||||
|
fuji_ca_r[i] = 0;
|
||||||
|
fuji_ca_b[i] = 0;
|
||||||
|
} else {
|
||||||
|
fuji_ca_r[i] = posc->toFloat(i + 10);
|
||||||
|
fuji_ca_b[i] = posc->toFloat(i + 20);
|
||||||
|
}
|
||||||
|
fuji_vignetting[i] = posv->toFloat(i + 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account for the 1.25x crop modes in some Fuji cameras
|
||||||
|
auto it = exif.findKey(Exiv2::ExifKey("Exif.Fujifilm.CropMode"));
|
||||||
|
|
||||||
|
if (it != exif.end() && (to_long(it) == 2 || to_long(it) == 4)) {
|
||||||
|
cropf = 1.25f;
|
||||||
|
} else {
|
||||||
|
cropf = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("cannot get Fuji correction data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
std::vector<double> knots_in;
|
||||||
|
std::vector<double> distortion_in;
|
||||||
|
std::vector<double> ca_r_in;
|
||||||
|
std::vector<double> ca_b_in;
|
||||||
|
|
||||||
|
// add a knot with no corrections at 0 value if not existing
|
||||||
|
int size = nc;
|
||||||
|
if (fuji_knots[0] > 0.f) {
|
||||||
|
knots_in.push_back(0);
|
||||||
|
distortion_in.push_back(1);
|
||||||
|
ca_r_in.push_back(0);
|
||||||
|
ca_b_in.push_back(0);
|
||||||
|
|
||||||
|
knots_vig.push_back(0);
|
||||||
|
vig.push_back(1);
|
||||||
|
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
knots_in.reserve(size);
|
||||||
|
vig.reserve(size);
|
||||||
|
|
||||||
|
for (int i = 0; i < nc; i++) {
|
||||||
|
knots_in.push_back(cropf * fuji_knots[i]);
|
||||||
|
distortion_in.push_back(fuji_distortion[i] / 100 + 1);
|
||||||
|
ca_r_in.push_back(fuji_ca_r[i]);
|
||||||
|
ca_b_in.push_back(fuji_ca_b[i]);
|
||||||
|
|
||||||
|
// vignetting correction is applied before distortion correction. So the
|
||||||
|
// spline is related to the source image before distortion.
|
||||||
|
knots_vig.push_back(cropf * fuji_knots[i]);
|
||||||
|
|
||||||
|
vig.push_back(100 / fuji_vignetting[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
knots_dist.resize(MAXKNOTS);
|
||||||
|
dist.resize(MAXKNOTS);
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
ca[i].resize(MAXKNOTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert from spline related to source image (input is source image
|
||||||
|
// radius) to spline related to dest image (input is dest image radius)
|
||||||
|
for (int i = 0; i < MAXKNOTS; i++) {
|
||||||
|
const double rin = static_cast<double>(i) / static_cast<double>(nc - 1);
|
||||||
|
const double m = interpolateLinearSpline(knots_in, distortion_in, rin);
|
||||||
|
const double r = rin / m;
|
||||||
|
knots_dist[i] = r;
|
||||||
|
|
||||||
|
dist[i] = m;
|
||||||
|
|
||||||
|
const double mcar = interpolateLinearSpline(knots_in, ca_r_in, rin);
|
||||||
|
const double mcab = interpolateLinearSpline(knots_in, ca_b_in, rin);
|
||||||
|
|
||||||
|
ca[0][i] = ca[1][i] = ca[2][i] = 1.f;
|
||||||
|
ca[0][i] *= mcar + 1;
|
||||||
|
ca[2][i] *= mcab + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double distortionCorrectionFactor(double rout) const override
|
||||||
|
{
|
||||||
|
return interpolateLinearSpline(knots_dist, dist, rout);
|
||||||
|
}
|
||||||
|
|
||||||
|
double caCorrectionFactor(double rout, int channel) const override
|
||||||
|
{
|
||||||
|
return interpolateLinearSpline(knots_dist, ca[channel], rout);
|
||||||
|
}
|
||||||
|
|
||||||
|
double distortionAndCACorrectionFactor(double rout, int channel) const override
|
||||||
|
{
|
||||||
|
return distortionCorrectionFactor(rout) * caCorrectionFactor(rout, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
double vignettingCorrectionFactor(double r) const override
|
||||||
|
{
|
||||||
|
return interpolateLinearSpline(knots_vig, vig, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasDistortionCorrection() const override { return true; }
|
||||||
|
bool hasVignettingCorrection() const override { return true; }
|
||||||
|
bool hasCACorrection() const override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
std::unique_ptr<MetadataLensCorrection> MetadataLensCorrectionFinder::findCorrection(const FramesMetaData *meta)
|
std::unique_ptr<MetadataLensCorrection> MetadataLensCorrectionFinder::findCorrection(const FramesMetaData *meta)
|
||||||
{
|
{
|
||||||
static const std::unordered_set<std::string> makers = {
|
static const std::unordered_set<std::string> makers = {
|
||||||
"SONY",
|
"SONY",
|
||||||
|
"FUJIFILM",
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string make = Glib::ustring(meta->getMake()).uppercase();
|
std::string make = Glib::ustring(meta->getMake()).uppercase();
|
||||||
@@ -296,6 +501,8 @@ std::unique_ptr<MetadataLensCorrection> MetadataLensCorrectionFinder::findCorrec
|
|||||||
try {
|
try {
|
||||||
if (make == "SONY") {
|
if (make == "SONY") {
|
||||||
correction.reset(new SonyMetadataLensCorrection(meta));
|
correction.reset(new SonyMetadataLensCorrection(meta));
|
||||||
|
} else if (make == "FUJIFILM") {
|
||||||
|
correction.reset(new FujiMetadataLensCorrection(meta));
|
||||||
}
|
}
|
||||||
} catch (std::exception &exc) {
|
} catch (std::exception &exc) {
|
||||||
if (settings->verbose) {
|
if (settings->verbose) {
|
||||||
|
Reference in New Issue
Block a user