如何使用cv :: parallel_for_来减少执行时间

时间:2015-04-14 10:52:59

标签: c++ opencv image-processing

我使用OpenCV创建了一个图像处理算法,目前我正在尝试提高我自己的简单函数的时间效率,该函数类似于LUT,但在值(double calibRI::corr(double))之间插值。 我根据OpenCV docs优化了像素循环。

非并行函数(calib(cv::Mat) - calibRI仿函数类的对象)大约需要0.15秒。我决定使用cv::parallel_for_来缩短它。 首先,我将其实现为图像平铺 - 根据>> this文档。时间减少到0.12s(4个线程)。

    virtual void operator()(const cv::Range& range) const
    {
        for(int i = range.start; i < range.end; i++)
        {
            // divide image in 'thr' number of parts and process simultaneously
            cv::Rect roi(0, (img.rows/thr)*i, img.cols, img.rows/thr);
            cv::Mat in = img(roi);
            cv::Mat out = retVal(roi);
            out = calib(in); //loops over all pixels and does out[u,v]=calibRI::corr(in[u,v])
        }

我虽然为子图像/图块/ ROI并行运行我的功能还不是最优的,所以我实现如下:

template <typename T>
class ParallelPixelLoop : public cv::ParallelLoopBody
{
    typedef boost::function<T(T)> pixelProcessingFuntionPtr;
private:
    cv::Mat& image; //source and result image (to be overwritten)
    bool cont; //if the image is continuous
    size_t rows;
    size_t cols;
    size_t threads;
    std::vector<cv::Range> ranges;
    pixelProcessingFuntionPtr pixelProcessingFunction; //pixel modif. function
public:
    ParallelPixelLoop(cv::Mat& img, pixelProcessingFuntionPtr fun, size_t thr = 4)
        : image(img), cont(image.isContinuous()), rows(img.rows), cols(img.cols), pixelProcessingFunction(fun), threads(thr)
    {
        int groupSize = 1;
        if (cont) {
            cols *= rows;
            rows = 1;
            groupSize = ceil( cols / threads );
        }
        else {
            groupSize = ceil( rows / threads );
        }

        int t = 0;
        for(t=0; t<threads-1; ++t) {
            ranges.push_back( cv::Range( t*groupSize, (t+1)*groupSize ) );
        }
        ranges.push_back( cv::Range( t*groupSize, rows<=1?cols:rows ) ); //last range must be to the end of image (ceil used before)
    }

    virtual void operator()(const cv::Range& range) const
    {
        for(int r = range.start; r < range.end; r++)
        {
            T* Ip = nullptr;
            cv::Range ran = ranges.at(r);
            if(cont) {
                Ip = image.ptr<T>(0);
                for (int j = ran.start; j < ran.end; ++j)
                {
                    Ip[j] = pixelProcessingFunction(Ip[j]);
                }
            }
            else {
                for(int i = ran.start; i < ran.end; ++i)
                {
                    Ip = image.ptr<T>(i);
                    for (int j = 0; j < cols; ++j)
                    {
                        Ip[j] = pixelProcessingFunction(Ip[j]);
                    }
                }
            }
        }
    }
};

然后我在1280x1024 64FC1图像,i5处理器,Win8上运行它,并使用以下代码获得 0.4s 范围内的时间:

double t = cv::getTickCount();
ParallelPixelLoop<double> loop(V,boost::bind(&calibRI::corr,this,_1),4);
cv::parallel_for_(cv::Range(0,4),loop);
std::cout << "Exec time: " << (cv::getTickCount()-t)/cv::getTickFrequency() << "s\n";

我不知道为什么我的实现比迭代子图像中的所有像素慢得多......我的代码中是否存在错误或OpenCV ROI以某种特殊方式进行优化? 我不认为存在时间测量错误问题,如here所述。我正在使用OpenCV时间函数。

还有其他方法可以减少此功能的时间吗?

提前致谢!

1 个答案:

答案 0 :(得分:1)

一般来说,很难说为什么使用cv :: parallel_for无法加速整个过程。一种可能性是问题与处理/多线程无关,而与时间测量无关。大约2个月前我试图优化this算法,我注意到奇怪的事情 - 第一次使用它,它需要x ms,但如果使用它使用它第二,第三,...时间(当然没有重新启动应用程序)它需要大约x / 2(或甚至x / 3)ms。我不确定导致这种行为的原因 - 很可能(在我看来)它是分支预测的原因 - 当代码首次执行时,分支预测器“学习”通常采用哪条路径,因此下次它可以预测要采取哪个分支(通常猜测是正确的)。你可以阅读更多关于它的信息here - 这是一个非常好的问题,它可以让你睁开眼睛看一些非常重要的事情。

所以,在你的情况下,我会尝试一些事情:

  • 多次测量 - 100或1000应该足够(如果它需要0.12-0.4s它不会花费太多时间)并查看你的代码的最后版本是否仍然是最慢的。所以只需用以下代码替换代码:

    double t = cv :: getTickCount();
    for(unsigned int i = 0; i&lt; 1000; i ++){
    ParallelPixelLoop循环(V,boost :: bind(&amp; calibRI :: corr,this,_1),4);
    CV :: parallel_for_(CV ::范围(0,4),环);
    }
    std :: cout&lt;&lt; “执行时间:”&lt;&lt; (cv :: getTickCount() - t)/ cv :: getTickFrequency()&lt;&lt; “S \ n” 个;

  • 在更大的图像上测试它。也许在你的情况下,你只需“不需要”4个核心,但在更大的图像上,4个核心将产生积极的差异。

  • 使用分析器(例如Very Sleepy)查看代码的哪一部分是至关重要的