С++ OpenCV использует вектор‹Point› как индекс матрицы

У меня есть матрица img (480*640 пикселей, 64 бита с плавающей запятой), к которой я применяю сложную маску. После этого мне нужно умножить мою матрицу на значение, но чтобы выиграть время, я хочу сделать это умножение только для ненулевых элементов, потому что пока умножение слишком длинное, потому что мне нужно повторить операцию 2000 раз на 2000 другая матрица, но с той же маской. Итак, я нашел индекс (по осям x/y) ненулевых пикселей, которые я храню в векторе Point. Но мне не удается использовать этот вектор для умножения только на пиксели, проиндексированные в этом же векторе.

Вот пример (с простой маской), чтобы понять мою проблему:

Mat img_temp(480, 640, CV_64FC1);
Mat img = img_temp.clone();
Mat mask = Mat::ones(img.size(), CV_8UC1);
double value = 3.56;

// Apply mask
img_temp.copyTo(img, mask);

// Finding non zero elements
vector<Point> nonZero;
findNonZero(img, nonZero);

// Previous multiplication (long because on all pixels)
Mat result = img.clone()*value;

// What I wish to do : multiplication only on non-zero pixels (not functional)
Mat result = Mat::zeros(img.size(), CV_64FC1);
result.at<int>(nonZero) = img.at(nonZero).clone() * value

Что сложно, так это то, что мои пиксели не находятся в диапазоне (например, пиксели 3, 4 и 50, 51 в строке).

Заранее спасибо.


person Mathieu Gauquelin    schedule 23.05.2018    source источник
comment
Как я сказал вам в предыдущем вопросе, который вы задали, вы можете использовать Mat.convertTo, если вам нужно умножить постоянное значение на ненулевые элементы, что будет самым быстрым. Я не понимаю, почему вам нужно найти все пиксели с ненулевыми значениями, поскольку вы в конечном итоге умножаете (умножаете на 0?)   -  person Rick M.    schedule 23.05.2018
comment
Потому что я полагаю, что умножение одного изображения на 90% от 0 медленнее, чем умножение только на 10%, отличное от 0, нет? И для вас convertTo это преобразование значения в матрицу для умножения, я думаю.   -  person Mathieu Gauquelin    schedule 23.05.2018
comment
Потому что я представляю, что умножить... Не гадать. Профилируйте это!   -  person Miki    schedule 23.05.2018
comment
Вы также можете использовать convertTo для умножения, не нужно менять тип матрицы. Я не уверен, что найти все ненулевые координаты, сохранить их в вектор, а затем использовать для умножения будет быстрее, чем просто использовать converTo. Почему бы вам не попробовать оба?   -  person Rick M.    schedule 23.05.2018
comment
Поскольку я новичок в openCV и никогда не использовал convertTo, но я изучу это, спасибо. Я не понял ваш первый ответ извините :(   -  person Mathieu Gauquelin    schedule 23.05.2018


Ответы (3)


Я бы предложил использовать Mat.convertTo. По сути, для параметра alpha, который является коэффициентом масштабирования, используйте значение маски (3,56 в вашем случае). Убедитесь, что мат относится к типу CV_32 или CV_64.

Это будет быстрее, чем находить все ненулевые пиксели, сохранять их координаты в Vector и перебирать (у меня на Java было быстрее).

Надеюсь, поможет!

person Rick M.    schedule 23.05.2018

Построение вектора точек также увеличит время вычислений. Я думаю, вам следует подумать о переборе всех пикселей и умножении, если пиксель не равен нулю.

Итерация будет быстрее, если у вас есть матрица в виде необработанных данных.

person erkut    schedule 23.05.2018
comment
да, но я создаю вектор только один раз и использую его на каждой итерации после. В начале у меня есть адрес памяти первого пикселя, и я использую его для создания массива Mat, поэтому я попытаюсь выполнить итерацию по этому адресу памяти в двух циклах for. - person Mathieu Gauquelin; 23.05.2018
comment
если ваше изображение хранится в массиве 1d, вы можете построить вектор индексов ненулевых пикселей. В вашем случае индекс будет Y * 480 + X, если изображение в градациях серого. Затем вы можете манипулировать пикселями в одном цикле for, напрямую обращаясь к уже известным вам индексам. - person erkut; 23.05.2018
comment
Да, я могу сделать только один цикл for, как вы объяснили, но для лучшего понимания, я думаю, лучше сделать два цикла for. Проблема также в том, что мне нужно преобразовать результат в массив Mat, чтобы выполнить другую операцию после этого, поэтому я должен изучить все эти советы, спасибо. - person Mathieu Gauquelin; 23.05.2018

Если вы сделаете

Mat result = img*value;

Вместо

Mat result = img.clone()*value;

Скорость будет почти в 10 раз быстрее

Я также проверил ваше предложение с вектором, но это даже медленнее, чем ваше первое решение. Ниже кода, который я использовал для проверки вашего первого предложения

cv::Mat multMask(cv::Mat &img, std::vector<cv::Point> mask, double fact)
{
    if (img.type() != CV_64FC1) throw "invalid format";
    cv::Mat res = cv::Mat::zeros(img.size(), img.type());
    int iLen = (int)mask.size();
    for (int i = 0; i < iLen; i++)
    {
        cv::Point &p = mask[i];
        ((double*)(res.data + res.step.p[0] * p.y))[p.x] = ((double*)(img.data + img.step.p[0] * p.y))[p.x] * fact;
    }
    return res;
}
person Benno Geels    schedule 23.05.2018
comment
Пожалуйста, опишите вашу проблему более конкретно, потому что этот простой невинно выглядящий клон имеет разницу в скорости в 10 раз. - person Benno Geels; 23.05.2018
comment
Я не знал, поэтому, когда я писал пример, я не имел в виду это, извините. - person Mathieu Gauquelin; 23.05.2018
comment
Я добавил код вашего предложения, чтобы вы могли проверить его самостоятельно. - person Benno Geels; 23.05.2018
comment
Спасибо. Благодаря вам я обнаружил ступенчатые атрибуты массива Mat ^^ - person Mathieu Gauquelin; 24.05.2018