如何为 OpenCV 多核图像处理创建 TBB 任务调度程序? C++
How to create TBB Task Scheduler for OpenCV multicore image processing? C++
我正在学习使用 OpenCV 和 TBB。我需要学习如何使用图像的多处理,因为我有多核 CPU 并且想为我的程序创建 muticpu 支持。
我已阅读英特尔® 技术期刊论文中的一篇文章 "The Foundations for Scalable Multi-core Software in Intel® Threading Building Blocks"(您可以在此处的 pdf 中找到它 http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.71.8289&rep=rep1&type=pdf)
他们使用法波纳奇数计算作为多处理的例子。在 TBB 包中的 TBB 示例中也有类似的 fabonacci number 示例(请参阅 ParallelTaskFib)。唯一的问题是计算非常简单,CPU 负担不大,因此当您 运行 对小数字进行多任务处理时,低 CutOff 效率不高,因为它需要太多开销。因此,要学习使用 TBB,我需要更多来自图像处理的实际示例。在我的概念中,我想使用 TBB Task Scheduler。我从 class FibTask 和我重命名的函数 ParallelFib 开始,更改了参数以处理图像向量。它的设计基本原则应该保持不变。 fabonacci 示例仅包含两个 children,称为 a 和 b。现在的问题是我不确定我是否可以在一个函数matTask(最初称为'execute')中使用两个以上的children。所以我尝试添加更多的调用,更多的指针和更多的等待spawn_and_wait_for_all()...在这个阶段我没有创建任何图像处理功能,因为我想问你这个设计是否正确,是否会有应该不是性能问题。它还没有完成。我会等待您的建议来修复我的概念中可能存在的错误。
我的基本想法是在 lena.jpg 上使用一些滤镜功能,例如高斯模糊。首先,我会传递一些线程。我有 8 个内核,所以最多只能传递 8 个线程。我计划将 lena 图像分成 8 个相同大小的条带,然后将像素复制到矢量(8 个基本矢量),然后它们应该被模糊。然后另一个阶段是我需要创建接下来的 7-8 个图像,这些图像与 8 个部分的边距重叠。我只想重复模糊动作。最后,可能是图像其余部分的区域需要再通过一次(source_image.rows()/8 中的剩余部分)。
我需要解决的主要问题(我不知道该怎么做)是停止无限循环。我应该为 1) 应对和 2) 模糊 3) 裁剪 4) 粘贴创建不同的 class 和不同的方法吗?或者我可以在一个电话中传递所有内容(复制+模糊)吗?这是与 fabonnaci 数字示例的区别,因为该代码做了同样的事情,但我需要做更多不同的事情......那么逻辑应该是什么,如何排序,如何命名函数?
更简单的解决方案是使用 8 个相同大小的条带......然后 7-8 个重叠区域。
下面的代码没有打印错误,但它不应该return正确的结果,因为它只是时间概念。
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include "tbb/task.h"
#include "tbb/task_scheduler_init.h"
#define CutOff 12
using namespace cv;
void SerialAction(int n){};
/**
**/
class matTask: public tbb::task {
public:
int n;
const int offset;
std::vector<cv::Mat> main_layers;
std::vector<cv::Mat> overlay_layers;
matTask( std::vector<cv::Mat>main_layers_, std::vector<cv::Mat> overlay_layers_, int n_, const int offset_ ) :
main_layers(main_layers_),
overlay_layers(overlay_layers_),
n(n_), offset(offset_)
{}
task* execute() {
if( n<CutOff ) {
SerialAction(n);
}
else {
// Main layers - copy regions
matTask& a = *new( allocate_child() )
matTask(main_layers,overlay_layers,n,0);
matTask& b = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-1,0);
matTask& c = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-2,0);
matTask& d = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-3,0);
matTask& e = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-4,0);
matTask& f = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-5,0);
matTask& g = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-6,0);
matTask& h = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-7,0);
spawn_and_wait_for_all( a );
spawn_and_wait_for_all( b );
spawn_and_wait_for_all( c );
spawn_and_wait_for_all( d );
spawn_and_wait_for_all( e );
spawn_and_wait_for_all( f );
spawn_and_wait_for_all( g );
spawn_and_wait_for_all( h );
// In the case of effect:
// Overlay layers
matTask& ab = *new( allocate_child() )
matTask(main_layers,overlay_layers,n,offset);
matTask& bc = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-1,offset);
matTask& cd = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-2,offset);
matTask& de = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-2,offset);
matTask& ef = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-2,offset);
matTask& gh = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-2,offset);
// ... + crop .. depends on size of kernel
set_ref_count(8);
spawn( b );
spawn_and_wait_for_all( a );
}
return NULL;
}
};
void ParallelAction( std::vector<cv::Mat> main, std::vector<cv::Mat> overlays, int n, const int offset ) {
matTask& a = *new(tbb::task::allocate_root())
matTask(main, overlays, n,offset);
tbb::task::spawn_root_and_wait(a);
}
int main( int argc, char** argv )
{
int threads = 8;
std::vector<cv::Mat> main_layers;
std::vector<cv::Mat> overlays;
cv:: Mat sourceImg;
sourceImg = imread( "../../data/lena.jpg");
if ( sourceImg.empty() )
return -1;
const int offset = (int) sourceImg.rows / threads;
cv::setNumThreads(0);
ParallelAction(main_layers, overlays, threads, offset );
// GaussianBlur( src, dst, Size(3,3), 0, 0, BORDER_DEFAULT );
return 0;
}
编辑:
对 Anton 的回答的反应。如果我使用 operator() 重载,究竟什么时候应用 operator()?也可以向 ApplyFoo 添加一些方法吗? W()重载时,好像只能有一个方法
void Foo(float a){};
class ApplyFoo {
float *const my_a;
public:
void operator()( const tbb::blocked_range<size_t>& r ) const {
float *a = my_a;
for( size_t i=r.begin(); i!=r.end(); ++i )
Foo(a[i]);
}
ApplyFoo( float a[] ) :
my_a(a) // initiate my_a
{}
};
您所指的文章是 2007 年的!它非常过时(尽管仍然相关,因为 TBB 保持所有源兼容性)。 tbb::task
接口被认为是低级的,对于应用程序开发来说不是那么方便。请 refer to tbb::parallel_for
、tbb::parallel_invoke
,尤其是直接支持取消的 tbb::task_group
。
我正在学习使用 OpenCV 和 TBB。我需要学习如何使用图像的多处理,因为我有多核 CPU 并且想为我的程序创建 muticpu 支持。
我已阅读英特尔® 技术期刊论文中的一篇文章 "The Foundations for Scalable Multi-core Software in Intel® Threading Building Blocks"(您可以在此处的 pdf 中找到它 http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.71.8289&rep=rep1&type=pdf)
他们使用法波纳奇数计算作为多处理的例子。在 TBB 包中的 TBB 示例中也有类似的 fabonacci number 示例(请参阅 ParallelTaskFib)。唯一的问题是计算非常简单,CPU 负担不大,因此当您 运行 对小数字进行多任务处理时,低 CutOff 效率不高,因为它需要太多开销。因此,要学习使用 TBB,我需要更多来自图像处理的实际示例。在我的概念中,我想使用 TBB Task Scheduler。我从 class FibTask 和我重命名的函数 ParallelFib 开始,更改了参数以处理图像向量。它的设计基本原则应该保持不变。 fabonacci 示例仅包含两个 children,称为 a 和 b。现在的问题是我不确定我是否可以在一个函数matTask(最初称为'execute')中使用两个以上的children。所以我尝试添加更多的调用,更多的指针和更多的等待spawn_and_wait_for_all()...在这个阶段我没有创建任何图像处理功能,因为我想问你这个设计是否正确,是否会有应该不是性能问题。它还没有完成。我会等待您的建议来修复我的概念中可能存在的错误。
我的基本想法是在 lena.jpg 上使用一些滤镜功能,例如高斯模糊。首先,我会传递一些线程。我有 8 个内核,所以最多只能传递 8 个线程。我计划将 lena 图像分成 8 个相同大小的条带,然后将像素复制到矢量(8 个基本矢量),然后它们应该被模糊。然后另一个阶段是我需要创建接下来的 7-8 个图像,这些图像与 8 个部分的边距重叠。我只想重复模糊动作。最后,可能是图像其余部分的区域需要再通过一次(source_image.rows()/8 中的剩余部分)。
我需要解决的主要问题(我不知道该怎么做)是停止无限循环。我应该为 1) 应对和 2) 模糊 3) 裁剪 4) 粘贴创建不同的 class 和不同的方法吗?或者我可以在一个电话中传递所有内容(复制+模糊)吗?这是与 fabonnaci 数字示例的区别,因为该代码做了同样的事情,但我需要做更多不同的事情......那么逻辑应该是什么,如何排序,如何命名函数?
下面的代码没有打印错误,但它不应该return正确的结果,因为它只是时间概念。
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include "tbb/task.h"
#include "tbb/task_scheduler_init.h"
#define CutOff 12
using namespace cv;
void SerialAction(int n){};
/**
**/
class matTask: public tbb::task {
public:
int n;
const int offset;
std::vector<cv::Mat> main_layers;
std::vector<cv::Mat> overlay_layers;
matTask( std::vector<cv::Mat>main_layers_, std::vector<cv::Mat> overlay_layers_, int n_, const int offset_ ) :
main_layers(main_layers_),
overlay_layers(overlay_layers_),
n(n_), offset(offset_)
{}
task* execute() {
if( n<CutOff ) {
SerialAction(n);
}
else {
// Main layers - copy regions
matTask& a = *new( allocate_child() )
matTask(main_layers,overlay_layers,n,0);
matTask& b = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-1,0);
matTask& c = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-2,0);
matTask& d = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-3,0);
matTask& e = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-4,0);
matTask& f = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-5,0);
matTask& g = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-6,0);
matTask& h = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-7,0);
spawn_and_wait_for_all( a );
spawn_and_wait_for_all( b );
spawn_and_wait_for_all( c );
spawn_and_wait_for_all( d );
spawn_and_wait_for_all( e );
spawn_and_wait_for_all( f );
spawn_and_wait_for_all( g );
spawn_and_wait_for_all( h );
// In the case of effect:
// Overlay layers
matTask& ab = *new( allocate_child() )
matTask(main_layers,overlay_layers,n,offset);
matTask& bc = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-1,offset);
matTask& cd = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-2,offset);
matTask& de = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-2,offset);
matTask& ef = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-2,offset);
matTask& gh = *new( allocate_child() )
matTask(main_layers,overlay_layers,n-2,offset);
// ... + crop .. depends on size of kernel
set_ref_count(8);
spawn( b );
spawn_and_wait_for_all( a );
}
return NULL;
}
};
void ParallelAction( std::vector<cv::Mat> main, std::vector<cv::Mat> overlays, int n, const int offset ) {
matTask& a = *new(tbb::task::allocate_root())
matTask(main, overlays, n,offset);
tbb::task::spawn_root_and_wait(a);
}
int main( int argc, char** argv )
{
int threads = 8;
std::vector<cv::Mat> main_layers;
std::vector<cv::Mat> overlays;
cv:: Mat sourceImg;
sourceImg = imread( "../../data/lena.jpg");
if ( sourceImg.empty() )
return -1;
const int offset = (int) sourceImg.rows / threads;
cv::setNumThreads(0);
ParallelAction(main_layers, overlays, threads, offset );
// GaussianBlur( src, dst, Size(3,3), 0, 0, BORDER_DEFAULT );
return 0;
}
编辑: 对 Anton 的回答的反应。如果我使用 operator() 重载,究竟什么时候应用 operator()?也可以向 ApplyFoo 添加一些方法吗? W()重载时,好像只能有一个方法
void Foo(float a){};
class ApplyFoo {
float *const my_a;
public:
void operator()( const tbb::blocked_range<size_t>& r ) const {
float *a = my_a;
for( size_t i=r.begin(); i!=r.end(); ++i )
Foo(a[i]);
}
ApplyFoo( float a[] ) :
my_a(a) // initiate my_a
{}
};
您所指的文章是 2007 年的!它非常过时(尽管仍然相关,因为 TBB 保持所有源兼容性)。 tbb::task
接口被认为是低级的,对于应用程序开发来说不是那么方便。请 refer to tbb::parallel_for
、tbb::parallel_invoke
,尤其是直接支持取消的 tbb::task_group
。