OpenCV - Как рассчитать логарифм мата? - PullRequest
1 голос
/ 27 февраля 2020

Я пытаюсь вычислить два вектора X и Y на основе этой формулы:

enter image description here enter image description here

enter image description here

где:

  • List item
  • List item

Я решил использовать два Mat s в качестве векторов:

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

Я изменил нулевые значения на единицы из-за этого в documentmentaion :

void log(InputArray src, OutputArray dst);

enter image description here

где C - большое отрицательное число (около -700 в текущая реализация).

Я не хочу больших отрицательных чисел, потому что позже я собираюсь суммировать все значения пикселей X и Y, и сумма станет -nan.

Я уверен, что что-то не так с моим логарифмом, потому что результат неправильный. Как я должен сделать это правильно?


Например, для этого изображения:

enter image description here

The * Значения 1066 * и Y должны быть такими (синие точки):

enter image description here

Но в моем случае они таковы:

enter image description here

Это коврики:

enter image description here


Согласно ответ ниже, я добавил это к коду, чтобы избежать деления нуля:

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

Но теперь мой график все еще выглядит иначе:

enter image description here

на основании следующего ответа это было исправлено (функция логарифма OpenCV берет журнал абсолютных значений):

enter image description here

1 Ответ

1 голос
/ 27 февраля 2020

Сложно сказать, какую именно часть вы делаете неправильно (деление матриц, смена нулей на единицы или вызов журнала на матрицах). В любом случае, я только что реализовал ваш алгоритм, следуя только уравнениям из вашего вопроса, и полученный мной результат более или менее соответствует вашим ожиданиям (используя ваше изображение):

enter image description here

Код тривиален, просто пошаговая оценка уравнений:

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

Графика, созданная с помощью gnuplot. X вектор на оси X, Y вектор на оси Y. X.size() == Y.size() == 11532.

Вероятно, вам следует выяснить, что должно произойти в случае G[i] == 0, поскольку вы можете получить «Исключение с плавающей запятой». Также особый случай, когда числитель и знаменатель равны нулю.

Если вы хотите избежать больших отрицательных журналов, измените только значение числителя (скажем, от 0 до одного). Не меняйте результат деления, потому что он имеет большое значение в случае большого знаменателя.

...