1 /*****************************************************************************/
2 // Copyright 2008 Adobe Systems Incorporated
3 // All Rights Reserved.
4 //
5 // NOTICE: Adobe permits you to use, modify, and distribute this file in
6 // accordance with the terms of the Adobe license agreement accompanying it.
7 /*****************************************************************************/
8
9 /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_lens_correction.cpp#1 $ */
10 /* $DateTime: 2012/05/30 13:28:51 $ */
11 /* $Change: 832332 $ */
12 /* $Author: tknoll $ */
13
14 /*****************************************************************************/
15
16 #include <cfloat>
17 #include <limits.h>
18
19 #include "dng_1d_table.h"
20 #include "dng_assertions.h"
21 #include "dng_bottlenecks.h"
22 #include "dng_exceptions.h"
23 #include "dng_filter_task.h"
24 #include "dng_globals.h"
25 #include "dng_host.h"
26 #include "dng_image.h"
27 #include "dng_lens_correction.h"
28 #include "dng_negative.h"
29 #include "dng_safe_arithmetic.h"
30 #include "dng_sdk_limits.h"
31 #include "dng_tag_values.h"
32 #include "dng_utils.h"
33
34 /*****************************************************************************/
35
dng_warp_params()36 dng_warp_params::dng_warp_params ()
37
38 : fPlanes (1)
39 , fCenter (0.5, 0.5)
40
41 {
42
43 }
44
45 /*****************************************************************************/
46
dng_warp_params(uint32 planes,const dng_point_real64 & center)47 dng_warp_params::dng_warp_params (uint32 planes,
48 const dng_point_real64 ¢er)
49
50 : fPlanes (planes)
51 , fCenter (center)
52
53 {
54
55 DNG_ASSERT (planes >= 1, "Too few planes." );
56 DNG_ASSERT (planes <= kMaxColorPlanes, "Too many planes.");
57
58 DNG_ASSERT (fCenter.h >= 0.0 && fCenter.h <= 1.0,
59 "Center (horizontal) out of range.");
60
61 DNG_ASSERT (fCenter.v >= 0.0 && fCenter.v <= 1.0,
62 "Center (vertical) out of range.");
63
64 }
65
66 /*****************************************************************************/
67
~dng_warp_params()68 dng_warp_params::~dng_warp_params ()
69 {
70
71 }
72
73 /*****************************************************************************/
74
IsNOPAll() const75 bool dng_warp_params::IsNOPAll () const
76 {
77
78 return IsRadNOPAll () &&
79 IsTanNOPAll ();
80
81 }
82
83 /*****************************************************************************/
84
IsNOP(uint32 plane) const85 bool dng_warp_params::IsNOP (uint32 plane) const
86 {
87
88 return IsRadNOP (plane) &&
89 IsTanNOP (plane);
90
91 }
92
93 /*****************************************************************************/
94
IsRadNOPAll() const95 bool dng_warp_params::IsRadNOPAll () const
96 {
97
98 for (uint32 plane = 0; plane < fPlanes; plane++)
99 {
100
101 if (!IsRadNOP (plane))
102 {
103 return false;
104 }
105
106 }
107
108 return true;
109
110 }
111
112 /*****************************************************************************/
113
IsRadNOP(uint32) const114 bool dng_warp_params::IsRadNOP (uint32 /* plane */) const
115 {
116
117 return false;
118
119 }
120
121 /*****************************************************************************/
122
IsTanNOPAll() const123 bool dng_warp_params::IsTanNOPAll () const
124 {
125
126 for (uint32 plane = 0; plane < fPlanes; plane++)
127 {
128
129 if (!IsTanNOP (plane))
130 {
131 return false;
132 }
133
134 }
135
136 return true;
137
138 }
139
140 /*****************************************************************************/
141
IsTanNOP(uint32) const142 bool dng_warp_params::IsTanNOP (uint32 /* plane */) const
143 {
144
145 return false;
146
147 }
148
149 /*****************************************************************************/
150
IsValid() const151 bool dng_warp_params::IsValid () const
152 {
153
154 if (fPlanes < 1 || fPlanes > kMaxColorPlanes)
155 {
156
157 return false;
158
159 }
160
161 if (fCenter.h < 0.0 ||
162 fCenter.h > 1.0 ||
163 fCenter.v < 0.0 ||
164 fCenter.v > 1.0)
165 {
166
167 return false;
168
169 }
170
171 return true;
172
173 }
174
175 /*****************************************************************************/
176
IsValidForNegative(const dng_negative & negative) const177 bool dng_warp_params::IsValidForNegative (const dng_negative &negative) const
178 {
179
180 if (!IsValid ())
181 {
182 return false;
183 }
184
185 if ((fPlanes != 1) &&
186 (fPlanes != negative.ColorChannels ()))
187 {
188 return false;
189 }
190
191 return true;
192
193 }
194
195 /*****************************************************************************/
196
EvaluateInverse(uint32 plane,real64 y) const197 real64 dng_warp_params::EvaluateInverse (uint32 plane,
198 real64 y) const
199 {
200
201 const uint32 kMaxIterations = 30;
202 const real64 kNearZero = 1.0e-10;
203
204 real64 x0 = 0.0;
205 real64 y0 = Evaluate (plane,
206 x0);
207
208 real64 x1 = 1.0;
209 real64 y1 = Evaluate (plane,
210 x1);
211
212 for (uint32 iteration = 0; iteration < kMaxIterations; iteration++)
213 {
214
215 if (Abs_real64 (y1 - y0) < kNearZero)
216 {
217 break;
218 }
219
220 const real64 x2 = Pin_real64 (0.0,
221 x1 + (y - y1) * (x1 - x0) / (y1 - y0),
222 1.0);
223
224 const real64 y2 = Evaluate (plane,
225 x2);
226
227 x0 = x1;
228 y0 = y1;
229
230 x1 = x2;
231 y1 = y2;
232
233 }
234
235 return x1;
236
237 }
238
239 /*****************************************************************************/
240
EvaluateTangential2(uint32 plane,const dng_point_real64 & diff) const241 dng_point_real64 dng_warp_params::EvaluateTangential2 (uint32 plane,
242 const dng_point_real64 &diff) const
243 {
244
245 const real64 dvdv = diff.v * diff.v;
246 const real64 dhdh = diff.h * diff.h;
247
248 const real64 rr = dvdv + dhdh;
249
250 dng_point_real64 diffSqr (dvdv,
251 dhdh);
252
253 return EvaluateTangential (plane,
254 rr,
255 diff,
256 diffSqr);
257
258 }
259
260 /*****************************************************************************/
261
EvaluateTangential3(uint32 plane,real64 r2,const dng_point_real64 & diff) const262 dng_point_real64 dng_warp_params::EvaluateTangential3 (uint32 plane,
263 real64 r2,
264 const dng_point_real64 &diff) const
265 {
266
267 dng_point_real64 diffSqr (diff.v * diff.v,
268 diff.h * diff.h);
269
270 return EvaluateTangential (plane,
271 r2,
272 diff,
273 diffSqr);
274
275 }
276
277 /*****************************************************************************/
278
Dump() const279 void dng_warp_params::Dump () const
280 {
281
282 #if qDNGValidate
283
284 printf ("Planes: %u\n", (unsigned) fPlanes);
285
286 printf (" Optical center:\n"
287 " h = %.6lf\n"
288 " v = %.6lf\n",
289 fCenter.h,
290 fCenter.v);
291
292 #endif
293
294 }
295
296 /*****************************************************************************/
297
dng_warp_params_rectilinear()298 dng_warp_params_rectilinear::dng_warp_params_rectilinear ()
299
300 : dng_warp_params ()
301
302 {
303
304 for (uint32 plane = 0; plane < kMaxColorPlanes; plane++)
305 {
306
307 fRadParams [plane] = dng_vector (4);
308 fTanParams [plane] = dng_vector (2);
309
310 fRadParams [plane][0] = 1.0;
311
312 }
313
314 }
315
316 /*****************************************************************************/
317
dng_warp_params_rectilinear(uint32 planes,const dng_vector radParams[],const dng_vector tanParams[],const dng_point_real64 & center)318 dng_warp_params_rectilinear::dng_warp_params_rectilinear (uint32 planes,
319 const dng_vector radParams [],
320 const dng_vector tanParams [],
321 const dng_point_real64 ¢er)
322
323 : dng_warp_params (planes,
324 center)
325
326 {
327
328 for (uint32 i = 0; i < fPlanes; i++)
329 {
330 fRadParams [i] = radParams [i];
331 fTanParams [i] = tanParams [i];
332 }
333
334 }
335
336 /*****************************************************************************/
337
~dng_warp_params_rectilinear()338 dng_warp_params_rectilinear::~dng_warp_params_rectilinear ()
339 {
340
341 }
342
343 /*****************************************************************************/
344
IsRadNOP(uint32 plane) const345 bool dng_warp_params_rectilinear::IsRadNOP (uint32 plane) const
346 {
347
348 DNG_ASSERT (plane < fPlanes, "plane out of range.");
349
350 const dng_vector &r = fRadParams [plane];
351
352 return (r [0] == 1.0 &&
353 r [1] == 0.0 &&
354 r [2] == 0.0 &&
355 r [3] == 0.0);
356
357 }
358
359 /*****************************************************************************/
360
IsTanNOP(uint32 plane) const361 bool dng_warp_params_rectilinear::IsTanNOP (uint32 plane) const
362 {
363
364 DNG_ASSERT (plane < fPlanes, "plane out of range.");
365
366 const dng_vector &t = fTanParams [plane];
367
368 return (t [0] == 0.0 &&
369 t [1] == 0.0);
370
371 }
372
373 /*****************************************************************************/
374
IsValid() const375 bool dng_warp_params_rectilinear::IsValid () const
376 {
377
378 for (uint32 plane = 0; plane < fPlanes; plane++)
379 {
380
381 if (fRadParams [plane].Count () != 4)
382 {
383 return false;
384 }
385
386 if (fTanParams [plane].Count () < 2)
387 {
388 return false;
389 }
390
391 }
392
393 return dng_warp_params::IsValid ();
394
395 }
396
397 /*****************************************************************************/
398
PropagateToAllPlanes(uint32 totalPlanes)399 void dng_warp_params_rectilinear::PropagateToAllPlanes (uint32 totalPlanes)
400 {
401
402 for (uint32 plane = fPlanes; plane < totalPlanes; plane++)
403 {
404
405 fRadParams [plane] = fRadParams [0];
406 fTanParams [plane] = fTanParams [0];
407
408 }
409
410 }
411
412 /*****************************************************************************/
413
Evaluate(uint32 plane,real64 x) const414 real64 dng_warp_params_rectilinear::Evaluate (uint32 plane,
415 real64 x) const
416 {
417
418 const dng_vector &K = fRadParams [plane]; // Coefficients.
419
420 const real64 x2 = x * x;
421
422 return x * (K [0] + x2 * (K [1] + x2 * (K [2] + x2 * K [3])));
423
424 }
425
426 /*****************************************************************************/
427
EvaluateRatio(uint32 plane,real64 r2) const428 real64 dng_warp_params_rectilinear::EvaluateRatio (uint32 plane,
429 real64 r2) const
430 {
431
432 const dng_vector &K = fRadParams [plane]; // Coefficients.
433
434 return K [0] + r2 * (K [1] + r2 * (K [2] + r2 * K [3]));
435
436 }
437
438 /*****************************************************************************/
439
EvaluateTangential(uint32 plane,real64 r2,const dng_point_real64 & diff,const dng_point_real64 & diff2) const440 dng_point_real64 dng_warp_params_rectilinear::EvaluateTangential (uint32 plane,
441 real64 r2,
442 const dng_point_real64 &diff,
443 const dng_point_real64 &diff2) const
444 {
445
446 const real64 kt0 = fTanParams [plane][0];
447 const real64 kt1 = fTanParams [plane][1];
448
449 const real64 dh = diff.h;
450 const real64 dv = diff.v;
451
452 const real64 dhdh = diff2.h;
453 const real64 dvdv = diff2.v;
454
455 return dng_point_real64 (kt0 * (r2 + 2.0 * dvdv) + (2.0 * kt1 * dh * dv), // v
456 kt1 * (r2 + 2.0 * dhdh) + (2.0 * kt0 * dh * dv)); // h
457
458 }
459
460 /*****************************************************************************/
461
MaxSrcRadiusGap(real64 maxDstGap) const462 real64 dng_warp_params_rectilinear::MaxSrcRadiusGap (real64 maxDstGap) const
463 {
464
465 real64 maxSrcGap = 0.0;
466
467 for (uint32 plane = 0; plane < fPlanes; plane++)
468 {
469
470 const dng_vector &coefs = fRadParams [plane];
471
472 const real64 k3 = coefs [1];
473 const real64 k5 = coefs [2];
474 const real64 k7 = coefs [3];
475
476 //
477 // Let f (r) be the radius warp function. Consider the function
478 //
479 // gap (r) = f (r + maxDstGap) - f (r)
480 //
481 // We wish to maximize gap (r) over the domain [0, 1 - maxDstGap]. This will
482 // give us a reasonable upper bound on the src tile size, independent of
483 // dstBounds.
484 //
485 // As usual, we maximize gap (r) by examining its critical points, i.e., by
486 // considering the roots of its derivative which lie in the domain [0, 1 -
487 // maxDstGap]. gap' (r) is a 5th-order polynomial. One of its roots is
488 // -maxDstGap / 2, which is negative and hence lies outside the domain of
489 // interest. This leaves 4 other possible roots. We solve for these
490 // analytically below.
491 //
492
493 real64 roots [4];
494 uint32 numRoots = 0;
495
496 if (k7 == 0.0)
497 {
498
499 if (k5 == 0.0)
500 {
501
502 // No roots in [0,1].
503
504 }
505
506 else
507 {
508
509 // k7 is zero, but k5 is non-zero. At most two real roots.
510
511 const real64 discrim = 25.0 * (-6.0 * k3 * k5 - 5.0 * k5 * maxDstGap * maxDstGap);
512
513 if (discrim >= 0.0)
514 {
515
516 // Two real roots.
517
518 const real64 scale = 0.1 * k5;
519 const real64 offset = -5.0 * maxDstGap * k5;
520 const real64 sDiscrim = sqrt (discrim);
521
522 roots [numRoots++] = scale * (offset + sDiscrim);
523 roots [numRoots++] = scale * (offset - sDiscrim);
524
525 }
526
527 }
528
529 }
530
531 else
532 {
533
534 // k7 is non-zero. Up to 4 real roots.
535
536 const real64 d = maxDstGap;
537 const real64 d2 = d * d;
538 const real64 d4 = d2 * d2;
539
540 const real64 discrim = 25.0 * k5 * k5
541 - 63.0 * k3 * k7
542 + 35.0 * d2 * k5 * k7
543 + 49.0 * d4 * k7 * k7;
544
545 if (discrim >= 0.0)
546 {
547
548 const real64 sDiscrim = 4.0 * k7 * sqrt (discrim);
549
550 const real64 offset = -20.0 * k5 * k7 - 35.0 * d2 * k7 * k7;
551
552 const real64 discrim1 = offset - sDiscrim;
553 const real64 discrim2 = offset + sDiscrim;
554
555 const real64 scale = sqrt (21.0) / (42.0 * k7);
556
557 if (discrim1 >= 0.0)
558 {
559
560 const real64 offset1 = -d * 0.5;
561 const real64 sDiscrim1 = scale * sqrt (discrim1);
562
563 roots [numRoots++] = offset1 + sDiscrim1;
564 roots [numRoots++] = offset1 - sDiscrim1;
565
566 }
567
568 if (discrim2 >= 0.0)
569 {
570
571 const real64 offset2 = -d * 0.5;
572 const real64 sDiscrim2 = scale * sqrt (discrim2);
573
574 roots [numRoots++] = offset2 + sDiscrim2;
575 roots [numRoots++] = offset2 - sDiscrim2;
576
577 }
578
579 }
580
581 }
582
583 real64 planeMaxSrcGap = 0.0;
584
585 // Examine the endpoints.
586
587 {
588
589 // Check left endpoint: f (maxDstGap) - f (0). Remember that f (0) == 0.
590
591 const real64 gap1 = Evaluate (plane, maxDstGap);
592
593 planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap1);
594
595 // Check right endpoint: f (1) - f (1 - maxDstGap).
596
597 const real64 gap2 = Evaluate (plane, 1.0)
598 - Evaluate (plane, 1.0 - maxDstGap);
599
600 planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap2);
601
602 }
603
604 // Examine the roots we found, if any.
605
606 for (uint32 i = 0; i < numRoots; i++)
607 {
608
609 const real64 r = roots [i];
610
611 if (r > 0.0 && r < 1.0 - maxDstGap)
612 {
613
614 const real64 gap = Evaluate (plane, r + maxDstGap)
615 - Evaluate (plane, r);
616
617 planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap);
618
619 }
620
621 }
622
623 maxSrcGap = Max_real64 (maxSrcGap,
624 planeMaxSrcGap);
625
626 }
627
628 return maxSrcGap;
629
630 }
631
632 /*****************************************************************************/
633
MaxSrcTanGap(dng_point_real64 minDst,dng_point_real64 maxDst) const634 dng_point_real64 dng_warp_params_rectilinear::MaxSrcTanGap (dng_point_real64 minDst,
635 dng_point_real64 maxDst) const
636 {
637
638 const real64 v [] = { minDst.v, maxDst.v, 0.0 };
639 const real64 h [] = { minDst.h, maxDst.h, 0.0 };
640
641 dng_point_real64 maxGap;
642
643 for (uint32 plane = 0; plane < fPlanes; plane++)
644 {
645
646 real64 hMin = +FLT_MAX;
647 real64 hMax = -FLT_MAX;
648
649 real64 vMin = +FLT_MAX;
650 real64 vMax = -FLT_MAX;
651
652 for (uint32 i = 0; i < 3; i++)
653 {
654
655 for (uint32 j = 0; j < 3; j++)
656 {
657
658 dng_point_real64 dstDiff (v [i],
659 h [j]);
660
661 dng_point_real64 srcDiff = EvaluateTangential2 (plane,
662 dstDiff);
663
664 hMin = Min_real64 (hMin, srcDiff.h);
665 hMax = Max_real64 (hMax, srcDiff.h);
666
667 vMin = Min_real64 (vMin, srcDiff.v);
668 vMax = Max_real64 (vMax, srcDiff.v);
669
670 }
671
672 }
673
674 const real64 hGap = hMax - hMin;
675 const real64 vGap = vMax - vMin;
676
677 maxGap.h = Max_real64 (maxGap.h, hGap);
678 maxGap.v = Max_real64 (maxGap.v, vGap);
679
680 }
681
682 return maxGap;
683
684 }
685
686 /*****************************************************************************/
687
Dump() const688 void dng_warp_params_rectilinear::Dump () const
689 {
690
691 #if qDNGValidate
692
693 dng_warp_params::Dump ();
694
695 for (uint32 plane = 0; plane < fPlanes; plane++)
696 {
697
698 printf (" Plane %u:\n", (unsigned) plane);
699
700 printf (" Radial params: %.6lf, %.6lf, %.6lf, %.6lf\n",
701 fRadParams [plane][0],
702 fRadParams [plane][1],
703 fRadParams [plane][2],
704 fRadParams [plane][3]);
705
706 printf (" Tangential params: %.6lf, %.6lf\n",
707 fTanParams [plane][0],
708 fTanParams [plane][1]);
709
710 }
711
712 #endif
713
714 }
715
716 /*****************************************************************************/
717
dng_warp_params_fisheye()718 dng_warp_params_fisheye::dng_warp_params_fisheye ()
719
720 : dng_warp_params ()
721
722 {
723
724 for (uint32 plane = 0; plane < kMaxColorPlanes; plane++)
725 {
726
727 fRadParams [plane] = dng_vector (4);
728
729 }
730
731 }
732
733 /*****************************************************************************/
734
dng_warp_params_fisheye(uint32 planes,const dng_vector radParams[],const dng_point_real64 & center)735 dng_warp_params_fisheye::dng_warp_params_fisheye (uint32 planes,
736 const dng_vector radParams [],
737 const dng_point_real64 ¢er)
738
739 : dng_warp_params (planes, center)
740
741 {
742
743 for (uint32 i = 0; i < fPlanes; i++)
744 {
745
746 fRadParams [i] = radParams [i];
747
748 }
749
750 }
751
752 /*****************************************************************************/
753
~dng_warp_params_fisheye()754 dng_warp_params_fisheye::~dng_warp_params_fisheye ()
755 {
756
757 }
758
759 /*****************************************************************************/
760
IsRadNOP(uint32) const761 bool dng_warp_params_fisheye::IsRadNOP (uint32 /* plane */) const
762 {
763
764 return false;
765
766 }
767
768 /*****************************************************************************/
769
IsTanNOP(uint32) const770 bool dng_warp_params_fisheye::IsTanNOP (uint32 /* plane */) const
771 {
772
773 return true;
774
775 }
776
777 /*****************************************************************************/
778
IsValid() const779 bool dng_warp_params_fisheye::IsValid () const
780 {
781
782 for (uint32 plane = 0; plane < fPlanes; plane++)
783 {
784
785 if (fRadParams [plane].Count () != 4)
786 {
787 return false;
788 }
789
790 }
791
792 return dng_warp_params::IsValid ();
793
794 }
795
796 /*****************************************************************************/
797
PropagateToAllPlanes(uint32 totalPlanes)798 void dng_warp_params_fisheye::PropagateToAllPlanes (uint32 totalPlanes)
799 {
800
801 for (uint32 plane = fPlanes; plane < totalPlanes; plane++)
802 {
803
804 fRadParams [plane] = fRadParams [0];
805
806 }
807
808 }
809
810 /*****************************************************************************/
811
Evaluate(uint32 plane,real64 r) const812 real64 dng_warp_params_fisheye::Evaluate (uint32 plane,
813 real64 r) const
814 {
815
816 const real64 t = atan (r);
817
818 const dng_vector &K = fRadParams [plane];
819
820 const real64 t2 = t * t;
821
822 return t * (K [0] + t2 * (K [1] + t2 * (K [2] + t2 * K [3])));
823
824 }
825
826 /*****************************************************************************/
827
EvaluateRatio(uint32 plane,real64 rSqr) const828 real64 dng_warp_params_fisheye::EvaluateRatio (uint32 plane,
829 real64 rSqr) const
830 {
831
832 const real64 eps = 1.0e-12;
833
834 if (rSqr < eps)
835 {
836
837 // r is very close to zero.
838
839 return 1.0;
840
841 }
842
843 const real64 r = sqrt (rSqr);
844
845 return Evaluate (plane, r) / r;
846
847 }
848
849 /*****************************************************************************/
850
EvaluateTangential(uint32,real64,const dng_point_real64 &,const dng_point_real64 &) const851 dng_point_real64 dng_warp_params_fisheye::EvaluateTangential (uint32 /* plane */,
852 real64 /* r2 */,
853 const dng_point_real64 & /* diff */,
854 const dng_point_real64 & /* diff2 */) const
855 {
856
857 // This fisheye model does not support tangential warping.
858
859 ThrowProgramError ();
860
861 return dng_point_real64 (0.0, 0.0);
862
863 }
864
865 /*****************************************************************************/
866
MaxSrcRadiusGap(real64 maxDstGap) const867 real64 dng_warp_params_fisheye::MaxSrcRadiusGap (real64 maxDstGap) const
868 {
869
870 //
871 // Let f (r) be the radius warp function. Consider the function
872 //
873 // gap (r) = f (r + maxDstGap) - f (r)
874 //
875 // We wish to maximize gap (r) over the domain [0, 1 - maxDstGap]. This will
876 // give us a reasonable upper bound on the src tile size, independent of
877 // dstBounds.
878 //
879 // Ideally, we'd like to maximize gap (r) by examining its critical points,
880 // i.e., by considering the roots of its derivative which lie in the domain [0,
881 // 1 - maxDstGap]. However, gap' (r) is too complex to find its roots
882 // analytically.
883 //
884
885 real64 maxSrcGap = 0.0;
886
887 DNG_REQUIRE (maxDstGap > 0.0, "maxDstGap must be positive.");
888
889 const real64 kMaxValue = 1.0 - maxDstGap;
890
891 const uint32 kSteps = 128;
892
893 const real64 kNorm = kMaxValue / real64 (kSteps - 1);
894
895 for (uint32 plane = 0; plane < fPlanes; plane++)
896 {
897
898 for (uint32 i = 0; i < kSteps; i++)
899 {
900
901 const real64 tt = i * kNorm;
902
903 const real64 gap = Evaluate (plane, tt + maxDstGap)
904 - Evaluate (plane, tt);
905
906 maxSrcGap = Max_real64 (maxSrcGap,
907 gap);
908
909 }
910
911 }
912
913 return maxSrcGap;
914
915 }
916
917 /*****************************************************************************/
918
MaxSrcTanGap(dng_point_real64,dng_point_real64) const919 dng_point_real64 dng_warp_params_fisheye::MaxSrcTanGap (dng_point_real64 /* minDst */,
920 dng_point_real64 /* maxDst */) const
921 {
922
923 // This fisheye model does not support tangential distortion.
924
925 return dng_point_real64 (0.0, 0.0);
926
927 }
928
929 /*****************************************************************************/
930
Dump() const931 void dng_warp_params_fisheye::Dump () const
932 {
933
934 #if qDNGValidate
935
936 dng_warp_params::Dump ();
937
938 for (uint32 plane = 0; plane < fPlanes; plane++)
939 {
940
941 printf (" Plane %u:\n", (unsigned) plane);
942
943 printf (" Radial params: %.6lf, %.6lf, %.6lf, %.6lf\n",
944 fRadParams [plane][0],
945 fRadParams [plane][1],
946 fRadParams [plane][2],
947 fRadParams [plane][3]);
948
949 }
950
951 #endif
952
953 }
954
955 /*****************************************************************************/
956
957 class dng_filter_warp: public dng_filter_task
958 {
959
960 protected:
961
962 AutoPtr<dng_warp_params> fParams;
963
964 dng_point_real64 fCenter;
965
966 dng_resample_weights_2d fWeights;
967
968 real64 fNormRadius;
969 real64 fInvNormRadius;
970
971 bool fIsRadNOP;
972 bool fIsTanNOP;
973
974 const real64 fPixelScaleV;
975 const real64 fPixelScaleVInv;
976
977 public:
978
979 dng_filter_warp (const dng_image &srcImage,
980 dng_image &dstImage,
981 const dng_negative &negative,
982 AutoPtr<dng_warp_params> ¶ms);
983
984 virtual void Initialize (dng_host &host);
985
986 virtual dng_rect SrcArea (const dng_rect &dstArea);
987
988 virtual dng_point SrcTileSize (const dng_point &dstTileSize);
989
990 virtual void ProcessArea (uint32 threadIndex,
991 dng_pixel_buffer &srcBuffer,
992 dng_pixel_buffer &dstBuffer);
993
994 virtual dng_point_real64 GetSrcPixelPosition (const dng_point_real64 &dst,
995 uint32 plane);
996
997 };
998
999 /*****************************************************************************/
1000
dng_filter_warp(const dng_image & srcImage,dng_image & dstImage,const dng_negative & negative,AutoPtr<dng_warp_params> & params)1001 dng_filter_warp::dng_filter_warp (const dng_image &srcImage,
1002 dng_image &dstImage,
1003 const dng_negative &negative,
1004 AutoPtr<dng_warp_params> ¶ms)
1005
1006 : dng_filter_task (srcImage,
1007 dstImage)
1008
1009 , fParams (params.Release ())
1010
1011 , fCenter ()
1012
1013 , fWeights ()
1014
1015 , fNormRadius (1.0)
1016 , fInvNormRadius (1.0)
1017
1018 , fIsRadNOP (false)
1019 , fIsTanNOP (false)
1020
1021 , fPixelScaleV (1.0 / negative.PixelAspectRatio ())
1022 , fPixelScaleVInv (1.0 / fPixelScaleV)
1023
1024 {
1025
1026 // Force float processing.
1027
1028 fSrcPixelType = ttFloat;
1029 fDstPixelType = ttFloat;
1030
1031 fIsRadNOP = fParams->IsRadNOPAll ();
1032 fIsTanNOP = fParams->IsTanNOPAll ();
1033
1034 const uint32 negPlanes = negative.ColorChannels ();
1035
1036 DNG_ASSERT (negPlanes >= 1, "Too few planes." );
1037 DNG_ASSERT (negPlanes <= kMaxColorPlanes, "Too many planes.");
1038
1039 (void) negPlanes;
1040
1041 // At least one set of params must do something interesting.
1042
1043 if (fIsRadNOP && fIsTanNOP)
1044 {
1045 ThrowProgramError ();
1046 }
1047
1048 // Make sure the warp params are valid for this negative.
1049
1050 if (!fParams->IsValidForNegative (negative))
1051 {
1052 ThrowBadFormat ();
1053 }
1054
1055 // Compute center.
1056
1057 const dng_rect bounds = srcImage.Bounds ();
1058
1059 fCenter.h = Lerp_real64 ((real64) bounds.l,
1060 (real64) bounds.r,
1061 fParams->fCenter.h);
1062
1063 fCenter.v = Lerp_real64 ((real64) bounds.t,
1064 (real64) bounds.b,
1065 fParams->fCenter.v);
1066
1067 // Compute max pixel radius and derive various normalized radius values. Note
1068 // that when computing the max pixel radius, we must take into account the pixel
1069 // aspect ratio.
1070
1071 {
1072
1073 dng_rect squareBounds (bounds);
1074
1075 squareBounds.b = squareBounds.t +
1076 Round_int32 (fPixelScaleV * (real64) squareBounds.H ());
1077
1078 const dng_point_real64 squareCenter (Lerp_real64 ((real64) squareBounds.t,
1079 (real64) squareBounds.b,
1080 fParams->fCenter.v),
1081
1082 Lerp_real64 ((real64) squareBounds.l,
1083 (real64) squareBounds.r,
1084 fParams->fCenter.h));
1085
1086 fNormRadius = MaxDistancePointToRect (squareCenter,
1087 squareBounds);
1088
1089 fInvNormRadius = 1.0 / fNormRadius;
1090
1091 }
1092
1093 // Propagate warp params to other planes.
1094
1095 fParams->PropagateToAllPlanes (fDstPlanes);
1096
1097 }
1098
1099 /*****************************************************************************/
1100
Initialize(dng_host & host)1101 void dng_filter_warp::Initialize (dng_host &host)
1102 {
1103
1104 // Make resample weights.
1105
1106 const dng_resample_function &kernel = dng_resample_bicubic::Get ();
1107
1108 fWeights.Initialize (kernel,
1109 host.Allocator ());
1110
1111 }
1112
1113 /*****************************************************************************/
1114
SrcArea(const dng_rect & dstArea)1115 dng_rect dng_filter_warp::SrcArea (const dng_rect &dstArea)
1116 {
1117
1118 // Walk each pixel of the boundary of dstArea, map it to the uncorrected src
1119 // pixel position, and return the rectangle that contains all such src pixels.
1120
1121 int32 xMin = INT_MAX;
1122 int32 xMax = INT_MIN;
1123 int32 yMin = INT_MAX;
1124 int32 yMax = INT_MIN;
1125
1126 for (uint32 plane = 0; plane < fDstPlanes; plane++)
1127 {
1128
1129 // Top and bottom edges.
1130
1131 for (int32 c = dstArea.l; c < dstArea.r; c++)
1132 {
1133
1134 // Top edge.
1135
1136 {
1137
1138 const dng_point_real64 dst (dstArea.t, c);
1139
1140 const dng_point_real64 src = GetSrcPixelPosition (dst, plane);
1141
1142 const int32 y = ConvertDoubleToInt32 (floor (src.v));
1143
1144 yMin = Min_int32 (yMin, y);
1145
1146 }
1147
1148 // Bottom edge.
1149
1150 {
1151
1152 const dng_point_real64 dst (dstArea.b - 1, c);
1153
1154 const dng_point_real64 src = GetSrcPixelPosition (dst, plane);
1155
1156 const int32 y = ConvertDoubleToInt32 (ceil (src.v));
1157
1158 yMax = Max_int32 (yMax, y);
1159
1160 }
1161
1162 }
1163
1164 // Left and right edges.
1165
1166 for (int32 r = dstArea.t; r < dstArea.b; r++)
1167 {
1168
1169 // Left edge.
1170
1171 {
1172
1173 const dng_point_real64 dst (r, dstArea.l);
1174
1175 const dng_point_real64 src = GetSrcPixelPosition (dst, plane);
1176
1177 const int32 x = ConvertDoubleToInt32 (floor (src.h));
1178
1179 xMin = Min_int32 (xMin, x);
1180
1181 }
1182
1183 // Right edge.
1184
1185 {
1186
1187 const dng_point_real64 dst (r, dstArea.r - 1);
1188
1189 const dng_point_real64 src = GetSrcPixelPosition (dst, plane);
1190
1191 const int32 x = ConvertDoubleToInt32 (ceil (src.h));
1192
1193 xMax = Max_int32 (xMax, x);
1194
1195 }
1196
1197 }
1198
1199 }
1200
1201 // Pad each side by filter radius.
1202
1203 const int32 pad = ConvertUint32ToInt32(fWeights.Radius());
1204
1205 xMin = SafeInt32Sub(xMin, pad);
1206 yMin = SafeInt32Sub(yMin, pad);
1207 xMax = SafeInt32Add(xMax, pad);
1208 yMax = SafeInt32Add(yMax, pad);
1209
1210 xMax = SafeInt32Add(xMax, 1);
1211 yMax = SafeInt32Add(yMax, 1);
1212
1213 const dng_rect srcArea (yMin,
1214 xMin,
1215 yMax,
1216 xMax);
1217
1218 return srcArea;
1219
1220 }
1221
1222 /*****************************************************************************/
1223
SrcTileSize(const dng_point & dstTileSize)1224 dng_point dng_filter_warp::SrcTileSize (const dng_point &dstTileSize)
1225 {
1226
1227 // Obtain an upper bound on the source tile size. We'll do this by considering
1228 // upper bounds on the radial and tangential warp components separately, then add
1229 // them together. This is not a tight bound but is good enough because the
1230 // tangential terms are usually quite small.
1231
1232 // Get upper bound on src tile size from radial warp.
1233
1234 DNG_REQUIRE (dstTileSize.v > 0, "Invalid tile height.");
1235 DNG_REQUIRE (dstTileSize.h > 0, "Invalid tile width.");
1236
1237 const real64 maxDstGap = fInvNormRadius * hypot ((real64) dstTileSize.h,
1238 (real64) dstTileSize.v);
1239
1240 dng_point srcTileSize;
1241
1242 if (maxDstGap >= 1.0)
1243 {
1244
1245 // The proposed tile size is unusually large, i.e., its hypotenuse is larger
1246 // than the maximum radius. Bite the bullet and just return a tile size big
1247 // enough to process the whole image.
1248
1249 srcTileSize = SrcArea (fDstImage.Bounds ()).Size ();
1250
1251 }
1252
1253 else
1254 {
1255
1256 // maxDstGap < 1.0.
1257
1258 const real64 maxSrcGap = fParams->MaxSrcRadiusGap (maxDstGap);
1259
1260 const int32 dim = ConvertDoubleToInt32 (ceil (maxSrcGap * fNormRadius));
1261
1262 srcTileSize = dng_point (dim, dim);
1263
1264 }
1265
1266 srcTileSize.h += ConvertUint32ToInt32(fWeights.Width());
1267 srcTileSize.v += ConvertUint32ToInt32(fWeights.Width());
1268
1269 // Get upper bound on src tile size from tangential warp.
1270
1271 const dng_rect_real64 bounds (fSrcImage.Bounds ());
1272
1273 const dng_point_real64 minDst ((bounds.t - fCenter.v) * fInvNormRadius,
1274 (bounds.l - fCenter.h) * fInvNormRadius);
1275
1276 const dng_point_real64 maxDst ((bounds.b - 1.0 - fCenter.v) * fInvNormRadius,
1277 (bounds.r - 1.0 - fCenter.h) * fInvNormRadius);
1278
1279 const dng_point_real64 srcTanGap = fParams->MaxSrcTanGap (minDst,
1280 maxDst);
1281
1282 // Add the two bounds together.
1283
1284 srcTileSize.v += ConvertDoubleToInt32 (ceil (srcTanGap.v * fNormRadius));
1285 srcTileSize.h += ConvertDoubleToInt32 (ceil (srcTanGap.h * fNormRadius));
1286
1287 return srcTileSize;
1288
1289 }
1290
1291 /*****************************************************************************/
1292
ProcessArea(uint32,dng_pixel_buffer & srcBuffer,dng_pixel_buffer & dstBuffer)1293 void dng_filter_warp::ProcessArea (uint32 /* threadIndex */,
1294 dng_pixel_buffer &srcBuffer,
1295 dng_pixel_buffer &dstBuffer)
1296 {
1297
1298 // Prepare resample constants.
1299
1300 const int32 wCount = fWeights.Width ();
1301
1302 const dng_point srcOffset (fWeights.Offset (),
1303 fWeights.Offset ());
1304
1305 const real64 numSubsamples = (real64) kResampleSubsampleCount2D;
1306
1307 // Prepare area and step constants.
1308
1309 const dng_rect srcArea = srcBuffer.fArea;
1310 const dng_rect dstArea = dstBuffer.fArea;
1311
1312 const int32 srcRowStep = (int32) srcBuffer.RowStep ();
1313
1314 const int32 hMin = srcArea.l;
1315 const int32 hMax = SafeInt32Sub (SafeInt32Sub (srcArea.r, wCount), 1);
1316
1317 const int32 vMin = srcArea.t;
1318 const int32 vMax = SafeInt32Sub (SafeInt32Sub (srcArea.b, wCount), 1);
1319
1320 if (hMax < hMin || vMax < vMin)
1321 {
1322
1323 ThrowBadFormat ("Empty source area in dng_filter_warp.");
1324
1325 }
1326
1327 // Warp each plane.
1328
1329 for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++)
1330 {
1331
1332 real32 *dPtr = dstBuffer.DirtyPixel_real32 (dstArea.t,
1333 dstArea.l,
1334 plane);
1335
1336 for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++)
1337 {
1338
1339 uint32 dstIndex = 0;
1340
1341 for (int32 dstCol = dstArea.l; dstCol < dstArea.r; dstCol++, dstIndex++)
1342 {
1343
1344 // Get destination (corrected) pixel position.
1345
1346 const dng_point_real64 dPos ((real64) dstRow,
1347 (real64) dstCol);
1348
1349 // Warp to source (uncorrected) pixel position.
1350
1351 const dng_point_real64 sPos = GetSrcPixelPosition (dPos,
1352 plane);
1353
1354 // Decompose into integer and fractional parts.
1355
1356 dng_point sInt (ConvertDoubleToInt32 (floor (sPos.v)),
1357 ConvertDoubleToInt32 (floor (sPos.h)));
1358
1359 dng_point sFct (ConvertDoubleToInt32 ((sPos.v - (real64) sInt.v) * numSubsamples),
1360 ConvertDoubleToInt32 ((sPos.h - (real64) sInt.h) * numSubsamples));
1361
1362 // Add resample offset.
1363
1364 sInt = sInt + srcOffset;
1365
1366 // Clip.
1367
1368 if (sInt.h < hMin)
1369 {
1370 sInt.h = hMin;
1371 sFct.h = 0;
1372 }
1373
1374 else if (sInt.h > hMax)
1375 {
1376 sInt.h = hMax;
1377 sFct.h = 0;
1378 }
1379
1380 if (sInt.v < vMin)
1381 {
1382 sInt.v = vMin;
1383 sFct.v = 0;
1384 }
1385
1386 else if (sInt.v > vMax)
1387 {
1388 sInt.v = vMax;
1389 sFct.v = 0;
1390 }
1391
1392 // Perform 2D resample.
1393
1394 const real32 *w = fWeights.Weights32 (sFct);
1395
1396 const real32 *s = srcBuffer.ConstPixel_real32 (sInt.v,
1397 sInt.h,
1398 plane);
1399
1400 real32 total = 0.0f;
1401
1402 for (int32 i = 0; i < wCount; i++)
1403 {
1404
1405 for (int32 j = 0; j < wCount; j++)
1406 {
1407
1408 total += w [j] * s [j];
1409
1410 }
1411
1412 w += wCount;
1413 s += srcRowStep;
1414
1415 }
1416
1417 // Store final pixel value.
1418
1419 dPtr [dstIndex] = Pin_real32 (total);
1420
1421 }
1422
1423 // Advance to next row.
1424
1425 dPtr += dstBuffer.RowStep ();
1426
1427 }
1428
1429 }
1430
1431 }
1432
1433 /*****************************************************************************/
1434
GetSrcPixelPosition(const dng_point_real64 & dst,uint32 plane)1435 dng_point_real64 dng_filter_warp::GetSrcPixelPosition (const dng_point_real64 &dst,
1436 uint32 plane)
1437 {
1438
1439 const dng_point_real64 diff = dst - fCenter;
1440
1441 const dng_point_real64 diffNorm (diff.v * fInvNormRadius,
1442 diff.h * fInvNormRadius);
1443
1444 const dng_point_real64 diffNormScaled (diffNorm.v * fPixelScaleV,
1445 diffNorm.h);
1446
1447 const dng_point_real64 diffNormSqr (diffNormScaled.v * diffNormScaled.v,
1448 diffNormScaled.h * diffNormScaled.h);
1449
1450 const real64 rr = Min_real64 (diffNormSqr.v + diffNormSqr.h,
1451 1.0);
1452
1453 dng_point_real64 dSrc;
1454
1455 if (fIsTanNOP)
1456 {
1457
1458 // Radial only.
1459
1460 const real64 ratio = fParams->EvaluateRatio (plane,
1461 rr);
1462
1463 dSrc.h = diff.h * ratio;
1464 dSrc.v = diff.v * ratio;
1465
1466 }
1467
1468 else if (fIsRadNOP)
1469 {
1470
1471 // Tangential only.
1472
1473 const dng_point_real64 tan = fParams->EvaluateTangential (plane,
1474 rr,
1475 diffNormScaled,
1476 diffNormSqr);
1477
1478 dSrc.h = diff.h + (fNormRadius * tan.h);
1479 dSrc.v = diff.v + (fNormRadius * tan.v * fPixelScaleVInv);
1480
1481 }
1482
1483 else
1484 {
1485
1486 // Radial and tangential.
1487
1488 const real64 ratio = fParams->EvaluateRatio (plane,
1489 rr);
1490
1491 const dng_point_real64 tan = fParams->EvaluateTangential (plane,
1492 rr,
1493 diffNormScaled,
1494 diffNormSqr);
1495
1496 dSrc.h = fNormRadius * (diffNorm.h * ratio + tan.h);
1497 dSrc.v = fNormRadius * (diffNorm.v * ratio + tan.v * fPixelScaleVInv);
1498
1499 }
1500
1501 return fCenter + dSrc;
1502
1503 }
1504
1505 /*****************************************************************************/
1506
dng_opcode_WarpRectilinear(const dng_warp_params_rectilinear & params,uint32 flags)1507 dng_opcode_WarpRectilinear::dng_opcode_WarpRectilinear (const dng_warp_params_rectilinear ¶ms,
1508 uint32 flags)
1509
1510 : dng_opcode (dngOpcode_WarpRectilinear,
1511 dngVersion_1_3_0_0,
1512 flags)
1513
1514 , fWarpParams (params)
1515
1516 {
1517
1518 if (!params.IsValid ())
1519 {
1520 ThrowBadFormat ();
1521 }
1522
1523 }
1524
1525 /*****************************************************************************/
1526
dng_opcode_WarpRectilinear(dng_stream & stream)1527 dng_opcode_WarpRectilinear::dng_opcode_WarpRectilinear (dng_stream &stream)
1528
1529 : dng_opcode (dngOpcode_WarpRectilinear,
1530 stream,
1531 "WarpRectilinear")
1532
1533 , fWarpParams ()
1534
1535 {
1536
1537 // Grab the size in bytes.
1538
1539 const uint32 bytes = stream.Get_uint32 ();
1540
1541 // Grab the number of planes to warp.
1542
1543 fWarpParams.fPlanes = stream.Get_uint32 ();
1544
1545 // Verify number of planes.
1546
1547 if (fWarpParams.fPlanes == 0 ||
1548 fWarpParams.fPlanes > kMaxColorPlanes)
1549 {
1550 ThrowBadFormat ();
1551 }
1552
1553 // Verify the size.
1554
1555 if (bytes != ParamBytes (fWarpParams.fPlanes))
1556 {
1557 ThrowBadFormat ();
1558 }
1559
1560 // Read warp parameters for each plane.
1561
1562 for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++)
1563 {
1564
1565 fWarpParams.fRadParams [plane][0] = stream.Get_real64 ();
1566 fWarpParams.fRadParams [plane][1] = stream.Get_real64 ();
1567 fWarpParams.fRadParams [plane][2] = stream.Get_real64 ();
1568 fWarpParams.fRadParams [plane][3] = stream.Get_real64 ();
1569
1570 fWarpParams.fTanParams [plane][0] = stream.Get_real64 ();
1571 fWarpParams.fTanParams [plane][1] = stream.Get_real64 ();
1572
1573 }
1574
1575 // Read the image center.
1576
1577 fWarpParams.fCenter.h = stream.Get_real64 ();
1578 fWarpParams.fCenter.v = stream.Get_real64 ();
1579
1580 #if qDNGValidate
1581
1582 if (gVerbose)
1583 {
1584
1585 fWarpParams.Dump ();
1586
1587 }
1588
1589 #endif
1590
1591 if (!fWarpParams.IsValid ())
1592 {
1593 ThrowBadFormat ();
1594 }
1595
1596 }
1597
1598 /*****************************************************************************/
1599
IsNOP() const1600 bool dng_opcode_WarpRectilinear::IsNOP () const
1601 {
1602
1603 return fWarpParams.IsNOPAll ();
1604
1605 }
1606
1607 /*****************************************************************************/
1608
IsValidForNegative(const dng_negative & negative) const1609 bool dng_opcode_WarpRectilinear::IsValidForNegative (const dng_negative &negative) const
1610 {
1611
1612 return fWarpParams.IsValidForNegative (negative);
1613
1614 }
1615
1616 /*****************************************************************************/
1617
PutData(dng_stream & stream) const1618 void dng_opcode_WarpRectilinear::PutData (dng_stream &stream) const
1619 {
1620
1621 const uint32 bytes = ParamBytes (fWarpParams.fPlanes);
1622
1623 stream.Put_uint32 (bytes);
1624
1625 stream.Put_uint32 (fWarpParams.fPlanes);
1626
1627 for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++)
1628 {
1629
1630 stream.Put_real64 (fWarpParams.fRadParams [plane][0]);
1631 stream.Put_real64 (fWarpParams.fRadParams [plane][1]);
1632 stream.Put_real64 (fWarpParams.fRadParams [plane][2]);
1633 stream.Put_real64 (fWarpParams.fRadParams [plane][3]);
1634
1635 stream.Put_real64 (fWarpParams.fTanParams [plane][0]);
1636 stream.Put_real64 (fWarpParams.fTanParams [plane][1]);
1637
1638 }
1639
1640 stream.Put_real64 (fWarpParams.fCenter.h);
1641 stream.Put_real64 (fWarpParams.fCenter.v);
1642
1643 }
1644
1645 /*****************************************************************************/
1646
Apply(dng_host & host,dng_negative & negative,AutoPtr<dng_image> & image)1647 void dng_opcode_WarpRectilinear::Apply (dng_host &host,
1648 dng_negative &negative,
1649 AutoPtr<dng_image> &image)
1650 {
1651
1652 #if qDNGValidate
1653
1654 dng_timer timer ("WarpRectilinear time");
1655
1656 #endif
1657
1658 // Allocate destination image.
1659
1660 AutoPtr<dng_image> dstImage (host.Make_dng_image (image->Bounds (),
1661 image->Planes (),
1662 image->PixelType ()));
1663
1664 // Warp the image.
1665
1666 AutoPtr<dng_warp_params> params (new dng_warp_params_rectilinear (fWarpParams));
1667
1668 dng_filter_warp filter (*image,
1669 *dstImage,
1670 negative,
1671 params);
1672
1673 filter.Initialize (host);
1674
1675 host.PerformAreaTask (filter,
1676 image->Bounds ());
1677
1678 // Return the new image.
1679
1680 image.Reset (dstImage.Release ());
1681
1682 }
1683
1684 /*****************************************************************************/
1685
ParamBytes(uint32 planes)1686 uint32 dng_opcode_WarpRectilinear::ParamBytes (uint32 planes)
1687 {
1688
1689 return (1 * (uint32) sizeof (uint32) ) + // Number of planes.
1690 (6 * (uint32) sizeof (real64) * planes) + // Warp coefficients.
1691 (2 * (uint32) sizeof (real64) ); // Optical center.
1692
1693 }
1694
1695 /*****************************************************************************/
1696
dng_opcode_WarpFisheye(const dng_warp_params_fisheye & params,uint32 flags)1697 dng_opcode_WarpFisheye::dng_opcode_WarpFisheye (const dng_warp_params_fisheye ¶ms,
1698 uint32 flags)
1699
1700 : dng_opcode (dngOpcode_WarpFisheye,
1701 dngVersion_1_3_0_0,
1702 flags)
1703
1704 , fWarpParams (params)
1705
1706 {
1707
1708 if (!params.IsValid ())
1709 {
1710 ThrowBadFormat ();
1711 }
1712
1713 }
1714
1715 /*****************************************************************************/
1716
dng_opcode_WarpFisheye(dng_stream & stream)1717 dng_opcode_WarpFisheye::dng_opcode_WarpFisheye (dng_stream &stream)
1718
1719 : dng_opcode (dngOpcode_WarpFisheye,
1720 stream,
1721 "WarpFisheye")
1722
1723 , fWarpParams ()
1724
1725 {
1726
1727 // Grab the size in bytes.
1728
1729 const uint32 bytes = stream.Get_uint32 ();
1730
1731 // Grab the number of planes to warp.
1732
1733 fWarpParams.fPlanes = stream.Get_uint32 ();
1734
1735 // Verify number of planes.
1736
1737 if (fWarpParams.fPlanes == 0 ||
1738 fWarpParams.fPlanes > kMaxColorPlanes)
1739 {
1740 ThrowBadFormat ();
1741 }
1742
1743 // Verify the size.
1744
1745 if (bytes != ParamBytes (fWarpParams.fPlanes))
1746 {
1747 ThrowBadFormat ();
1748 }
1749
1750 // Read warp parameters for each plane.
1751
1752 for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++)
1753 {
1754
1755 fWarpParams.fRadParams [plane][0] = stream.Get_real64 ();
1756 fWarpParams.fRadParams [plane][1] = stream.Get_real64 ();
1757 fWarpParams.fRadParams [plane][2] = stream.Get_real64 ();
1758 fWarpParams.fRadParams [plane][3] = stream.Get_real64 ();
1759
1760 }
1761
1762 // Read the image center.
1763
1764 fWarpParams.fCenter.h = stream.Get_real64 ();
1765 fWarpParams.fCenter.v = stream.Get_real64 ();
1766
1767 #if qDNGValidate
1768
1769 if (gVerbose)
1770 {
1771
1772 fWarpParams.Dump ();
1773
1774 }
1775
1776 #endif
1777
1778 if (!fWarpParams.IsValid ())
1779 {
1780 ThrowBadFormat ();
1781 }
1782
1783 }
1784
1785 /*****************************************************************************/
1786
IsNOP() const1787 bool dng_opcode_WarpFisheye::IsNOP () const
1788 {
1789
1790 return fWarpParams.IsNOPAll ();
1791
1792 }
1793
1794 /*****************************************************************************/
1795
IsValidForNegative(const dng_negative & negative) const1796 bool dng_opcode_WarpFisheye::IsValidForNegative (const dng_negative &negative) const
1797 {
1798
1799 return fWarpParams.IsValidForNegative (negative);
1800
1801 }
1802
1803 /*****************************************************************************/
1804
PutData(dng_stream & stream) const1805 void dng_opcode_WarpFisheye::PutData (dng_stream &stream) const
1806 {
1807
1808 const uint32 bytes = ParamBytes (fWarpParams.fPlanes);
1809
1810 stream.Put_uint32 (bytes);
1811
1812 // Write the number of planes.
1813
1814 stream.Put_uint32 (fWarpParams.fPlanes);
1815
1816 // Write the warp coefficients.
1817
1818 for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++)
1819 {
1820
1821 stream.Put_real64 (fWarpParams.fRadParams [plane][0]);
1822 stream.Put_real64 (fWarpParams.fRadParams [plane][1]);
1823 stream.Put_real64 (fWarpParams.fRadParams [plane][2]);
1824 stream.Put_real64 (fWarpParams.fRadParams [plane][3]);
1825
1826 }
1827
1828 // Write the optical center.
1829
1830 stream.Put_real64 (fWarpParams.fCenter.h);
1831 stream.Put_real64 (fWarpParams.fCenter.v);
1832
1833 }
1834
1835 /*****************************************************************************/
1836
Apply(dng_host & host,dng_negative & negative,AutoPtr<dng_image> & image)1837 void dng_opcode_WarpFisheye::Apply (dng_host &host,
1838 dng_negative &negative,
1839 AutoPtr<dng_image> &image)
1840 {
1841
1842 #if qDNGValidate
1843
1844 dng_timer timer ("WarpFisheye time");
1845
1846 #endif
1847
1848 // Allocate destination image.
1849
1850 AutoPtr<dng_image> dstImage (host.Make_dng_image (image->Bounds (),
1851 image->Planes (),
1852 image->PixelType ()));
1853
1854 // Warp the image.
1855
1856 AutoPtr<dng_warp_params> params (new dng_warp_params_fisheye (fWarpParams));
1857
1858 dng_filter_warp filter (*image,
1859 *dstImage,
1860 negative,
1861 params);
1862
1863 filter.Initialize (host);
1864
1865 host.PerformAreaTask (filter,
1866 image->Bounds ());
1867
1868 // Return the new image.
1869
1870 image.Reset (dstImage.Release ());
1871
1872 }
1873
1874 /*****************************************************************************/
1875
ParamBytes(uint32 planes)1876 uint32 dng_opcode_WarpFisheye::ParamBytes (uint32 planes)
1877 {
1878
1879 return (1 * (uint32) sizeof (uint32) ) + // Number of planes.
1880 (4 * (uint32) sizeof (real64) * planes) + // Warp coefficients.
1881 (2 * (uint32) sizeof (real64) ); // Optical center.
1882
1883 }
1884
1885 /*****************************************************************************/
1886
dng_vignette_radial_params()1887 dng_vignette_radial_params::dng_vignette_radial_params ()
1888
1889 : fParams (kNumTerms)
1890 , fCenter (0.5, 0.5)
1891
1892 {
1893
1894 }
1895
1896 /*****************************************************************************/
1897
dng_vignette_radial_params(const dng_std_vector<real64> & params,const dng_point_real64 & center)1898 dng_vignette_radial_params::dng_vignette_radial_params (const dng_std_vector<real64> ¶ms,
1899 const dng_point_real64 ¢er)
1900
1901 : fParams (params)
1902 , fCenter (center)
1903
1904 {
1905
1906 }
1907
1908 /*****************************************************************************/
1909
IsNOP() const1910 bool dng_vignette_radial_params::IsNOP () const
1911 {
1912
1913 for (uint32 i = 0; i < fParams.size (); i++)
1914 {
1915
1916 if (fParams [i] != 0.0)
1917 {
1918 return false;
1919 }
1920
1921 }
1922
1923 return true;
1924
1925 }
1926
1927 /*****************************************************************************/
1928
IsValid() const1929 bool dng_vignette_radial_params::IsValid () const
1930 {
1931
1932 if (fParams.size () != kNumTerms)
1933 {
1934 return false;
1935 }
1936
1937 if (fCenter.h < 0.0 ||
1938 fCenter.h > 1.0 ||
1939 fCenter.v < 0.0 ||
1940 fCenter.v > 1.0)
1941 {
1942 return false;
1943 }
1944
1945 return true;
1946
1947 }
1948
1949 /*****************************************************************************/
1950
Dump() const1951 void dng_vignette_radial_params::Dump () const
1952 {
1953
1954 #if qDNGValidate
1955
1956 printf (" Radial vignette params: ");
1957
1958 for (uint32 i = 0; i < fParams.size (); i++)
1959 {
1960
1961 printf ("%s%.6lf",
1962 (i == 0) ? "" : ", ",
1963 fParams [i]);
1964
1965 }
1966
1967 printf ("\n");
1968
1969 printf (" Optical center:\n"
1970 " h = %.6lf\n"
1971 " v = %.6lf\n",
1972 fCenter.h,
1973 fCenter.v);
1974
1975 #endif
1976
1977 }
1978
1979 /*****************************************************************************/
1980
1981 class dng_vignette_radial_function: public dng_1d_function
1982 {
1983
1984 protected:
1985
1986 const dng_vignette_radial_params fParams;
1987
1988 public:
1989
dng_vignette_radial_function(const dng_vignette_radial_params & params)1990 explicit dng_vignette_radial_function (const dng_vignette_radial_params ¶ms)
1991
1992 : fParams (params)
1993
1994 {
1995
1996 }
1997
1998 // x represents r^2, where r is the normalized Euclidean distance from the
1999 // optical center to a pixel. r lies in [0,1], so r^2 (and hence x) also lies
2000 // in [0,1].
2001
Evaluate(real64 x) const2002 virtual real64 Evaluate (real64 x) const
2003 {
2004
2005 DNG_REQUIRE (fParams.fParams.size () == dng_vignette_radial_params::kNumTerms,
2006 "Bad number of vignette opcode coefficients.");
2007
2008 real64 sum = 0.0;
2009
2010 const dng_std_vector<real64> &v = fParams.fParams;
2011
2012 for (dng_std_vector<real64>::const_reverse_iterator i = v.rbegin (); i != v.rend (); i++)
2013 {
2014 sum = x * ((*i) + sum);
2015 }
2016
2017 sum += 1.0;
2018
2019 return sum;
2020
2021 }
2022
2023 };
2024
2025 /*****************************************************************************/
2026
dng_opcode_FixVignetteRadial(const dng_vignette_radial_params & params,uint32 flags)2027 dng_opcode_FixVignetteRadial::dng_opcode_FixVignetteRadial (const dng_vignette_radial_params ¶ms,
2028 uint32 flags)
2029
2030 : dng_inplace_opcode (dngOpcode_FixVignetteRadial,
2031 dngVersion_1_3_0_0,
2032 flags)
2033
2034 , fParams (params)
2035
2036 , fImagePlanes (1)
2037
2038 , fSrcOriginH (0)
2039 , fSrcOriginV (0)
2040
2041 , fSrcStepH (0)
2042 , fSrcStepV (0)
2043
2044 , fTableInputBits (0)
2045 , fTableOutputBits (0)
2046
2047 , fGainTable ()
2048
2049 {
2050
2051 if (!params.IsValid ())
2052 {
2053 ThrowBadFormat ();
2054 }
2055
2056 }
2057
2058 /*****************************************************************************/
2059
dng_opcode_FixVignetteRadial(dng_stream & stream)2060 dng_opcode_FixVignetteRadial::dng_opcode_FixVignetteRadial (dng_stream &stream)
2061
2062 : dng_inplace_opcode (dngOpcode_FixVignetteRadial,
2063 stream,
2064 "FixVignetteRadial")
2065
2066 , fParams ()
2067
2068 , fImagePlanes (1)
2069
2070 , fSrcOriginH (0)
2071 , fSrcOriginV (0)
2072
2073 , fSrcStepH (0)
2074 , fSrcStepV (0)
2075
2076 , fTableInputBits (0)
2077 , fTableOutputBits (0)
2078
2079 , fGainTable ()
2080
2081 {
2082
2083 // Grab the size in bytes.
2084
2085 const uint32 bytes = stream.Get_uint32 ();
2086
2087 // Verify the size.
2088
2089 if (bytes != ParamBytes ())
2090 {
2091 ThrowBadFormat ();
2092 }
2093
2094 // Read vignette coefficients.
2095
2096 fParams.fParams = dng_std_vector<real64> (dng_vignette_radial_params::kNumTerms);
2097
2098 for (uint32 i = 0; i < dng_vignette_radial_params::kNumTerms; i++)
2099 {
2100 fParams.fParams [i] = stream.Get_real64 ();
2101 }
2102
2103 // Read the image center.
2104
2105 fParams.fCenter.h = stream.Get_real64 ();
2106 fParams.fCenter.v = stream.Get_real64 ();
2107
2108 // Debug.
2109
2110 #if qDNGValidate
2111
2112 if (gVerbose)
2113 {
2114
2115 fParams.Dump ();
2116
2117 }
2118
2119 #endif
2120
2121 if (!fParams.IsValid ())
2122 {
2123 ThrowBadFormat ();
2124 }
2125
2126 }
2127
2128 /*****************************************************************************/
2129
IsNOP() const2130 bool dng_opcode_FixVignetteRadial::IsNOP () const
2131 {
2132
2133 return fParams.IsNOP ();
2134
2135 }
2136
2137 /*****************************************************************************/
2138
IsValidForNegative(const dng_negative &) const2139 bool dng_opcode_FixVignetteRadial::IsValidForNegative (const dng_negative & /* negative */) const
2140 {
2141
2142 return fParams.IsValid ();
2143
2144 }
2145
2146 /*****************************************************************************/
2147
PutData(dng_stream & stream) const2148 void dng_opcode_FixVignetteRadial::PutData (dng_stream &stream) const
2149 {
2150
2151 const uint32 bytes = ParamBytes ();
2152
2153 stream.Put_uint32 (bytes);
2154
2155 DNG_REQUIRE (fParams.fParams.size () == dng_vignette_radial_params::kNumTerms,
2156 "Bad number of vignette opcode coefficients.");
2157
2158 for (uint32 i = 0; i < dng_vignette_radial_params::kNumTerms; i++)
2159 {
2160 stream.Put_real64 (fParams.fParams [i]);
2161 }
2162
2163 stream.Put_real64 (fParams.fCenter.h);
2164 stream.Put_real64 (fParams.fCenter.v);
2165
2166 }
2167
2168 /*****************************************************************************/
2169
Prepare(dng_negative & negative,uint32 threadCount,const dng_point & tileSize,const dng_rect & imageBounds,uint32 imagePlanes,uint32 bufferPixelType,dng_memory_allocator & allocator)2170 void dng_opcode_FixVignetteRadial::Prepare (dng_negative &negative,
2171 uint32 threadCount,
2172 const dng_point &tileSize,
2173 const dng_rect &imageBounds,
2174 uint32 imagePlanes,
2175 uint32 bufferPixelType,
2176 dng_memory_allocator &allocator)
2177 {
2178
2179 // This opcode is restricted to 32-bit images.
2180
2181 if (bufferPixelType != ttFloat)
2182 {
2183 ThrowBadFormat ();
2184 }
2185
2186 // Sanity check number of planes.
2187
2188 DNG_ASSERT (imagePlanes >= 1 && imagePlanes <= kMaxColorPlanes,
2189 "Bad number of planes.");
2190
2191 if (imagePlanes < 1 || imagePlanes > kMaxColorPlanes)
2192 {
2193 ThrowProgramError ();
2194 }
2195
2196 fImagePlanes = imagePlanes;
2197
2198 // Set the vignette correction curve.
2199
2200 const dng_vignette_radial_function curve (fParams);
2201
2202 // Grab the destination image area.
2203
2204 const dng_rect_real64 bounds (imageBounds);
2205
2206 // Determine the optical center and maximum radius in pixel coordinates.
2207
2208 const dng_point_real64 centerPixel (Lerp_real64 (bounds.t,
2209 bounds.b,
2210 fParams.fCenter.v),
2211
2212 Lerp_real64 (bounds.l,
2213 bounds.r,
2214 fParams.fCenter.h));
2215
2216 const real64 pixelScaleV = 1.0 / negative.PixelAspectRatio ();
2217
2218 const real64 maxRadius = hypot (Max_real64 (Abs_real64 (centerPixel.v - bounds.t),
2219 Abs_real64 (centerPixel.v - bounds.b)) * pixelScaleV,
2220
2221 Max_real64 (Abs_real64 (centerPixel.h - bounds.l),
2222 Abs_real64 (centerPixel.h - bounds.r)));
2223
2224 const dng_point_real64 radius (maxRadius,
2225 maxRadius);
2226
2227 // Find origin and scale.
2228
2229 const real64 pixelScaleH = 1.0;
2230
2231 fSrcOriginH = Real64ToFixed64 (-centerPixel.h * pixelScaleH / radius.h);
2232 fSrcOriginV = Real64ToFixed64 (-centerPixel.v * pixelScaleV / radius.v);
2233
2234 fSrcStepH = Real64ToFixed64 (pixelScaleH / radius.h);
2235 fSrcStepV = Real64ToFixed64 (pixelScaleV / radius.v);
2236
2237 // Adjust for pixel centers.
2238
2239 fSrcOriginH += fSrcStepH >> 1;
2240 fSrcOriginV += fSrcStepV >> 1;
2241
2242 // Evaluate 32-bit vignette correction table.
2243
2244 dng_1d_table table32;
2245
2246 table32.Initialize (allocator,
2247 curve,
2248 false);
2249
2250 // Find maximum scale factor.
2251
2252 const real64 maxScale = Max_real32 (table32.Interpolate (0.0f),
2253 table32.Interpolate (1.0f));
2254
2255 // Find table input bits.
2256
2257 fTableInputBits = 16;
2258
2259 // Find table output bits.
2260
2261 fTableOutputBits = 15;
2262
2263 while ((1 << fTableOutputBits) * maxScale > 65535.0)
2264 {
2265 fTableOutputBits--;
2266 }
2267
2268 // Allocate 16-bit scale table.
2269
2270 const uint32 tableEntries = (1 << fTableInputBits) + 1;
2271
2272 fGainTable.Reset (allocator.Allocate (tableEntries * (uint32) sizeof (uint16)));
2273
2274 uint16 *table16 = fGainTable->Buffer_uint16 ();
2275
2276 // Interpolate 32-bit table into 16-bit table.
2277
2278 const real32 scale0 = 1.0f / (1 << fTableInputBits );
2279 const real32 scale1 = 1.0f * (1 << fTableOutputBits);
2280
2281 for (uint32 index = 0; index < tableEntries; index++)
2282 {
2283
2284 real32 x = index * scale0;
2285
2286 real32 y = table32.Interpolate (x) * scale1;
2287
2288 table16 [index] = (uint16) Round_uint32 (y);
2289
2290 }
2291
2292 // Prepare vignette mask buffers.
2293
2294 {
2295
2296 const uint32 pixelType = ttShort;
2297 const uint32 bufferSize = ComputeBufferSize(pixelType, tileSize,
2298 imagePlanes, pad16Bytes);
2299
2300 for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++)
2301 {
2302
2303 fMaskBuffers [threadIndex] . Reset (allocator.Allocate (bufferSize));
2304
2305 }
2306
2307 }
2308
2309 }
2310
2311 /*****************************************************************************/
2312
ProcessArea(dng_negative &,uint32 threadIndex,dng_pixel_buffer & buffer,const dng_rect & dstArea,const dng_rect &)2313 void dng_opcode_FixVignetteRadial::ProcessArea (dng_negative & /* negative */,
2314 uint32 threadIndex,
2315 dng_pixel_buffer &buffer,
2316 const dng_rect &dstArea,
2317 const dng_rect & /* imageBounds */)
2318 {
2319
2320 // Setup mask pixel buffer.
2321
2322 dng_pixel_buffer maskPixelBuffer(dstArea, 0, fImagePlanes, ttShort,
2323 pcRowInterleavedAlign16,
2324 fMaskBuffers [threadIndex]->Buffer ());
2325
2326 // Compute mask.
2327
2328 DoVignetteMask16 (maskPixelBuffer.DirtyPixel_uint16 (dstArea.t, dstArea.l),
2329 dstArea.H (),
2330 dstArea.W (),
2331 maskPixelBuffer.RowStep (),
2332 fSrcOriginH + fSrcStepH * dstArea.l,
2333 fSrcOriginV + fSrcStepV * dstArea.t,
2334 fSrcStepH,
2335 fSrcStepV,
2336 fTableInputBits,
2337 fGainTable->Buffer_uint16 ());
2338
2339 // Apply mask.
2340
2341 DoVignette32 (buffer.DirtyPixel_real32 (dstArea.t, dstArea.l),
2342 maskPixelBuffer.ConstPixel_uint16 (dstArea.t, dstArea.l),
2343 dstArea.H (),
2344 dstArea.W (),
2345 fImagePlanes,
2346 buffer.RowStep (),
2347 buffer.PlaneStep (),
2348 maskPixelBuffer.RowStep (),
2349 fTableOutputBits);
2350
2351 }
2352
2353 /*****************************************************************************/
2354
ParamBytes()2355 uint32 dng_opcode_FixVignetteRadial::ParamBytes ()
2356 {
2357
2358 const uint32 N = dng_vignette_radial_params::kNumTerms;
2359
2360 return ((N * sizeof (real64)) + // Vignette coefficients.
2361 (2 * sizeof (real64))); // Optical center.
2362
2363 }
2364
2365 /*****************************************************************************/
2366