Hadi GhahremanNezhad
Hadi GhahremanNezhad

Reputation: 2445

OpenCV - How to claculate logarithm of a Mat?

I am trying to calculate two vectors X and Y based on this formula:

enter image description here enter image description here

enter image description here

where:

I have decided to use two Mats as the vectors:

    Mat X, Y, B, G, R, input_f;

    // read RGB image
    Mat input = imread("an RGB image");

    // convert to float to deal with larger numbers
    input.convertTo(input_f, CV_64FC3);

    // split the image into 3 channels
    vector<Mat> channels(3);
    split(input_f, channels);
    B = channels[0];
    G = channels[1];
    R = channels[2];

    // calculate X , Y vectors
    Mat div_x, div_y;
    divide(R, G, div_x); // R(p_i) / G(p_i)
    divide(B, G, div_y); // B(p_i) / G(p_i)
    div_x.setTo(1, Mat(div_x == 0)); // set zeros to ones to avoid large negative numbers
    div_y.setTo(1, Mat(div_y == 0));
    log(div_x, X); // X = log(R(p_i) / G(p_i))
    log(div_y, Y); // Y = log(B(p_i) / G(p_i))

I changed the zero values to ones because of this in the documentaion:

void log(InputArray src, OutputArray dst);

enter image description here

where C is a large negative number (about -700 in the current implementation).

I don't want the large negative numbers because later I am going to sum up all the pixel values of X and Y and the sum becomes -nan.

I am sure something is wrong with the way I am taking the logarithm because the result is wrong. How am I supposed to do this the right way?


For example, for this image:

enter image description here

The X and Y values are supposed to be like this (blue points):

enter image description here

But in my case they are like this:

enter image description here

These are the Mats:

enter image description here


According to the answer below, I added this to the code to avoid zero divisions:

R += 1;
B += 1;
G += 1;

But now my plot still looks different:

enter image description here

based on the following answer, it was fixed (OpenCV logarithm function takes the log of the abolute values):

enter image description here

Upvotes: 1

Views: 963

Answers (1)

pptaszni
pptaszni

Reputation: 8228

Hard to tell which part exactly are you doing wrong (matrix division, changing zeroes to ones, or invoking log on matrices). Anyway, I just implemented your algorithm following only the equations from your question and the result I obtained is more or less what you expected (using your image):

enter image description here

The code is trivial, just step by step equations evaluation:

  cv::Mat bgr[3];
  cv::Mat pic = cv::imread(PATH_TO_FILE);
  cv::split(pic, bgr);
  cv::Mat B = bgr[0];
  cv::Mat G = bgr[1];
  cv::Mat R = bgr[2];
  std::vector<float> X;
  std::vector<float> Y;
  std::transform(R.data, R.data + R.total(), G.data, std::back_inserter(X),
    [](uint8_t r, uint8_t g)
    {
      return std::log((float)r/(float)g);
    });
  std::transform(B.data, B.data + B.total(), G.data, std::back_inserter(Y),
    [](uint8_t b, uint8_t g)
    {
      return std::log((float)b/(float)g);
    });

Graphics created with gnuplot. X vector on X axis, Y vector on Y axis. X.size() == Y.size() == 11532.

You should probably investigate what should happen in case G[i] == 0, because you might get "Floating point exception". Also the special case when both numerator and denominator are zeroes.

If you want to avoid large negative logs, change only the value of numerator (from 0 to one let's say). Don't change the division result, because it makes already a big difference in case of big denominator.

Upvotes: 1

Related Questions