0

I have a class Texture containing few static variables which would act as look-up tables. These would be used to compute GLCM (Gray-Level Concurrence Matrix) properties. These are kept static because once generated, they could be shared between different objects of the same class irrespective of the number of objects created. Here is my class header.

texture.h

#pragma once
#ifndef TEXTURE_H
#define TEXTURE_H

#include <iostream>
#include <iomanip>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <algorithm>
#include <string>

class Texture
{
public:
    Texture();
    ~Texture();

private:

    const static int MAXIMUM_GLCM = 4; //!< Maximum number of GLCM matrices that would be computed (same as num of directions)

    const static int MAX_LEVELS = 256; //!< Maximum number of levels is assumed to be 256. A valid assumption for 8-bit gray-scale images.

    static cv::Mat lutWeights; //! @brief LUT to generate Contrast, Dissimilarity, Similarity and Homogeneity weights.
    static cv::Mat contrastWeights; //! @brief Container for Contrast and Homogeneity weights.
    static cv::Mat dissimilarWeights; //! @brief Container for Dissimilarity and Similarity weights.

    /*!
     * Generates a 256-element single channel LUT of of type CV_32F. The elements start from 0 and
     * increase linearly.
     * @param[in] None
     * @param[in] None
     * @param[out] None
     */
    static void generateLUT();

    /*!
     * Populates required weight matrices based on the LUT generates as part of generateLUT().
     * The generated weight matrices are single channel and of type CV_32F
     * @param[in] None
     * @param[in] None
     * @param[out] None
     */
    static void prepareWeightMatrices();
};
#endif // !TEXTURE_H

Following is my texture.cpp file.

#include "texture.h"
using namespace std;
using namespace cv; //!< /namespace for OpenCV

cv::Mat Texture::lutWeights = cv::Mat::zeros(cv::Size2i(Texture::MAX_LEVELS,1), CV_32FC1); //! @brief Intialise static LUT with zeros.
cv::Mat Texture::contrastWeights = cv::Mat::zeros(cv::Size2i(Texture::MAX_LEVELS, Texture::MAX_LEVELS), CV_32FC1); //! @brief Intialise static Contrast weights with zeros.
cv::Mat Texture::dissimilarWeights = cv::Mat::zeros(cv::Size2i(Texture::MAX_LEVELS, Texture::MAX_LEVELS), CV_32FC1); //! @brief Intialise static Dissimilarity weights with zeros.

void Texture::generateLUT()
{
    cout << "\ngenerateLUT()";
    for (int i = 0; i < lutWeights.cols; i++)
    {
        lutWeights.at<float>(i) = (float)i; //! Every index is an entry for the LUT.
    }
}

void Texture::prepareWeightMatrices()
{
    cout << "\nprepareWeightMatrices()";
    generateLUT(); //! Generate LUT

    int rowSpan = dissimilarWeights.rows - 1;
    cout << "\n rowSpan: " << rowSpan;
    for (int i = 0; i < rowSpan; i++)
    {
        lutWeights.colRange(1, (rowSpan +1) - i).copyTo(dissimilarWeights.row(i).colRange(i+1, rowSpan +1));
    }

    cv::completeSymm(dissimilarWeights, false); //! Mirror the upper triangle.
    cv::accumulateSquare(dissimilarWeights, contrastWeights); //! Elementwise square to generate contrast weights.
}

Texture::Texture()
{
    prepareWeightMatrices(); //! Generate weights matrices
}

Texture::~Texture()
{
}

Now, the problem is if I declare a global object of the class, my application crashes. See below my main.cpp.

#include "texture.h"
using namespace std;

Texture texture;
int main()
{
    return(0);
}

However, a declaration of the object within main() works without any issues.

#include "texture.h"
using namespace std;

int main()
{
    Texture texture;
    return(0);
}

Debugging of the crash revealed that in the global declaration case, the weight matrices 'lutWeights', 'contrastWeights', and 'dissimilarWeights' are not getting initialised and crash occurs at cv::completeSymm(dissimilarWeights, false) in prepareWeightMatrices() function due to rowSpan coming to be -1. However, with local declaration the initialisation seems to be happening and rowSpan has the correct value of 255.

So, the questions are,

  1. Why the initialisation does not happen when the object is declared globally?
  2. How do I replicate the behaviour of local object declaration with global object declaration?
  3. From design perspective, even if this issue is fixed, should I be going ahead with this class definition or reevaluate my decision?

I am still exploring static member functions and variables of a class. Thanks in advance.

KevalGyan
  • 23
  • 3
  • Does this answer your question? [Static variables initialisation order](https://stackoverflow.com/questions/211237/static-variables-initialisation-order) – Chris Dodd Aug 23 '21 at 05:52

1 Answers1

0

The basic problem is that order of initialization of global statics in different compilation units is not defined (within a compilation unit, they're initialized in order) -- so in your case, the global statics in main.cpp are getting initialized before those in texture.cpp. There's no way to 'set' an order either, it is just implementation defined. The only global order you can rely on is that default initialized static vars of types with no constructor will be zero-initialized before any global that actually requires a constructor call.

So you have a couple of choices

  • put ALL of your global static definitions into one compilation units (in the order you want)
  • carefully code your constructors so that they don't depend on static variables being initialized yet. Here the zero-initialization helps -- you can make your global static vars pointers, and ensure that anyone that wants to use them checks to see if they are nullptr (and initializes them if so).
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226