Reputation: 2445
I am trying to calculate two vectors X
and Y
based on this formula:
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);
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:
The X
and Y
values are supposed to be like this (blue points):
But in my case they are like this:
These are the Mats:
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:
based on the following answer, it was fixed (OpenCV logarithm function takes the log of the abolute values):
Upvotes: 1
Views: 963
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):
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