Question

I think this is more of a philosophical question about readability and tupled types in C++11.

I am writing some code to produce Gaussian Mixture Models (the details are kind of irrelevant but it serves and a nice example.) My code is below:

GMM.hpp

#pragma once
#include <opencv2/opencv.hpp>
#include <vector>
#include <tuple>
#include "../Util/Types.hpp"

namespace LocalDescriptorAndBagOfFeature 
{   
    // Weighted gaussian is defined as a (weight, mean vector, covariance matrix)
    typedef std::tuple<double, cv::Mat, cv::Mat> WeightedGaussian; 

    class GMM
    {
        public:
            GMM(int numGaussians);

            void Train(const FeatureSet &featureSet);
            std::vector<double> Supervector(const BagOfFeatures &bof);

            int NumGaussians(void) const;

            double operator ()(const cv::Mat &x) const;

        private:                        
            static double ComputeWeightedGaussian(const cv::Mat &x, WeightedGaussian wg);

            std::vector<WeightedGaussian> _Gaussians;

            int _NumGaussians;
    };
}

GMM.cpp

using namespace LocalDescriptorAndBagOfFeature;

double GMM::ComputeWeightedGaussian(const cv::Mat &x, WeightedGaussian wg)
{
    double weight;
    cv::Mat mean, covariance;
    std::tie(weight, mean, covariance) = wg;

    cv::Mat precision;
    cv::invert(covariance, precision);

    double detp = cv::determinant(precision);

    double outter = std::sqrt(detp / 2.0 * M_PI);

    cv::Mat meanDist = x - mean;
    cv::Mat meanDistTrans;
    cv::transpose(meanDist, meanDistTrans);

    cv::Mat symmetricProduct = meanDistTrans * precision * meanDist; // This is a "1x1" matrix e.g. a scalar value
    double inner = symmetricProduct.at<double>(0,0) / -2.0;

    return weight * outter * std::exp(inner);
}

double GMM::operator ()(const cv::Mat &x) const
{
    return std::accumulate(_Gaussians.begin(), _Gaussians.end(), 0, [&x](double val, WeightedGaussian wg) { return val + ComputeWeightedGaussian(x, wg); });
}

In this case, am I gaining anything (clarity, readability, speed, ...) by using a tuple representation for the weighted Gaussian distribution over using a struct, or even a class with its own operator()?

Was it helpful?

Solution

You're reducing the size of your source code a little bit, but I'd argue that you're reducing its overall readability and type safety. Specifically, if you defined:

struct WeightedGaussian {
  double weight;
  cv::Mat mean, covariance;
};

then you wouldn't have a chance of writing the incorrect

std::tie(weight, covariance, mean) = wg;

and you'd guarantee that your users would use wg.mean instead of std::get<0>(wg). The biggest downside is that std::tuple comes with definitions of operator< and operator==, while you have to implement them yourself for a custom struct:

operator<(const WeightedGaussian& lhs, const WeightedGaussian& rhs) {
  return std::tie(lhs.weight, lhs.mean, lhs.covariance) <
         std::tie(rhs.weight, rhs.mean, rhs.covariance);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top