1 /* -*- C++ -*-
2  *
3  *  This file is part of ART.
4  *
5  *  Copyright (c) 2020 Alberto Griggio <alberto.griggio@gmail.com>
6  *
7  *  ART is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  ART is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with ART.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 // adapted from mlens.c in PR #7092 of darktable
22 // original author: Freddie Witherden (https://freddie.witherden.org/)
23 // copyright of original code follows
24 /*
25     This file is part of darktable,
26     Copyright (C) 2010-2020 darktable developers.
27 
28     darktable is free software: you can redistribute it and/or modify
29     it under the terms of the GNU General Public License as published by
30     the Free Software Foundation, either version 3 of the License, or
31     (at your option) any later version.
32 
33     darktable is distributed in the hope that it will be useful,
34     but WITHOUT ANY WARRANTY; without even the implied warranty of
35     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36     GNU General Public License for more details.
37 
38     You should have received a copy of the GNU General Public License
39     along with darktable.  If not, see <http://www.gnu.org/licenses/>.
40 */
41 
42 #include "lensexif.h"
43 #include "metadata.h"
44 #include "settings.h"
45 #include <array>
46 #include <iostream>
47 
48 namespace rtengine {
49 
50 extern const Settings *settings;
51 
52 
53 namespace {
54 
55 class SonyCorrectionData: public ExifLensCorrection::CorrectionData {
56 public:
57     int nc;
58     std::array<short, 16> distortion;
59     std::array<short, 16> ca_r;
60     std::array<short, 16> ca_b;
61     std::array<short, 16> vignetting;
62 
get_coeffs(std::vector<float> & knots,std::vector<float> & dist,std::vector<float> & vig,std::array<std::vector<float>,3> & ca,bool & is_dng) const63     void get_coeffs(std::vector<float> &knots, std::vector<float> &dist, std::vector<float> &vig, std::array<std::vector<float>, 3> &ca, bool &is_dng) const override
64     {
65         is_dng = false;
66         const float scale = 1.f;
67 
68         knots.resize(nc);
69         for (int i = 0; i < 3; ++i) {
70             ca[i].resize(nc);
71         }
72         dist.resize(nc);
73         vig.resize(nc);
74 
75         constexpr float vig_scaling = 0.7f; // empirically determined on my lenses
76 
77         for (int i = 0; i < nc; i++) {
78             knots[i] = float(i) / (nc - 1);
79 
80             dist[i] = (distortion[i] * std::pow(2.f, -14.f) + 1) * scale;
81 
82             ca[0][i] = ca[1][i] = ca[2][i] = 1.f;
83             ca[0][i] *= ca_r[i] * std::pow(2.f, -21.f) + 1;
84             ca[2][i] *= ca_b[i] * std::pow(2.f, -21.f) + 1;
85 
86             vig[i] = std::pow(2.f, 0.5f - std::pow(2.f, vig_scaling * vignetting[i] * std::pow(2.f, -13.f) -1));
87         }
88     }
89 };
90 
91 
92 class FujiCorrectionData: public ExifLensCorrection::CorrectionData {
93 public:
94     float cropf;
95     std::array<float, 9> knots;
96     std::array<float, 9> distortion;
97     std::array<float, 9> ca_r;
98     std::array<float, 9> ca_b;
99     std::array<float, 9> vignetting;
100 
get_coeffs(std::vector<float> & knots,std::vector<float> & dist,std::vector<float> & vig,std::array<std::vector<float>,3> & ca,bool & is_dng) const101     void get_coeffs(std::vector<float> &knots, std::vector<float> &dist, std::vector<float> &vig, std::array<std::vector<float>, 3> &ca, bool &is_dng) const override
102     {
103         is_dng = false;
104         knots.resize(9);
105         dist.resize(9);
106         vig.resize(9);
107         for (int i = 0; i < 3; ++i) {
108             ca[i].resize(9);
109         }
110 
111         for (int i = 0; i < 9; i++) {
112             knots[i] = cropf * this->knots[i];
113 
114             dist[i] = distortion[i] / 100 + 1;
115 
116             ca[0][i] = ca[1][i] = ca[2][i] = 1.f;
117 
118             ca[0][i] *= ca_r[i] + 1;
119             ca[2][i] *= ca_b[i] + 1;
120 
121             vig[i] = 1 - (1 - vignetting[i] / 100);
122         }
123     }
124 };
125 
126 
127 
128 class DNGCorrectionData: public ExifLensCorrection::CorrectionData {
129 public:
130     float cx_d;
131     float cy_d;
132     float cx_v;
133     float cy_v;
134     std::vector<float> warp_rectilinear;
135     std::vector<float> vignette_radial;
136 
DNGCorrectionData()137     DNGCorrectionData():
138         cx_d(0), cy_d(0),
139         cx_v(0), cy_v(0),
140         warp_rectilinear(),
141         vignette_radial()
142     {}
143 
get_coeffs(std::vector<float> & knots,std::vector<float> & dist,std::vector<float> & vig,std::array<std::vector<float>,3> & ca,bool & is_dng) const144     void get_coeffs(std::vector<float> &knots, std::vector<float> &dist, std::vector<float> &vig, std::array<std::vector<float>, 3> &ca, bool &is_dng) const override
145     {
146         is_dng = true;
147         knots = { cx_d, cy_d, cx_v, cy_v };
148         dist = warp_rectilinear;
149         vig = vignette_radial;
150     }
151 
parse(const std::vector<Exiv2::byte> & buf)152     static DNGCorrectionData *parse(const std::vector<Exiv2::byte> &buf)
153     {
154         DNGCorrectionData ret;
155         const Exiv2::byte *data = &buf[0];
156         uint32_t num_entries = Exiv2::getULong(data, Exiv2::bigEndian);
157         size_t idx = 4;
158         bool ok = false;
159 
160         for (size_t i = 0; i < num_entries && idx < buf.size(); ++i) {
161             uint32_t opid = Exiv2::getULong(data+idx, Exiv2::bigEndian);
162             idx += 4;
163             idx += 4; // version
164             idx += 4; // flags
165             size_t size = Exiv2::getULong(data+idx, Exiv2::bigEndian);
166             idx += 4;
167             if (opid == 1) { // WarpRectilinear
168                 uint32_t n = Exiv2::getULong(data + idx, Exiv2::bigEndian);
169                 size_t wstart = idx + 4;
170                 size_t cstart = wstart + 6 * 8;
171                 if (n == 3) {
172                     wstart += 6 * 8;
173                     cstart += 6 * 8 * 2;
174                 } else if (n != 1) {
175                     cstart = buf.size() + 1;
176                 }
177                 if (cstart + 8 * 2 <= buf.size()) {
178                     ret.warp_rectilinear.resize(6);
179                     for (int j = 0; j < 6; ++j) {
180                         ret.warp_rectilinear[j] = Exiv2::getDouble(data + wstart, Exiv2::bigEndian);
181                         wstart += 8;
182                     }
183                     ret.cx_d = Exiv2::getDouble(data + cstart, Exiv2::bigEndian);
184                     cstart += 8;
185                     ret.cy_d = Exiv2::getDouble(data + cstart, Exiv2::bigEndian);
186                     ok = true;
187                 }
188                 if (settings->verbose) {
189                     std::cout << "WARP RECTILINEAR: ";
190                     for (auto w : ret.warp_rectilinear) {
191                         std::cout << w << " ";
192                     }
193                     std::cout << "| " << ret.cx_d << " " << ret.cy_d << std::endl;
194                 }
195             } else if (opid == 3) { // FixVignetteRadial
196                 size_t start = idx;
197                 size_t end = idx + 7 * 8;
198                 if (end <= buf.size()) {
199                     ret.vignette_radial.resize(6);
200                     for (int j = 0; j < 5; ++j) {
201                         ret.vignette_radial[j] = Exiv2::getDouble(data + start, Exiv2::bigEndian);
202                         start += 8;
203                     }
204                     ret.cx_v = Exiv2::getDouble(data + start, Exiv2::bigEndian);
205                     start += 8;
206                     ret.cy_v = Exiv2::getDouble(data + start, Exiv2::bigEndian);
207                     ok = true;
208                 }
209             }
210             idx += size;
211         }
212 
213         if (!ok) {
214             return nullptr;
215         } else {
216             return new DNGCorrectionData(ret);
217         }
218     }
219 };
220 
221 
interpolate(const std::vector<float> & xi,const std::vector<float> & yi,float x)222 float interpolate(const std::vector<float> &xi, const std::vector<float> &yi, float x)
223 {
224     if (x < xi[0]) {
225         return yi[0];
226     }
227 
228     for (size_t i = 1; i < xi.size(); i++) {
229         if (x >= xi[i - 1] && x <= xi[i]) {
230             float dydx = (yi[i] - yi[i - 1]) / (xi[i] - xi[i - 1]);
231 
232             return yi[i - 1] + (x - xi[i - 1]) * dydx;
233         }
234     }
235 
236     return yi[yi.size() - 1];
237 }
238 
239 } // namespace
240 
241 
ExifLensCorrection(const FramesMetaData * meta,int width,int height,const CoarseTransformParams & coarse,int rawRotationDeg)242 ExifLensCorrection::ExifLensCorrection(const FramesMetaData *meta, int width, int height, const CoarseTransformParams &coarse, int rawRotationDeg):
243     data_(),
244     is_dng_(false),
245     swap_xy_(false)
246 {
247     if (rawRotationDeg >= 0) {
248         int rot = (coarse.rotate + rawRotationDeg) % 360;
249         swap_xy_ = (rot == 90 || rot == 270);
250         if (swap_xy_) {
251             std::swap(width, height);
252         }
253     }
254 
255     w2_ = width * 0.5f;
256     h2_ = height * 0.5f;
257     r_ = 1 / std::sqrt(SQR(w2_) + SQR(h2_));
258 
259     auto make = meta->getMake();
260     if (make != "SONY" && make != "FUJIFILM" && !meta->isDNG()) {
261         return;
262     }
263 
264     try {
265         auto md = Exiv2Metadata(meta->getFileName());
266         if (meta->isDNG()) {
267             // check if this is a DNG
268             md.load();
269             auto &exif = md.exifData();
270             auto it = exif.findKey(Exiv2::ExifKey("Exif.SubImage1.OpcodeList3"));
271             if (it != exif.end()) {
272                 std::vector<Exiv2::byte> buf;
273                 buf.resize(it->value().size());
274                 it->value().copy(&buf[0], Exiv2::invalidByteOrder);
275                 data_.reset(DNGCorrectionData::parse(buf));
276             }
277         } else {
278             auto mn = md.getMakernotes();
279 
280             const auto gettag =
281                 [&](const char *name) -> std::string
282                 {
283                     auto it = mn.find(name);
284                     if (it != mn.end()) {
285                         return it->second;
286                     }
287                     return "";
288                 };
289 
290             const auto getvec =
291                 [&](const char *tag) -> std::vector<float>
292                 {
293                     std::istringstream src(gettag(tag));
294                     std::vector<float> ret;
295                     float val;
296                     while (src >> val) {
297                         ret.push_back(val);
298                     }
299                     return ret;
300                 };
301 
302 
303             if (make == "SONY") {
304                 auto posd = getvec("DistortionCorrParams");
305                 auto posc = getvec("ChromaticAberrationCorrParams");
306                 auto posv = getvec("VignettingCorrParams");
307 
308                 if (!posd.empty() && !posc.empty() && !posv.empty() &&
309                     posd[0] <= 16 && posc[0] == 2 * posd[0] && posv[0] == posd[0]) {
310                     SonyCorrectionData *sony = new SonyCorrectionData();
311                     data_.reset(sony);
312                     sony->nc = posd[0];
313                     for (int i = 0; i < sony->nc; ++i) {
314                         sony->distortion[i] = posd[i+1];
315                         sony->ca_r[i] = posc[i+1];
316                         sony->ca_b[i] = posc[sony->nc + i+1];
317                         sony->vignetting[i] = posv[i+1];
318                     }
319                 }
320             } else if (make == "FUJIFILM") {
321                 auto posd = getvec("GeometricDistortionParams");
322                 auto posc = getvec("ChromaticAberrationParams");
323                 auto posv = getvec("VignettingParams");
324 
325                 if (posd.size() == 19 && posc.size() == 29 && posv.size() == 19) {
326                     FujiCorrectionData *fuji = new FujiCorrectionData();
327                     data_.reset(fuji);
328 
329                     for(int i = 0; i < 9; i++) {
330                         float kd = posd[i+1], kc = posc[i + 1], kv = posv[i + 1];
331                         if (kd != kc || kd != kv) {
332                             data_.reset(nullptr);
333                             break;
334                         }
335 
336                         fuji->knots[i] = kd;
337                         fuji->distortion[i] = posd[i + 10];
338                         fuji->ca_r[i] = posc[i + 10];
339                         fuji->ca_b[i] = posc[i + 19];
340                         fuji->vignetting[i] = posv[i + 10];
341                     }
342 
343                     if (data_) {
344                         // Account for the 1.25x crop modes in some Fuji cameras
345                         std::string val = gettag("CropMode");
346                         if (val == "2" || val == "4") {
347                             fuji->cropf = 1.25f;
348                         } else {
349                             fuji->cropf = 1;
350                         }
351                     }
352                 }
353             }
354         }
355     } catch (std::exception &exc) {
356         data_.reset(nullptr);
357     }
358 
359     if (data_) {
360         data_->get_coeffs(knots_, dist_, vig_, ca_, is_dng_);
361 
362         if (is_dng_) {
363             float cx_d = knots_[0] * width;
364             float cy_d = knots_[1] * height;
365             float cx_v = knots_[2] * width;
366             float cy_v = knots_[3] * height;
367             float mx_d = std::max(cx_d, width - cx_d);
368             float my_d = std::max(cy_d, height - cy_d);
369             float m_d = std::sqrt(SQR(mx_d) + SQR(my_d));
370             float mx_v = std::max(cx_v, width - cx_v);
371             float my_v = std::max(cy_v, height - cy_v);
372             float m_v = std::sqrt(SQR(mx_v) + SQR(my_v));
373             knots_[0] = cx_d;
374             knots_[1] = cy_d;
375             knots_[2] = cx_v;
376             knots_[3] = cy_v;
377             knots_.push_back(m_d);
378             knots_.push_back(m_v);
379         }
380     }
381 }
382 
383 
ok() const384 bool ExifLensCorrection::ok() const
385 {
386     return data_.get();
387 }
388 
389 
ok(const FramesMetaData * meta)390 bool ExifLensCorrection::ok(const FramesMetaData *meta)
391 {
392     ExifLensCorrection corr(meta, -1, -1, CoarseTransformParams(), -1);
393     return corr.ok();
394 }
395 
396 
correctDistortion(double & x,double & y,int cx,int cy,double scale) const397 void ExifLensCorrection::correctDistortion(double &x, double &y, int cx, int cy, double scale) const
398 {
399     if (!data_) {
400         return;
401     }
402 
403     if (!is_dng_) {
404         float xx = x + cx;
405         float yy = y + cy;
406         if (swap_xy_) {
407             std::swap(xx, yy);
408         }
409 
410         float ccx = xx - w2_;
411         float ccy = yy - h2_;
412         float dr = interpolate(knots_, dist_, r_ * std::sqrt(SQR(ccx) + SQR(ccy)));
413 
414         x = dr * ccx + w2_;
415         y = dr * ccy + h2_;
416         if (swap_xy_) {
417             std::swap(x, y);
418         }
419         x -= cx;
420         y -= cy;
421 
422         x *= scale;
423         y *= scale;
424     } else if (dist_.size() == 6) {
425         float xx = x + cx;
426         float yy = y + cy;
427         if (swap_xy_) {
428             std::swap(xx, yy);
429         }
430 
431         const float cx1 = knots_[0];
432         const float cy1 = knots_[1];
433         const float m = knots_[4];
434 
435         const float dx = (xx - cx1) / m;
436         const float dy = (yy - cy1) / m;
437         const float dx2 = SQR(dx);
438         const float dy2 = SQR(dy);
439         const float r2 = dx2 + dy2;
440         const float f = dist_[0] + r2 * (dist_[1] + r2 * (dist_[2] + r2 * dist_[3]));
441         const float dx_r = f * dx;
442         const float dy_r = f * dy;
443         const float dxdy2 = 2 * dx * dy;
444         const float dx_t = dist_[4] * dxdy2 + dist_[5] * (r2 + 2 * dx2);
445         const float dy_t = dist_[5] * dxdy2 + dist_[4] * (r2 + 2 * dx2);
446 
447         x = cx1 + m * (dx_r + dx_t);
448         y = cy1 + m * (dy_r + dy_t);
449 
450         if (swap_xy_) {
451             std::swap(x, y);
452         }
453         x -= cx;
454         y -= cy;
455 
456         x *= scale;
457         y *= scale;
458     }
459 }
460 
461 
isCACorrectionAvailable() const462 bool ExifLensCorrection::isCACorrectionAvailable() const
463 {
464     return data_.get() && !is_dng_;
465 }
466 
467 
correctCA(double & x,double & y,int cx,int cy,int channel) const468 void ExifLensCorrection::correctCA(double &x, double &y, int cx, int cy, int channel) const
469 {
470     if (data_ && !is_dng_) {
471         float xx = x + cx;
472         float yy = y + cy;
473         if (swap_xy_) {
474             std::swap(xx, yy);
475         }
476 
477         float ccx = xx - w2_;
478         float ccy = yy - h2_;
479         float dr = interpolate(knots_, ca_[channel], r_ * std::sqrt(SQR(ccx) + SQR(ccy)));
480 
481         x = dr * ccx + w2_;
482         y = dr * ccy + h2_;
483         if (swap_xy_) {
484             std::swap(x, y);
485         }
486         x -= cx;
487         y -= cy;
488     }
489 }
490 
491 
processVignette(int width,int height,float ** rawData) const492 void ExifLensCorrection::processVignette(int width, int height, float** rawData) const
493 {
494     if (!data_) {
495         return;
496     }
497 
498     if (!is_dng_) {
499         for (int y = 0; y < height; ++y) {
500             for (int x = 0; x < width; ++x) {
501                 float cx = x - w2_;
502                 float cy = y - h2_;
503                 float sf = interpolate(knots_, vig_, r_ * std::sqrt(SQR(cx) + SQR(cy)));
504                 rawData[y][x] /= SQR(sf);
505             }
506         }
507     } else if (vig_.size() == 5) {
508         const float cx = knots_[2];
509         const float cy = knots_[3];
510         const float m2 = 1.f / SQR(knots_[5]);
511 
512         for (int y = 0; y < height; ++y) {
513             for (int x = 0; x < width; ++x) {
514                 const float r2 = m2 * (SQR(x - cx) + SQR(y - cy));
515                 const float g = 1.f + r2 * (vig_[0] + r2 * (vig_[1] + r2 * (vig_[2] + r2 * (vig_[3] + r2 * vig_[4]))));
516                 rawData[y][x] *= g;
517             }
518         }
519     }
520 }
521 
522 } // namespace rtengine
523