vMF distribution class improvements, python bindings for it
parent
006c26891f
commit
4e8015f787
|
@ -28,7 +28,9 @@ MTS_NAMESPACE_BEGIN
|
|||
* \brief Von Mises-Fisher distribution on the 2-sphere
|
||||
*
|
||||
* This is a basic implementation, which assumes that the
|
||||
* distribution is centered around the Z-axis.
|
||||
* distribution is centered around the Z-axis. All provided
|
||||
* functions are implemented in such a way that they avoid
|
||||
* issues with numerical overflow.
|
||||
*
|
||||
* \author Wenzel Jakob
|
||||
* \ingroup libcore
|
||||
|
@ -39,13 +41,21 @@ public:
|
|||
* \brief Create a new von Mises-Fisher distribution
|
||||
* with the given concentration parameter
|
||||
*/
|
||||
VonMisesFisherDistr(Float kappa = 0);
|
||||
explicit inline VonMisesFisherDistr(Float kappa = 0) : m_kappa(kappa) { }
|
||||
|
||||
/// Return the concentration parameter kappa
|
||||
inline void setKappa(Float kappa) {
|
||||
m_kappa = kappa;
|
||||
}
|
||||
|
||||
/// Return the concentration parameter kappa
|
||||
inline Float getKappa() const {
|
||||
return m_kappa;
|
||||
}
|
||||
|
||||
/// Return the mean cosine of the distribution
|
||||
Float getMeanCosine() const;
|
||||
|
||||
/// Evaluate the distribution for a given value of cos(theta)
|
||||
Float eval(Float cosTheta) const;
|
||||
|
||||
|
@ -57,12 +67,21 @@ public:
|
|||
*/
|
||||
Vector sample(const Point2 &sample) const;
|
||||
|
||||
/// Return a string representation
|
||||
std::string toString() const;
|
||||
|
||||
/**
|
||||
* \brief Compute an appropriate concentration parameter so that
|
||||
* the associated vMF distribution takes on the value \c x at its peak
|
||||
*/
|
||||
static Float forPeakValue(Float x);
|
||||
|
||||
/**
|
||||
* \brief Compute an appropriate concentration parameter so that
|
||||
* the associated vMF distribution has the mean cosine \c g.
|
||||
*/
|
||||
static Float forMeanCosine(Float g);
|
||||
|
||||
/**
|
||||
* \brief Compute an concentration parameter that approximately
|
||||
* corresponds to the spherical convolution of two vMF distributions.
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
|
||||
#include <mitsuba/core/vmf.h>
|
||||
#include <mitsuba/core/warp.h>
|
||||
#include <mitsuba/core/brent.h>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
VonMisesFisherDistr::VonMisesFisherDistr(Float kappa): m_kappa(kappa) { }
|
||||
|
||||
Float VonMisesFisherDistr::eval(Float cosTheta) const {
|
||||
if (m_kappa == 0.0f)
|
||||
return INV_FOURPI;
|
||||
|
@ -63,22 +63,23 @@ Vector VonMisesFisherDistr::sample(const Point2 &sample) const {
|
|||
sinPhi * sinTheta, cosTheta);
|
||||
}
|
||||
|
||||
Float VonMisesFisherDistr::forPeakValue(Float x) {
|
||||
if (x < INV_FOURPI) {
|
||||
return 0.0f;
|
||||
} else if (x > 0.795) {
|
||||
return 2 * M_PI * x;
|
||||
} else {
|
||||
return std::max((Float) 0.0f,
|
||||
(168.479f * x * x + 16.4585f * x - 2.39942f) /
|
||||
(-1.12718f * x * x + 29.1433f * x + 1.0f));
|
||||
}
|
||||
Float VonMisesFisherDistr::getMeanCosine() const {
|
||||
if (m_kappa == 0)
|
||||
return 0;
|
||||
Float coth = m_kappa > 6 ? 1 : ((std::exp(2*m_kappa)+1)/(std::exp(2*m_kappa)-1));
|
||||
return coth-1/m_kappa;
|
||||
}
|
||||
|
||||
static Float A3(Float kappa) {
|
||||
return 1/ std::tanh(kappa) - 1 / kappa;
|
||||
}
|
||||
|
||||
std::string VonMisesFisherDistr::toString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "VonMisesFisherDistr[kappa=" << m_kappa << "]";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static Float dA3(Float kappa) {
|
||||
Float csch = 2.0f /
|
||||
(math::fastexp(kappa)-math::fastexp(-kappa));
|
||||
|
@ -110,4 +111,33 @@ Float VonMisesFisherDistr::convolve(Float kappa1, Float kappa2) {
|
|||
return A3inv(A3(kappa1) * A3(kappa2), std::min(kappa1, kappa2));
|
||||
}
|
||||
|
||||
Float VonMisesFisherDistr::forPeakValue(Float x) {
|
||||
if (x < INV_FOURPI) {
|
||||
return 0.0f;
|
||||
} else if (x > 0.795) {
|
||||
return 2 * M_PI * x;
|
||||
} else {
|
||||
return std::max((Float) 0.0f,
|
||||
(168.479f * x * x + 16.4585f * x - 2.39942f) /
|
||||
(-1.12718f * x * x + 29.1433f * x + 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
static Float meanCosineFunctor(Float kappa, Float g) {
|
||||
return VonMisesFisherDistr(kappa).getMeanCosine()-g;
|
||||
}
|
||||
|
||||
Float VonMisesFisherDistr::forMeanCosine(Float g) {
|
||||
if (g == 0)
|
||||
return 0;
|
||||
else if (g < 0)
|
||||
SLog(EError, "Error: vMF distribution cannot be created for g<0.");
|
||||
|
||||
BrentSolver brentSolver(100, 1e-6f);
|
||||
BrentSolver::Result result = brentSolver.solve(
|
||||
boost::bind(&meanCosineFunctor, _1, g), 0, 1000);
|
||||
SAssert(result.success);
|
||||
return result.x;
|
||||
}
|
||||
|
||||
MTS_NAMESPACE_END
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <mitsuba/core/mstream.h>
|
||||
#include <mitsuba/core/cstream.h>
|
||||
#include <mitsuba/core/qmc.h>
|
||||
#include <mitsuba/core/vmf.h>
|
||||
#include <mitsuba/core/shvector.h>
|
||||
#include <mitsuba/core/sshstream.h>
|
||||
#include <mitsuba/render/scenehandler.h>
|
||||
|
@ -2251,6 +2252,20 @@ void export_core() {
|
|||
.def("getMillisecondsSinceStart", &Timer::getMillisecondsSinceStart)
|
||||
.def("getSecondsSinceStart", &Timer::getSecondsSinceStart);
|
||||
|
||||
BP_STRUCT(VonMisesFisherDistr, bp::init<Float>())
|
||||
.def("getKappa", &VonMisesFisherDistr::getKappa)
|
||||
.def("setKappa", &VonMisesFisherDistr::setKappa)
|
||||
.def("eval", &VonMisesFisherDistr::eval)
|
||||
.def("getMeanCosine", &VonMisesFisherDistr::getMeanCosine)
|
||||
.def("sample", &VonMisesFisherDistr::sample, BP_RETURN_VALUE)
|
||||
.def("forMeanCosine", &VonMisesFisherDistr::forMeanCosine)
|
||||
.def("forPeakValue", &VonMisesFisherDistr::forPeakValue)
|
||||
.def("convolve", &VonMisesFisherDistr::convolve)
|
||||
.def("__repr__", &VonMisesFisherDistr::toString)
|
||||
.staticmethod("forMeanCosine")
|
||||
.staticmethod("forPeakValue")
|
||||
.staticmethod("convolve");
|
||||
|
||||
bp::detail::current_scope = oldScope;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue