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>
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
136template <
class VTIn,
class VTOut>
147 typedef std::vector<double>
LUT;
148 typedef std::vector<dest_type>
LUTD;
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
216 template <
class T,
class A>
238 const double f =
static_cast<double>(
i) / (
Base::m_lutR.size() - 1);
266namespace HuginBase {
namespace Photometric {
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();
310 switch (m_src.getResponseType())
331 vigra_fail(
"ResponseTransform: unknown response function type");
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;
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);
370typename 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);
385typename 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);
404typename vigra::NumericTraits<typename ResponseTransform<VTIn>::VT1>::RealPromote
407 typedef typename vigra::NumericTraits<VT1>::isScalar
is_scalar;
412typename vigra::NumericTraits<vigra::RGBValue<typename ResponseTransform<VTIn>::VT1> >::RealPromote
415 typedef typename vigra::NumericTraits<vigra::RGBValue<VT1> >::isScalar
is_scalar;
419template <
class VTIn,
class VTOut>
422 m_destExposure = 1.0;
424 m_rangeCompression = 0.0;
428template <
class VTIn,
class VTOut>
440template <
class VTIn,
class VTOut>
443 m_destExposure = 1.0;
445 m_rangeCompression = 0.0;
447 if (!Base::m_lutR.
empty()) {
453template <
class VTIn,
class VTOut>
460 m_rangeCompression = 0.0;
463template <
class VTIn,
class VTOut>
468 if (!m_destLut.empty()) {
474 m_rangeCompression = 0.0;
481template <
class VTIn,
class VTOut>
484 std::mt19937 &mt =
const_cast<std::mt19937 &
>(Twister);
501template <
class VTIn,
class VTOut>
502typename 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);
523 if ( m_intScale > 1) {
524 return dither(
ret * m_intScale);
530template <
class VTIn,
class VTOut>
531typename 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);
556 if (m_intScale > 1) {
557 for (
size_t i=0;
i < 3;
i++) {
558 ret[
i] = dither(
ret[
i] * m_intScale);
565template <
class VTIn,
class VTOut>
566typename vigra::NumericTraits<typename InvResponseTransform<VTIn,VTOut>::dest_type>::RealPromote
569 typedef typename vigra::NumericTraits<VT1>::isScalar
is_scalar;
573template <
class VTIn,
class VTOut>
574typename vigra::NumericTraits<vigra::RGBValue<typename InvResponseTransform<VTIn,VTOut>::VT1> >::RealPromote
577 typedef typename vigra::NumericTraits<vigra::RGBValue<VT1> >::isScalar
is_scalar;
581template <
class VTIn,
class VTOut>
588 const double invLutSize = Base::m_lutR.size();
593 <<
" // pixelMax = " <<
pixelMax << 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;
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;
@ 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
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...
void createEMoRLUT(const std::vector< float > ¶ms, VECTOR &lut)
void resizeLUT(const VEC &iLUT, VEC2 &oLUT)
V getMaxComponent(vigra::RGBValue< V > const &v)
get the maximum component of a vector (also works for single pixel types...)
void createGammaLUT(double gamma, VECTOR &lut)
void enforceMonotonicity(LUT &lut)
enforce monotonicity of an array (mostly used for lookup tables)
functor to apply a LUT to gray and color images.
functor to apply a LUT to gray and color images.
T1::value_type value_type
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.
functions to manage ROI's