vMF distribution class improvements, python bindings for it

metadata
Wenzel Jakob 2014-02-10 15:24:58 +01:00
parent 006c26891f
commit 4e8015f787
3 changed files with 78 additions and 14 deletions

View File

@ -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.

View File

@ -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

View File

@ -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;
}