在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);

但是这两种方式不能同时声明定义,因为这会出现二义性。造成程序不知道该执行那个函数。在进行运算符重载的时候千万要注意造成二义性的情况。