Hugin trunk 0.1
Loading...
Searching...
No Matches
ResponseTransform.h
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
24#ifndef _PHOTOMETRIC_VIGNETTING_CORRECTION_H
25#define _PHOTOMETRIC_VIGNETTING_CORRECTION_H
26
27#include <vector>
28#include <functional>
29#include "hugin_config.h"
30#include <random>
31
32#include <vigra/stdimage.hxx>
33#include <vigra/numerictraits.hxx>
34#include <vigra/array_vector.hxx>
35
37#include <vigra_ext/lut.h>
38#include <vigra_ext/utils.h>
40
41
42namespace HuginBase { namespace Photometric {
43
44
50template <class VTIn>
52{
53
54 public:
57
59 typedef std::vector<double> LUT;
60
61
62 public:
65
68
70 virtual ~ResponseTransform() {};
71
72 private:
75
76
77 public:
79 void setFlatfield(const vigra::FImage * flat)
80 { m_flatfield = flat; }
81
84
89
91 typename vigra::NumericTraits<VT1>::RealPromote
92 apply(VT1 v, const hugin_utils::FDiff2D & pos, vigra::VigraTrueType) const;
93
95 typename vigra::NumericTraits<VT1>::RealPromote
97
99 typename vigra::NumericTraits<vigra::RGBValue<VT1> >::RealPromote
100 apply(vigra::RGBValue<VT1> v, const hugin_utils::FDiff2D & pos, vigra::VigraFalseType) const;
101
103 typename vigra::NumericTraits<vigra::RGBValue<VT1> >::RealPromote
104 apply(vigra::RGBValue<VT1> v, const hugin_utils::FDiff2D & pos) const;
105
106
108 template <class T>
109 typename vigra::NumericTraits<T>::RealPromote
110 operator()(T v, const hugin_utils::FDiff2D & pos) const { return apply(v, pos); }
111
112
113
114 public:
115
119 const vigra::FImage * m_flatfield;
121 std::vector<double> m_RadialVigCorrCoeff;
126
128};
129
130
136template <class VTIn, class VTOut>
138{
139
141
142 public:
144 typedef typename vigra::NumericTraits<VT1>::RealPromote VTInCompReal;
146
147 typedef std::vector<double> LUT;
148 typedef std::vector<dest_type> LUTD;
149
150
151 public:
154
157
160
161 private:
163 void init(const HuginBase::SrcPanoImage & src);
164
165
166 public:
168 void setHDROutput(bool hdrMode, double destExposure);
169
171 void setOutput(double destExposure, const LUTD & destLut, double scale, double rangeCompression = 0.0);
172
181
188 double dither(const double &v) const;
189
191 typename vigra::NumericTraits<dest_type>::RealPromote
192 apply(VT1 v, const hugin_utils::FDiff2D & pos, vigra::VigraTrueType) const;
193
195 typename vigra::NumericTraits<dest_type>::RealPromote
196 apply(VT1 v, const hugin_utils::FDiff2D & pos) const;
197
199 typename vigra::NumericTraits<vigra::RGBValue<VT1> >::RealPromote
200 apply(vigra::RGBValue<VT1> v, const hugin_utils::FDiff2D & pos, vigra::VigraFalseType) const;
201
203 typename vigra::NumericTraits<vigra::RGBValue<VT1> >::RealPromote
204 apply(vigra::RGBValue<VT1> v, const hugin_utils::FDiff2D & pos) const;
205
206
208 template <class T>
209 typename vigra::NumericTraits<T>::RealPromote
211 {
212 return apply(v, pos);
213 }
214
216 template <class T, class A>
217 A hdrWeight(T v, A a) const
218 {
219 if (m_hdrMode && a > 0) {
220 return vigra::NumericTraits<A>::fromRealPromote(vigra_ext::getMaxComponent(v)/(double)vigra_ext::LUTTraits<T>::max()*vigra_ext::LUTTraits<A>::max());
221 } else {
222 return a;
223 }
224 }
225
226 void emitGLSL(std::ostringstream& oss, std::vector<double>& invLut, std::vector<double>& destLut) const;
227
228 private:
230 {
231 m_lutRInv.clear();
232 if (!Base::m_lutR.empty())
233 {
234 m_lutRInv.reserve(Base::m_lutR.size());
236 for (int i = 0; i < Base::m_lutR.size(); i++)
237 {
238 const double f = static_cast<double>(i) / (Base::m_lutR.size() - 1);
239 m_lutRInv.push_back(slowRInvFunc(f));
240 };
241 };
242 };
243 protected: // needs be public?
252
253 private:
254 std::mt19937 Twister;
255};
256
257
258}} // namespace
259
260
261
262
263// templated implementation ----------------------------------------------------
264
265
266namespace HuginBase { namespace Photometric {
267
268
269template <class VTIn>
271{
272 m_radiusScale=0;
273 m_flatfield = 0;
274}
275
276template <class VTIn>
281
282
283template <class VTIn>
285{
286// DEBUG_DEBUG(" " << src.getFilename() << ": resp type:" << src.getResponseType() << " expo: " << src.getExposure());
287 m_flatfield = 0;
288 m_src = src;
289 m_radiusScale = 1.0/sqrt(m_src.getSize().x/2.0*m_src.getSize().x/2.0 + m_src.getSize().y/2.0*m_src.getSize().y/2.0);
290 m_srcExposure = m_src.getExposure();
291 //save some variables, direct access is slower since merging of layout mode
292 m_RadialVigCorrCoeff = m_src.getRadialVigCorrCoeff();
293 m_RadialVigCorrCenter = m_src.getRadialVigCorrCenter();
294 m_VigCorrMode = m_src.getVigCorrMode();
295 m_WhiteBalanceRed = m_src.getWhiteBalanceRed();
296 m_WhiteBalanceBlue = m_src.getWhiteBalanceBlue();
297
298 // build response function lookup table, if required
299 if (m_src.getResponseType() != HuginBase::SrcPanoImage::RESPONSE_LINEAR) {
300 // scale lut to right byte size..
302 // maximum lut size: 10 bits. Should be enought for most purposes.
303 // and fit into the L1 cache.
304 size_t lutLen=0;
305 if (lutLenD == 1.0 || (lutLenD > ((1<<10)-1))) {
306 lutLen = (1<<10);
307 } else {
308 lutLen = size_t(lutLenD) + 1;
309 }
310 switch (m_src.getResponseType())
311 {
313 {
314 if (lutLen == 1<<10) {
315 vigra_ext::EMoR::createEMoRLUT(m_src.getEMoRParams(), m_lutR);
316 } else {
317 // resize lut, if size doesn't fit
318 LUT tmp;
319 vigra_ext::EMoR::createEMoRLUT(m_src.getEMoRParams(), tmp);
320 m_lutR.resize(lutLen);
321 vigra_ext::resizeLUT(tmp, m_lutR);
322 }
323 }
324 break;
326 m_lutR.resize(lutLen);
327 vigra_ext::createGammaLUT(m_src.getGamma(), m_lutR);
328 break;
329 default:
330 // response curve is stored in src image
331 vigra_fail("ResponseTransform: unknown response function type");
332 break;
333 }
334 m_lutRFunc = vigra_ext::LUTFunctor<VT1, LUT>(m_lutR);
335 }
336}
337
338
339template <class VTIn>
341{
342 if (m_VigCorrMode & HuginBase::SrcPanoImage::VIGCORR_RADIAL) {
343 d = d - m_RadialVigCorrCenter;
344 // scale according to
345 d *= m_radiusScale;
346 double vig = m_RadialVigCorrCoeff[0];
347 double r2 = d.x*d.x + d.y*d.y;
348 double r = r2;
349 for (unsigned int i = 1; i < 4; i++) {
350 vig += m_RadialVigCorrCoeff[i] * r;
351 r *= r2;
352 }
353 return vig;
354 } else if (m_VigCorrMode & HuginBase::SrcPanoImage::VIGCORR_FLATFIELD) {
355 // TODO: implement flatfield
356 if (m_flatfield) {
357 int x = std::min(std::max(hugin_utils::roundi(d.x),0), static_cast<int>(m_flatfield->width()-1));
358 int y = std::min(std::max(hugin_utils::roundi(d.y),0), static_cast<int>(m_flatfield->height()-1));
359 return (*m_flatfield)(x,y);
360 } else {
361 return 1;
362 }
363 } else {
364 return 1;
365 }
366}
367
368
369template <class VTIn>
370typename vigra::NumericTraits<typename ResponseTransform<VTIn>::VT1>::RealPromote
371ResponseTransform<VTIn>::apply(typename ResponseTransform<VTIn>::VT1 v, const hugin_utils::FDiff2D & pos, vigra::VigraTrueType) const
372{
373 typename vigra::NumericTraits<VT1>::RealPromote ret = v;
374 // first, apply vignetting
375
376 ret = ret*calcVigFactor(pos)*m_srcExposure;
377 if (!m_lutR.empty()) {
378 return m_lutRFunc(ret);
379 } else {
380 return ret;
381 }
382}
383
384template <class VTIn>
385typename vigra::NumericTraits<vigra::RGBValue<typename ResponseTransform<VTIn>::VT1> >::RealPromote
386ResponseTransform<VTIn>::apply(vigra::RGBValue<typename ResponseTransform<VTIn>::VT1> v, const hugin_utils::FDiff2D & pos, vigra::VigraFalseType) const
387{
388 typename vigra::NumericTraits<vigra::RGBValue<VT1> >::RealPromote ret = v;
389 // first, apply vignetting
390 double common = calcVigFactor(pos)*m_srcExposure;
391 ret = ret*common;
392 // apply white balance factors
393 ret.red() = ret.red() * m_WhiteBalanceRed;
394 ret.blue() = ret.blue() * m_WhiteBalanceBlue;
395 // apply response curve
396 if (!m_lutR.empty()) {
397 return m_lutRFunc(ret);
398 } else {
399 return ret;
400 }
401}
402
403template <class VTIn>
404typename vigra::NumericTraits<typename ResponseTransform<VTIn>::VT1>::RealPromote
406{
407 typedef typename vigra::NumericTraits<VT1>::isScalar is_scalar;
408 return apply(v, pos, is_scalar());
409}
410
411template <class VTIn>
412typename vigra::NumericTraits<vigra::RGBValue<typename ResponseTransform<VTIn>::VT1> >::RealPromote
414{
415 typedef typename vigra::NumericTraits<vigra::RGBValue<VT1> >::isScalar is_scalar;
416 return apply(v, pos, is_scalar());
417}
418
419template <class VTIn, class VTOut>
421{
422 m_destExposure = 1.0;
423 m_hdrMode = false;
424 m_rangeCompression = 0.0;
425 m_intScale = 1;
426}
427
428template <class VTIn, class VTOut>
439
440template <class VTIn, class VTOut>
442{
443 m_destExposure = 1.0;
444 m_intScale = 1;
445 m_rangeCompression = 0.0;
446 Base::init(src);
447 if (!Base::m_lutR.empty()) {
448 invertLUT();
449 m_lutRInvFunc = vigra_ext::LUTFunctor<VT1, LUT>(m_lutRInv);
450 }
451}
452
453template <class VTIn, class VTOut>
455{
456 m_hdrMode = hdrMode;
457 m_intScale = 1;
458 m_destExposure = destExposure;
459 m_destLut.clear();
460 m_rangeCompression = 0.0;
461}
462
463template <class VTIn, class VTOut>
465{
466 m_hdrMode = false;
467 m_destLut = destLut;
468 if (!m_destLut.empty()) {
469 m_destLutFunc = vigra_ext::LUTFunctor<VTInCompReal, LUTD>(m_destLut);
470 m_rangeCompression = rangeCompression;
471 }
472 else
473 {
474 m_rangeCompression = 0.0;
475 };
476 m_destExposure = destExposure;
477 m_intScale = scale;
478}
479
480
481template <class VTIn, class VTOut>
483{
484 std::mt19937 &mt = const_cast<std::mt19937 &>(Twister);
485 double vFraction = v - floor(v);
486 // Only dither values within a certain range of the rounding cutoff point.
487 if (vFraction > 0.25 && vFraction <= 0.75) {
488 // Generate a random number between 0 and 0.5.
489 double random = 0.5 * (double)mt() / UINT_MAX;
490 if ((vFraction - 0.25) >= random) {
491 return ceil(v);
492 } else {
493 return floor(v);
494 }
495 } else {
496 return v;
497 }
498}
499
500
501template <class VTIn, class VTOut>
502typename vigra::NumericTraits<typename InvResponseTransform<VTIn,VTOut>::dest_type>::RealPromote
504{
505 // inverse response
506 typename vigra::NumericTraits<VT1>::RealPromote ret(v);
507 if (!Base::m_lutR.empty()) {
508 ret = m_lutRInvFunc(v);
509 } else {
511 }
512 // inverse vignetting and exposure
513 ret *= m_destExposure / (Base::calcVigFactor(pos) * Base::m_srcExposure);
514 // apply output transform if required
515 if (!m_destLut.empty()) {
516 if (m_rangeCompression > 0.0)
517 {
518 ret = log2(m_rangeCompression*ret + 1) / log2(m_rangeCompression + 1);
519 };
520 ret = m_destLutFunc(ret);
521 }
522 // dither all integer images
523 if ( m_intScale > 1) {
524 return dither(ret * m_intScale);
525 }
526 return ret;
527}
528
529
530template <class VTIn, class VTOut>
531typename vigra::NumericTraits<vigra::RGBValue<typename InvResponseTransform<VTIn,VTOut>::VT1> >::RealPromote
532InvResponseTransform<VTIn,VTOut>::apply(vigra::RGBValue<VT1> v, const hugin_utils::FDiff2D & pos, vigra::VigraFalseType) const
533{
534 typename vigra::NumericTraits<vigra::RGBValue<VT1> >::RealPromote ret(v);
535 if (!Base::m_lutR.empty()) {
536 ret = m_lutRInvFunc(v);
537 } else {
539 }
540
541 // inverse vignetting and exposure
542 ret *= m_destExposure/(Base::calcVigFactor(pos)*Base::m_srcExposure);
543 ret.red() /= Base::m_WhiteBalanceRed;
544 ret.blue() /= Base::m_WhiteBalanceBlue;
545 // apply output transform if required
546 if (!m_destLut.empty()) {
547 if (m_rangeCompression > 0)
548 {
549 ret.red() = log2(m_rangeCompression*ret.red() + 1) / log2(m_rangeCompression + 1);
550 ret.blue() = log2(m_rangeCompression*ret.blue() + 1) / log2(m_rangeCompression + 1);
551 ret.green() = log2(m_rangeCompression*ret.green() + 1) / log2(m_rangeCompression + 1);
552 };
553 ret = m_destLutFunc(ret);
554 }
555 // dither 8 bit images.
556 if (m_intScale > 1) {
557 for (size_t i=0; i < 3; i++) {
558 ret[i] = dither(ret[i] * m_intScale);
559 }
560 }
561 return ret;
562}
563
564
565template <class VTIn, class VTOut>
566typename vigra::NumericTraits<typename InvResponseTransform<VTIn,VTOut>::dest_type>::RealPromote
568{
569 typedef typename vigra::NumericTraits<VT1>::isScalar is_scalar;
570 return apply(v, pos, is_scalar());
571}
572
573template <class VTIn, class VTOut>
574typename vigra::NumericTraits<vigra::RGBValue<typename InvResponseTransform<VTIn,VTOut>::VT1> >::RealPromote
576{
577 typedef typename vigra::NumericTraits<vigra::RGBValue<VT1> >::isScalar is_scalar;
578 return apply(v, pos, is_scalar());
579}
580
581template <class VTIn, class VTOut>
582void
583InvResponseTransform<VTIn,VTOut>::emitGLSL(std::ostringstream& oss, std::vector<double>& invLut, std::vector<double>& destLut) const
584{
585 invLut = m_lutRInv;
586 destLut = m_destLut;
587
588 const double invLutSize = Base::m_lutR.size();
590 const double destLutSize = m_destLut.size();
591
592 oss << " // invLutSize = " << invLutSize << endl
593 << " // pixelMax = " << pixelMax << endl
594 << " // destLutSize = " << destLutSize << endl
595 << " // destExposure = " << m_destExposure << endl
596 << " // srcExposure = " << Base::m_srcExposure << endl
597 << " // whiteBalanceRed = " << Base::m_src.getWhiteBalanceRed() << endl
598 << " // whiteBalanceBlue = " << Base::m_src.getWhiteBalanceBlue() << endl;
599
600 // alpha hdrWeight
601 if (m_hdrMode)
602 {
603 // hdr weight needs to be calculated from input pixel value
604 // so do it before the whole InvResponseTransformation is done
605 oss << " p.a = max(p.r, max(p.g, p.b));" << endl;
606 }
607
608 if (!Base::m_lutR.empty()) {
609 oss << " p.rgb = p.rgb * " << (invLutSize - 1.0) << ";" << endl
610 << " vec2 invR = texture2DRect(InvLutTexture, vec2(p.r, 0.0)).sq;" << endl
611 << " vec2 invG = texture2DRect(InvLutTexture, vec2(p.g, 0.0)).sq;" << endl
612 << " vec2 invB = texture2DRect(InvLutTexture, vec2(p.b, 0.0)).sq;" << endl
613 << " vec3 invX = vec3(invR.x, invG.x, invB.x);" << endl
614 << " vec3 invY = vec3(invR.y, invG.y, invB.y);" << endl
615 << " vec3 invA = fract(p.rgb);" << endl
616 << " p.rgb = mix(invX, invY, invA);" << endl;
617 }
618
620 oss << " // VigCorrMode=VIGCORR_RADIAL" << endl
621 << " float vig = 1.0;" << endl
622 << " {" << endl
623 << " vec2 vigCorrCenter = vec2(" << Base::m_src.getRadialVigCorrCenter().x << ", "
624 << Base::m_src.getRadialVigCorrCenter().y << ");" << endl
625 << " float radiusScale=" << Base::m_radiusScale << ";" << endl
626 << " float radialVigCorrCoeff0 = " << Base::m_src.getRadialVigCorrCoeff()[0] << ";" << endl
627 << " float radialVigCorrCoeff1 = " << Base::m_src.getRadialVigCorrCoeff()[1] << ";" << endl
628 << " float radialVigCorrCoeff2 = " << Base::m_src.getRadialVigCorrCoeff()[2] << ";" << endl
629 << " float radialVigCorrCoeff3 = " << Base::m_src.getRadialVigCorrCoeff()[3] << ";" << endl
630 << " vec2 src = texture2DRect(CoordTexture, gl_TexCoord[0].st).sq;" << endl
631 << " vec2 d = src - vigCorrCenter;" << endl
632 << " d *= radiusScale;" << endl
633 << " vig = radialVigCorrCoeff0;" << endl
634 << " float r2 = dot(d, d);" << endl
635 << " float r = r2;" << endl
636 << " vig += radialVigCorrCoeff1 * r;" << endl
637 << " r *= r2;" << endl
638 << " vig += radialVigCorrCoeff2 * r;" << endl
639 << " r *= r2;" << endl
640 << " vig += radialVigCorrCoeff3 * r;" << endl
641 << " }" << endl;
643 oss << " // VigCorrMode=VIGCORR_FLATFIELD" << endl
644 << " float vig = 1.0;" << endl;
645 } else {
646 oss << " // VigCorrMode=none" << endl
647 << " float vig = 1.0;" << endl;
648 }
649
650 oss << " vec3 exposure_whitebalance = vec3("
651 << (m_destExposure / (Base::m_srcExposure * Base::m_src.getWhiteBalanceRed())) << ", "
652 << (m_destExposure / (Base::m_srcExposure)) << ", "
653 << (m_destExposure / (Base::m_srcExposure * Base::m_src.getWhiteBalanceBlue())) << ");" << endl
654 << " p.rgb = (p.rgb * exposure_whitebalance) / vig;" << endl;
655
656 if (!m_destLut.empty()) {
657 if (m_rangeCompression > 0)
658 {
659 oss << " p.rgb = log2(" << m_rangeCompression << " * p.rgb + 1.0) / " << log2(m_rangeCompression + 1) << ";" << endl;
660 };
661 oss << " p.rgb = p.rgb * " << (destLutSize - 1.0) << ";" << endl
662 << " vec2 destR = texture2DRect(DestLutTexture, vec2(p.r, 0.0)).sq;" << endl
663 << " vec2 destG = texture2DRect(DestLutTexture, vec2(p.g, 0.0)).sq;" << endl
664 << " vec2 destB = texture2DRect(DestLutTexture, vec2(p.b, 0.0)).sq;" << endl
665 << " vec3 destX = vec3(destR.x, destG.x, destB.x);" << endl
666 << " vec3 destY = vec3(destR.y, destG.y, destB.y);" << endl
667 << " vec3 destA = fract(p.rgb);" << endl
668 << " p.rgb = mix(destX, destY, destA);" << endl;
669 }
670}
671
672}} // namespace
673
674#endif // _H
@ VIGCORR_FLATFIELD
flatfield correction
@ VIGCORR_RADIAL
radial vignetting correction
@ RESPONSE_GAMMA
a simple gamma response curve
@ RESPONSE_LINEAR
linear response
@ RESPONSE_EMOR
empirical model of response
radiometric transformation, includes exposure, vignetting and white balance
vigra_ext::ValueTypeTraits< VTIn >::value_type VT1
vigra::NumericTraits< T >::RealPromote operator()(T v, const hugin_utils::FDiff2D &pos) const
deprecated
void emitGLSL(std::ostringstream &oss, std::vector< double > &invLut, std::vector< double > &destLut) const
vigra::NumericTraits< dest_type >::RealPromote apply(VT1 v, const hugin_utils::FDiff2D &pos, vigra::VigraTrueType) const
function for gray values (ignores white balance :-)
vigra_ext::ValueTypeTraits< VTOut >::value_type dest_type
void setHDROutput(bool hdrMode, double destExposure)
void init(const HuginBase::SrcPanoImage &src)
vigra_ext::LUTFunctor< VTInCompReal, LUTD > m_destLutFunc
void setOutput(double destExposure, const LUTD &destLut, double scale, double rangeCompression=0.0)
output lut
double dither(const double &v) const
Dithering is used to fool the eye into seeing gradients that are finer than the precision of the pixe...
vigra_ext::LUTFunctor< VT1, LUT > m_lutRInvFunc
vigra::NumericTraits< VT1 >::RealPromote VTInCompReal
radiometric transformation, includes exposure, vignetting and white balance.
vigra::NumericTraits< T >::RealPromote operator()(T v, const hugin_utils::FDiff2D &pos) const
deprecated
vigra::NumericTraits< vigra::RGBValue< VT1 > >::RealPromote apply(vigra::RGBValue< VT1 > v, const hugin_utils::FDiff2D &pos, vigra::VigraFalseType) const
function for color values
vigra_ext::ValueTypeTraits< VTIn >::value_type VT1
void setFlatfield(const vigra::FImage *flat)
vigra::NumericTraits< vigra::RGBValue< VT1 > >::RealPromote apply(vigra::RGBValue< VT1 > v, const hugin_utils::FDiff2D &pos) const
function for color values
double calcVigFactor(hugin_utils::FDiff2D d) const
vigra::NumericTraits< VT1 >::RealPromote apply(VT1 v, const hugin_utils::FDiff2D &pos, vigra::VigraTrueType) const
function for gray values (ignores white balance :-)
void initWithSrcImg(const HuginBase::SrcPanoImage &src)
vigra_ext::LUTFunctor< VT1, LUT > m_lutRFunc
vigra::NumericTraits< VT1 >::RealPromote apply(VT1 v, const hugin_utils::FDiff2D &pos) const
function for color values
All variables of a source image.
misc math function & classes used by other parts of the program
mainly consists of wrapper around the pano tools library, to assist in ressource management and to pr...
Definition wxcms.cpp:39
int roundi(T x)
Definition hugin_math.h:73
void createEMoRLUT(const std::vector< float > &params, VECTOR &lut)
Definition emor.h:44
void resizeLUT(const VEC &iLUT, VEC2 &oLUT)
Definition lut.h:64
V getMaxComponent(vigra::RGBValue< V > const &v)
get the maximum component of a vector (also works for single pixel types...)
Definition utils.h:268
void createGammaLUT(double gamma, VECTOR &lut)
Definition lut.h:50
void enforceMonotonicity(LUT &lut)
enforce monotonicity of an array (mostly used for lookup tables)
Definition lut.h:87
functor to apply a LUT to gray and color images.
Definition lut.h:253
functor to apply a LUT to gray and color images.
Definition lut.h:119
T1::value_type value_type
Definition utils.h:153
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41
functions to manage ROI's