Лучший способ индексации матрицы в opencv

5

Скажем, A и B являются матрицами одинакового размера. В Matlab я мог бы использовать простую индексацию, как показано ниже.

idx = A>0;
B(idx) = 0

Как это сделать в OpenCV? Должен ли я просто использовать

for (i=0; ... rows)
    for(j=0; ... cols)
        if (A.at<double>(i,j)>0) B.at<double>(i,j) = 0;

что-то вроде этого? Есть ли лучший (более быстрый и эффективный) способ?

Кроме того, в OpenCV, когда я пытаюсь

Mat idx = A>0;

переменная idx представляется матрицей CV_8U (не логической, а целочисленной).

  • 1
    что вы подразумеваете под "лучшим"? самый быстрый? проще всего кодировать? лучше всего поддерживать? многие функции используют «маски», которые представляют собой 8-битные входные изображения, которые сообщают, является ли пиксель «активным» (ненулевым) или «неактивным» (нулевым). Из изображения в градациях серого вы можете легко создать маску, используя, например, cv::Mat mask = A > 0 . Для вашего примера вы можете использовать B.setTo(cv::Scalar(0), mask)
  • 1
    Спасибо, Мика. Я имел в виду «быстрее и эффективнее» под «лучше». Я отредактировал. Поскольку я являюсь своего рода парнем из Matlab, я просто колебался, используя вложенные циклы, и думал о другом способе сделать это, используя некоторые встроенные функции, разработанные для более быстрой реализации.
Показать ещё 2 комментария
Теги:
opencv

2 ответа

5
Лучший ответ

Вы можете легко преобразовать этот код MATLAB:

idx = A > 0;
B(idx) = 0;

// same as 

B(A>0) = 0;

в OpenCV как:

Mat1d A(...)
Mat1d B(...)

Mat1b idx = A > 0;
B.setTo(0, idx) = 0;

// or

B.setTo(0, A > 0);

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

for (int r = 0; r < B.rows; ++r)
{
    double* pA = A.ptr<double>(r);
    double* pB = B.ptr<double>(r);
    for (int c = 0; c < B.cols; ++c)
    {
        if (pA[c] > 0.0) pB[c] = 0.0;
    }
}

Также обратите внимание, что в OpenCV нет никакой логической матрицы, но это a CV_8UC1 matrix (aka одноканальная матрица unsigned char), где 0 означает false, а любое значение >0 (обычно 255).

Оценка

Обратите внимание, что это может варьироваться в соответствии с оптимизацией, включенной в OpenCV. Вы можете проверить код ниже на своем ПК, чтобы получить точные результаты.

Время в мс:

          my results           my results      @AdrienDescamps
          (OpenCV 3.0 No IPP)  (OpenCV 2.4.9)

Matlab  : 13.473     
C++ Mask: 640.824              5.81815         ~5
C++ Loop: 5.24414              4.95127         ~4

Примечание. Я не совсем уверен в снижении производительности с помощью OpenCV 3.0, поэтому я просто замечаю: проверить код ниже на вашем ПК, чтобы получить точные результаты.

Как @AdrienDescamps в комментариях:

Похоже, что падение производительности с OpenCV 3.0 связано с опцией OpenCL, которая теперь включена в операторе сравнения.

Код С++

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
    // Random initialize A with values in [-100, 100]
    Mat1d A(1000, 1000);
    randu(A, Scalar(-100), Scalar(100));

    // B initialized with some constant (5) value
    Mat1d B(A.rows, A.cols, 5.0); 

    // Operation: B(A>0) = 0;

    {
        // Using mask

        double tic = double(getTickCount());
        B.setTo(0, A > 0);
        double toc = (double(getTickCount()) - tic) * 1000 / getTickFrequency();

        cout << "Mask: " << toc << endl;
    }
    {
        // Using for loop

        double tic = double(getTickCount());
        for (int r = 0; r < B.rows; ++r)
        {
            double* pA = A.ptr<double>(r);
            double* pB = B.ptr<double>(r);
            for (int c = 0; c < B.cols; ++c)
            {
                if (pA[c] > 0.0) pB[c] = 0.0;
            }
        }
        double toc = (double(getTickCount()) - tic) * 1000 / getTickFrequency();

        cout << "Loop: " << toc << endl;
    }


    getchar();
    return 0;
}

Код Matlab

% Random initialize A with values in [-100, 100]
A = (rand(1000) * 200) - 100;
% B initialized with some constant (5) value
B = ones(1000) * 5; 

tic
B(A>0) = 0; 
toc

UPDATE

OpenCV 3.0 использует оптимизацию IPP в функции setTo. Если у вас есть это (вы можете проверить с помощью cv::getBuildInformation()), вы получите более быстрое вычисление.

  • 1
    Вы уверены во времени маски с ++? Я получаю 5 мс с opencv (2.4), скомпилированным в режиме Release, и 9 мс в Debug, для времени цикла c ++ 4 мс.
  • 0
    @AdrienDescamps - это результаты, которые я получаю, но у меня не включена активная оптимизация. Как я уже говорил, результаты могут отличаться (теперь выделены жирным шрифтом). Я отредактировал ваши результаты в ответ. Спасибо за ответ!
Показать ещё 3 комментария
3

Ответ Miki очень хороший, но я просто хочу добавить некоторые пояснения к проблеме производительности, чтобы избежать путаницы.

Верно, что лучший способ реализовать фильтр изображений (или любой алгоритм) с OpenCV - использовать необработанные указатели, как показано на втором примере С++ Miki (С++ Loop). Использование функции at также корректно, но значительно медленнее.

Однако большую часть времени вам не нужно беспокоиться об этом, и вы можете просто использовать функции высокого уровня OpenCV (первый пример Miki, С++ Mask). Они хорошо оптимизированы и обычно будут почти такими же быстрыми, как петля низкого уровня на указателях, или даже быстрее.

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

Теперь, касаясь этой конкретной проблемы:

Пример здесь, где функция высокого уровня была намного медленнее (на 100 раз медленнее), чем петля низкого уровня, не является нормальным случаем, поскольку это демонстрируется таймингами с другой версией/конфигурацией OpenCV, которые намного ниже. Проблема заключается в том, что когда OpenCV3.0 скомпилирован с OpenCL, при первом вызове функции, использующей OpenCL, есть огромные накладные расходы. Самое простое решение - отключить OpenCL во время компиляции, если вы используете OpenCV3.0 (см. Также здесь для других возможных решений, если вам интересно).

  • 0
    действительно полезная дискуссия

Ещё вопросы

Сообщество Overcoder
Наверх
Меню