Преобразование цветов RGB->BGR на месте медленнее в OpenCV

Дело в том, что процедура преобразования цветов RGB->BGR на месте в OpenCV экономит память, но занимает больше времени? Если да, то может ли кто-нибудь объяснить, почему?

Мое приложение вызывает процедуру cv::cvtColor(srcMat, dstMat, cv::COLOR_RGB2BGR) в OpenCV (версия 4.2.0). Чтобы сделать приложение быстрее, я попробовал версию этой подпрограммы на месте (вызвав ее с одним и тем же объектом Mat для источника и назначения). Я ожидал, что скорость немного улучшится, так как версия на месте не выделяет новую память.

Чтобы проверить свои ожидания, я запустил свое приложение в цикле на 10 000 изображений RGB 250x250. К моему удивлению, мое приложение стало медленнее, когда использовалась версия на месте. На самом деле я увидел, что чем больше изображение (500x500 против 250x250), тем больше разница между на месте и обычной версией.

Ожидается ли это? Если да, то это потому, что версия на месте выполняет операцию подкачки (больше инструкций), а обычная версия — только операцию копирования?

Кто-нибудь захочет попытаться воспроизвести это поведение? Это можно легко сделать, рассчитывая следующий фрагмент двумя разными способами: 1) используйте приведенный ниже фрагмент и 2) следуйте кратким инструкциям в комментариях во фрагменте для версии на месте.

// Read image
Mat srcMat = imread(filename);

// Comment out this line for the in-place version
Mat dstMat;

for (int i=0; i<10000; i++)
{
  // Use srcMat instead of dstMat in the in-place version
  cv::cvtColor(srcMat, dstMat, cv::COLOR_RGB2BGR);
}

Спасибо.


person Fijoy Vadakkumpadan    schedule 15.05.2021    source источник
comment
На каком процессоре вы это испытываете и, возможно, даже на ОС? Какую сборку OpenCV вы используете? | Хороший вопрос, но на него будет довольно сложно ответить.   -  person Dan Mašek    schedule 15.05.2021
comment
ЦП: ЦП Intel(R) Xeon(R) E5-2450 v2 @ 2,50 ГГц. Он имеет 16 ядер с технологией Hyper-Threading. Мое приложение запускает 32 потока, каждый (параллельный) поток обрабатывает одно изображение. ОС: Юникс. Оптимизированная сборка OpenCV с компилятором ICC.   -  person Fijoy Vadakkumpadan    schedule 15.05.2021
comment
Я предполагаю, что это операция в каком-то цикле обработки. В этом случае вместо того, чтобы делать это на месте, я бы просто использовал Mat, который сохраняется на протяжении итераций, и использовал его для назначения cvtColor. Пока место назначения имеет правильный тип данных и размер, оно не будет перераспределено. Как правило, это так, и лишняя память, скорее всего, не критична. | О, 32 из них параллельно, я думаю, это может иметь большее значение в этом сценарии :)   -  person Dan Mašek    schedule 15.05.2021
comment
Есть ли способ попробовать это с самой последней версией OpenCV? Возможно, отредактируйте вопрос с коротким минимально воспроизводимый пример, который позволит другим легко воспроизвести его. И я бы посоветовал создать задачу в системе отслеживания проблем opencv на github, если вы еще этого не сделали.   -  person Dan Mašek    schedule 15.05.2021
comment
Я могу просмотреть самую последнюю версию, но потребуется немало усилий, чтобы скомпилировать мое приложение с другой версией OpenCV. Отредактировал вопрос, чтобы добавить больше деталей для воспроизведения. Спасибо!   -  person Fijoy Vadakkumpadan    schedule 15.05.2021
comment
Обработка на месте предотвращает некоторые оптимизации компиляции (и выполнения) из-за циклических зависимостей. Я не могу сказать, что это объяснение здесь, но оно может быть.   -  person Rotem    schedule 15.05.2021
comment
Я думаю, что версия на месте может выделять новую память для каждого вызова функции, в то время как версия src-dst выделяет память только один раз, если вы сохраняете матричный буфер dst (и размер изображений не изменяется). Это очевидно для таких вещей, как gaussianBlur и т. д., но может произойти и в cvtColor.   -  person Micka    schedule 16.05.2021
comment
@DanMašek Вслед за этим я попробовал целевую матрицу, которая сохраняется на протяжении итераций. Производительность этой новой версии лучше, чем у встроенной версии (где srcMat и dstMat одинаковы), но все же немного хуже, чем у обычной версии. Я все еще изучаю все это, но пока кажется, что отсутствие выделения памяти для целевой матрицы не улучшает производительность.   -  person Fijoy Vadakkumpadan    schedule 16.05.2021


Ответы (1)


Вы можете копаться в источниках, чтобы найти причину.

Существует несколько возможных путей кода (используя OpenCL или нет, используя IPP или нет).
На моей машине выполнение cv::cvtColor достигает функции CvtColorIPPLoopCopy в color.hpp:

template <typename Cvt>
bool CvtColorIPPLoopCopy(const uchar * src_data, size_t src_step, int src_type, uchar * dst_data, size_t dst_step, int width, int height, const Cvt& cvt)
{
    Mat temp;
    Mat src(Size(width, height), src_type, const_cast<uchar*>(src_data), src_step);
    Mat source = src;
    if( src_data == dst_data )
    {
        src.copyTo(temp);
        source = temp;
    }
    bool ok;
    parallel_for_(Range(0, source.rows),
                  CvtColorIPPLoop_Invoker<Cvt>(source.data, source.step, dst_data, dst_step,
                                               source.cols, cvt, &ok),
                  source.total()/(double)(1<<16) );
    return ok;
}

Код проверяет, если src_data == dst_data, и, если он равен, копирует исходное изображение во временное:

if( src_data == dst_data )
{
    src.copyTo(temp);
    source = temp;
}

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


Примечание.
Я не могу точно сказать, что причина именно в этом, потому что возможны и другие пути кода.
Существует множество высокоэффективных оптимизированных функций, которые не поддерживают обработку на месте.
Когда OpenCV необходимо выполнить функцию, которая не поддерживает обработку на месте, решением может быть копирование исходного изображения во временное расположение.
Та же практика может использоваться для других путей выполнения кода.

Как я уже говорил,
обработка на месте предотвращает некоторые оптимизации компиляции (и выполнения) из-за зависимостей, переносимых циклом.
В некоторых случаях также возникают проблемы с распараллеливанием обработки на месте.
причина, по которой многие оптимизированные примитивные функции не поддерживают обработку на месте.

person Rotem    schedule 15.05.2021