24 #ifndef _PHOTOMETRIC_VIGNETTING_CORRECTION_H
25 #define _PHOTOMETRIC_VIGNETTING_CORRECTION_H
29 #include "hugin_config.h"
32 #include <vigra/stdimage.hxx>
33 #include <vigra/numerictraits.hxx>
34 #include <vigra/array_vector.hxx>
42 namespace HuginBase {
namespace Photometric {
59 typedef std::vector<double>
LUT;
91 typename vigra::NumericTraits<VT1>::RealPromote
95 typename vigra::NumericTraits<VT1>::RealPromote
99 typename vigra::NumericTraits<vigra::RGBValue<VT1> >::RealPromote
103 typename vigra::NumericTraits<vigra::RGBValue<VT1> >::RealPromote
109 typename vigra::NumericTraits<T>::RealPromote
136 template <
class VTIn,
class VTOut>
147 typedef std::vector<double>
LUT;
148 typedef std::vector<dest_type>
LUTD;
171 void setOutput(
double destExposure,
const LUTD & destLut,
double scale,
double rangeCompression = 0.0);
188 double dither(
const double &v)
const;
191 typename vigra::NumericTraits<dest_type>::RealPromote
195 typename vigra::NumericTraits<dest_type>::RealPromote
199 typename vigra::NumericTraits<vigra::RGBValue<VT1> >::RealPromote
203 typename vigra::NumericTraits<vigra::RGBValue<VT1> >::RealPromote
209 typename vigra::NumericTraits<T>::RealPromote
212 return apply(v, pos);
216 template <
class T,
class A>
226 void emitGLSL(std::ostringstream& oss, std::vector<double>& invLut, std::vector<double>& destLut)
const;
238 const double f =
static_cast<double>(i) / (
Base::m_lutR.size() - 1);
266 namespace HuginBase {
namespace Photometric {
269 template <
class VTIn>
276 template <
class VTIn>
283 template <
class VTIn>
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();
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();
305 if (lutLenD == 1.0 || (lutLenD > ((1<<10)-1))) {
308 lutLen = size_t(lutLenD) + 1;
310 switch (m_src.getResponseType())
314 if (lutLen == 1<<10) {
320 m_lutR.resize(lutLen);
326 m_lutR.resize(lutLen);
331 vigra_fail(
"ResponseTransform: unknown response function type");
339 template <
class VTIn>
343 d = d - m_RadialVigCorrCenter;
346 double vig = m_RadialVigCorrCoeff[0];
347 double r2 = d.
x*d.
x + d.
y*d.
y;
349 for (
unsigned int i = 1; i < 4; i++) {
350 vig += m_RadialVigCorrCoeff[i] * r;
359 return (*m_flatfield)(x,y);
369 template <
class VTIn>
370 typename vigra::NumericTraits<typename ResponseTransform<VTIn>::VT1>::RealPromote
373 typename vigra::NumericTraits<VT1>::RealPromote ret = v;
376 ret = ret*calcVigFactor(pos)*m_srcExposure;
377 if (!m_lutR.empty()) {
378 return m_lutRFunc(ret);
384 template <
class VTIn>
385 typename vigra::NumericTraits<vigra::RGBValue<typename ResponseTransform<VTIn>::VT1> >::RealPromote
388 typename vigra::NumericTraits<vigra::RGBValue<VT1> >::RealPromote ret = v;
390 double common = calcVigFactor(pos)*m_srcExposure;
393 ret.red() = ret.red() * m_WhiteBalanceRed;
394 ret.blue() = ret.blue() * m_WhiteBalanceBlue;
396 if (!m_lutR.empty()) {
397 return m_lutRFunc(ret);
403 template <
class VTIn>
404 typename vigra::NumericTraits<typename ResponseTransform<VTIn>::VT1>::RealPromote
407 typedef typename vigra::NumericTraits<VT1>::isScalar is_scalar;
408 return apply(v, pos, is_scalar());
411 template <
class VTIn>
412 typename vigra::NumericTraits<vigra::RGBValue<typename ResponseTransform<VTIn>::VT1> >::RealPromote
415 typedef typename vigra::NumericTraits<vigra::RGBValue<VT1> >::isScalar is_scalar;
416 return apply(v, pos, is_scalar());
419 template <
class VTIn,
class VTOut>
422 m_destExposure = 1.0;
424 m_rangeCompression = 0.0;
428 template <
class VTIn,
class VTOut>
430 :
Base(src), m_hdrMode(false), m_rangeCompression(0.0)
440 template <
class VTIn,
class VTOut>
443 m_destExposure = 1.0;
445 m_rangeCompression = 0.0;
447 if (!Base::m_lutR.empty()) {
453 template <
class VTIn,
class VTOut>
458 m_destExposure = destExposure;
460 m_rangeCompression = 0.0;
463 template <
class VTIn,
class VTOut>
468 if (!m_destLut.empty()) {
470 m_rangeCompression = rangeCompression;
474 m_rangeCompression = 0.0;
476 m_destExposure = destExposure;
481 template <
class VTIn,
class VTOut>
484 std::mt19937 &mt =
const_cast<std::mt19937 &
>(Twister);
485 double vFraction = v - floor(v);
487 if (vFraction > 0.25 && vFraction <= 0.75) {
489 double random = 0.5 * (double)mt() / UINT_MAX;
490 if ((vFraction - 0.25) >= random) {
501 template <
class VTIn,
class VTOut>
502 typename vigra::NumericTraits<typename InvResponseTransform<VTIn,VTOut>::dest_type>::RealPromote
506 typename vigra::NumericTraits<VT1>::RealPromote ret(v);
507 if (!Base::m_lutR.empty()) {
508 ret = m_lutRInvFunc(v);
513 ret *= m_destExposure / (Base::calcVigFactor(pos) * Base::m_srcExposure);
515 if (!m_destLut.empty()) {
516 if (m_rangeCompression > 0.0)
518 ret = log2(m_rangeCompression*ret + 1) / log2(m_rangeCompression + 1);
520 ret = m_destLutFunc(ret);
523 if ( m_intScale > 1) {
524 return dither(ret * m_intScale);
530 template <
class VTIn,
class VTOut>
531 typename vigra::NumericTraits<vigra::RGBValue<typename InvResponseTransform<VTIn,VTOut>::VT1> >::RealPromote
534 typename vigra::NumericTraits<vigra::RGBValue<VT1> >::RealPromote ret(v);
535 if (!Base::m_lutR.empty()) {
536 ret = m_lutRInvFunc(v);
542 ret *= m_destExposure/(Base::calcVigFactor(pos)*Base::m_srcExposure);
543 ret.red() /= Base::m_WhiteBalanceRed;
544 ret.blue() /= Base::m_WhiteBalanceBlue;
546 if (!m_destLut.empty()) {
547 if (m_rangeCompression > 0)
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);
553 ret = m_destLutFunc(ret);
556 if (m_intScale > 1) {
557 for (
size_t i=0; i < 3; i++) {
558 ret[i] = dither(ret[i] * m_intScale);
565 template <
class VTIn,
class VTOut>
566 typename vigra::NumericTraits<typename InvResponseTransform<VTIn,VTOut>::dest_type>::RealPromote
569 typedef typename vigra::NumericTraits<VT1>::isScalar is_scalar;
570 return apply(v, pos, is_scalar());
573 template <
class VTIn,
class VTOut>
574 typename vigra::NumericTraits<vigra::RGBValue<typename InvResponseTransform<VTIn,VTOut>::VT1> >::RealPromote
577 typedef typename vigra::NumericTraits<vigra::RGBValue<VT1> >::isScalar is_scalar;
578 return apply(v, pos, is_scalar());
581 template <
class VTIn,
class VTOut>
588 const double invLutSize = Base::m_lutR.size();
590 const double destLutSize = m_destLut.size();
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;
605 oss <<
" p.a = max(p.r, max(p.g, p.b));" << endl;
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;
620 oss <<
" // VigCorrMode=VIGCORR_RADIAL" << endl
621 <<
" float vig = 1.0;" << 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
643 oss <<
" // VigCorrMode=VIGCORR_FLATFIELD" << endl
644 <<
" float vig = 1.0;" << endl;
646 oss <<
" // VigCorrMode=none" << endl
647 <<
" float vig = 1.0;" << endl;
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;
656 if (!m_destLut.empty()) {
657 if (m_rangeCompression > 0)
659 oss <<
" p.rgb = log2(" << m_rangeCompression <<
" * p.rgb + 1.0) / " << log2(m_rangeCompression + 1) <<
";" << endl;
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;
V getMaxComponent(vigra::RGBValue< V > const &v)
get the maximum component of a vector (also works for single pixel types...)
void resizeLUT(const VEC &iLUT, VEC2 &oLUT)
misc math function & classes used by other parts of the program
static const double A(-0.75)
void enforceMonotonicity(LUT &lut)
enforce monotonicity of an array (mostly used for lookup tables)
functions to manage ROI's
functor to apply a LUT to gray and color images.
empirical model of response
radial vignetting correction
void createGammaLUT(double gamma, VECTOR &lut)
void createEMoRLUT(const std::vector< float > ¶ms, VECTOR &lut)
a simple gamma response curve
T1::value_type value_type
All variables of a source image.