Скажем, 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
(не логической, а целочисленной).
Вы можете легко преобразовать этот код 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()
), вы получите более быстрое вычисление.
Ответ Miki очень хороший, но я просто хочу добавить некоторые пояснения к проблеме производительности, чтобы избежать путаницы.
Верно, что лучший способ реализовать фильтр изображений (или любой алгоритм) с OpenCV - использовать необработанные указатели, как показано на втором примере С++ Miki (С++ Loop). Использование функции at также корректно, но значительно медленнее.
Однако большую часть времени вам не нужно беспокоиться об этом, и вы можете просто использовать функции высокого уровня OpenCV (первый пример Miki, С++ Mask). Они хорошо оптимизированы и обычно будут почти такими же быстрыми, как петля низкого уровня на указателях, или даже быстрее.
Конечно, есть исключения (мы только что их нашли), и вы всегда должны проверить свою конкретную проблему.
Теперь, касаясь этой конкретной проблемы:
Пример здесь, где функция высокого уровня была намного медленнее (на 100 раз медленнее), чем петля низкого уровня, не является нормальным случаем, поскольку это демонстрируется таймингами с другой версией/конфигурацией OpenCV, которые намного ниже. Проблема заключается в том, что когда OpenCV3.0 скомпилирован с OpenCL, при первом вызове функции, использующей OpenCL, есть огромные накладные расходы. Самое простое решение - отключить OpenCL во время компиляции, если вы используете OpenCV3.0 (см. Также здесь для других возможных решений, если вам интересно).
cv::Mat mask = A > 0
. Для вашего примера вы можете использоватьB.setTo(cv::Scalar(0), mask)