我使用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时间函数。
还有其他方法可以减少此功能的时间吗?
提前致谢!
答案 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)查看代码的哪一部分是至关重要的