OpenCV中就地RGB->BGR颜色转换速度较慢

5
OpenCV中的原位RGB to BGR转换例程是否节省了一些内存,但需要更长的时间?如果是的话,有人可以解释一下吗?
我的应用程序调用了OpenCV(版本4.2.0)中的cv :: cvtColor(srcMat,dstMat,cv :: COLOR_RGB2BGR)例程。为了使应用程序更快,我尝试过这个例程的原位版本(通过使用相同的Mat对象作为源和目标进行调用)。我希望速度稍微提高,因为原位版本不会分配新的内存。
为了测试我的期望,我对10,000个250x250的RGB图像进行了循环测试。令我惊讶的是,在使用原位版本时,我的应用程序变得更慢了。事实上,我发现图像越大(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);
}

谢谢。

你在哪个CPU和操作系统上遇到这个问题?你使用的是什么类型的OpenCV版本?| 这是一个好问题,但回答起来可能会很困难。 - Dan Mašek
1
CPU:Intel(R) Xeon(R) CPU E5-2450 v2 @ 2.50GHz。它有16个超线程核心。我的应用程序运行32个线程,每个(并行)线程处理单个图像。操作系统:Unix。使用ICC编译器优化构建的OpenCV。 - Fijoy Vadakkumpadan
1
我猜这是在某种处理循环中的操作。如果是这样的话,我会使用一个持续存在于迭代过程中的Mat,并将其用作cvtColor的目标,而不是直接在原地进行操作。只要目标具有正确的数据类型和大小,它就不会被重新分配。通常情况下都是如此,额外的内存不太可能成为关键问题。|哦,32个并行,我想在这种情况下它可能更重要一些 :) - Dan Mašek
1
原地处理会由于循环依赖关系阻碍一些编译(和执行)优化。我不能确定这就是解释,但它有可能是其中之一。 - Rotem
1
@DanMašek 我现在尝试了一个跨迭代持续存在的目标矩阵。这个新版本的性能比原地版本(其中srcMat和dstMat相同)要好,但仍然略逊于常规版本。我仍在调查所有这些问题,但到目前为止,似乎不为目标矩阵分配内存并没有提高性能。 - Fijoy Vadakkumpadan
显示剩余3条评论
1个回答

6

你可以查找源代码来查找问题的原因。

有几种可能的代码路径(是否使用OpenCL,是否使用IPP)。 在我的计算机上,cv::cvtColor的执行到达color.hpp中的CvtColorIPPLoopCopy函数:

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需要执行不支持“原地”处理的函数时,解决方案可能是将源图像复制到临时位置。
同样的做法也可以用于其他执行代码路径。

正如我所评论的,
原地处理会由于循环依赖而阻碍一些编译(和执行)优化。
在某些情况下,对于“原地”处理还存在并行化问题。
这就是许多优化的“基本”函数不支持“原地”处理的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接