在OpenCV 中,并行框架按照以下顺序提供:
- 英特尔线程构建块(第三方库,应显式启用),如TBB
- C =并行C / C ++编程语言扩展(第三方库,应明确启用)
- OpenMP
- APPLE GCD
- Windows RT
- Windows并发
- Pthreads
OpenCV库中可以使用多个并行框架。一些并行库是第三方库,必须在CMake(例如TBB,C =)中进行显式构建和启用,其他可以自动与平台(例如APPLE GCD)一起使用,但是您应该可以使用这些库来访问并行框架直接或通过启用CMake中的选项并重建库。
在使用opencv的过程中,对图片的处理计算量还是很大的,所以在实施运行的程序中如何高效的计算会节省很多时间。现有的方法有很多,如OpenMp,TBB,OpenCL,当然还有Nvidia的CUDA。但是OpenMP在windows的VS上支持的很好,设置简单,效果也还不错,但是在Linux虽然也支持,
但是我用Cmake时,感觉效果并不明显。TBB和OpenCL没有直接用过。CUDA是个好东西,但是并不太适合毫秒级别的程序运行,单一张图片在cpu和gpu之间的传输时间就已经达到300ms(我用的是opencv的cuda库函数);在TX2上直接对cuda进行编程,数据的传输也是在50ms(不包含初始化)以上,根本不能拿来做实时的运算。所以如何在cpu上更加高效的计算变得尤为重要。偶然间发现了opencv的并行计算函数parallel_for_,它整合了上述的多个组件。
步骤:
1、声明ParallelLoopBody 子类(取名为FillParallel),并实现其纯虚函数 virtual void operator() (const Range& range) const的功能;
2、实例化子类(FillParallel)的对象(取名为 fill) ;
3、调用 cv::parallel_for_()方式进行并行计算。
#include <iostream>
#include <opencv2/core.hpp>
class MyParallelClass : public cv::ParallelLoopBody
{
};
struct MyParallelStruct : public cv::ParallelLoopBody
{
};
class ParallelAdd : public cv::ParallelLoopBody
{
public:
ParallelAdd(const cv::Mat& _src1, const cv::Mat& _src2, float* _src3)
{
src1 = _src1;
src2 = _src2;
src3 = _src3;
CV_Assert((src1.rows == src2.rows) && (src1.cols == src2.cols));
rows = src1.rows;
cols = src1.cols;
}
void operator()(const cv::Range& range) const
{
size_t step = src1.step1();
for (int row = range.start; row < range.end; ++row)
{
for (int col = 0; col < src1.cols; ++col)
{
const float* p1 = src1.ptr<float>(row, col);
const float* p2 = src2.ptr<float>(row, col);
src3[row * step + col] = (*p1) * (*p2);
}
}
}
private:
cv::Mat src1;
cv::Mat src2;
float* src3;
int rows;
int cols;
};
int main()
{
cv::Mat m1(cv::Mat::ones(5000, 5000, CV_32F));
cv::Mat m2(cv::Mat::ones(5000, 5000, CV_32F));
int64 t1 = cv::getTickCount();
//直接调用
cv::Mat r3(m1.rows, m1.cols, m1.type());
cv::Ptr<ParallelAdd> add= cv::makePtr<ParallelAdd>(m1, m2, (float*)r3.data);
(*add)(cv::Range(0, m1.rows)); //直接调用,没有并发
//add->operator()(cv::Range(0, m1.cols));
int64 t2 = cv::getTickCount();
std::cout << "time: " << (t2 - t1)/cv::getTickFrequency() * 1000 << " ms" << std::endl;
cv::Mat r4(m1.size(), m1.type());
cv::parallel_for_(cv::Range(0, m1.rows), ParallelAdd(m1, m2, (float*)r3.data));//隐式调用,并发
int64 t3 = cv::getTickCount();
std::cout << "parallel time: " << (t3 - t2) / cv::getTickFrequency() * 1000 << " ms" << std::endl;
int64 t4 = cv::getTickCount();
cv::Mat r5 = m1.mul(m2);
std::cout << "mat mul: " << (t4 - t3) / cv::getTickFrequency() * 1000 << " ms" << std::endl;
return 1;
}
time: 1074.81 ms
parallel time: 160.62 ms
mat mul: 0.775 ms
总结:使用 parallel_for_ 并发的方式的确比直接调用快一些,但是没有使用 OpenCV 自带的标准函数 mul 函数速度快,因为,OpenCV 实现的函数库不仅仅经过了并行处理,还是用了更强大的底层优化,所以,只要是 OpenCV 一般都是优先使用,除非自己写的比 OpenCV 的还牛逼一些。
最重要的注意点:
T1 = T2 + T3;
可以为T1 = T2.operator+(T3);
也可以为T1 = operator+(T2,T3);
但是这两种方式不能同时声明定义,因为这会出现二义性。造成程序不知道该执行那个函数。在进行运算符重载的时候千万要注意造成二义性的情况。