【发布时间】:2016-06-30 16:10:18
【问题描述】:
我正在学习使用 OpenCV 和 TBB。我需要学习如何使用图像的多处理,因为我有多核 CPU,并且想为我的程序创建 muticpu 支持。
我已阅读英特尔® 技术期刊论文中的一篇文章“英特尔® 线程构建模块中可扩展多核软件的基础”(您可以在此处http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.71.8289&rep=rep1&type=pdf 的 pdf 中找到它)
他们使用 fabonacci 数计算作为多处理的示例。 TBB 包中的 TBB 示例中也有类似的 fabonacci 数字示例(请参阅 ParallelTaskFib)。唯一的问题是计算非常简单,对 CPU 的负担并不大,所以当你在小数字上运行多任务时,低 CutOff 它效率不高,因为它需要太多开销。因此,要学习使用 TBB,我需要更多来自图像处理的实际示例。在我的概念中,我想使用 TBB 任务计划程序。我从一个类 FibTask 和我重命名的函数 ParallelFib 开始,更改了参数以处理图像向量。它的设计基本原则应该保持不变。 fabonacci 示例仅包括两个孩子,分别称为 a 和 b。现在的问题是我不确定我是否可以在一个函数 matTask (最初称为“执行”)中使用两个以上的孩子。所以我尝试添加更多的调用,更多的指针和更多的等待 spawn_and_wait_for_all()... 在这个阶段我没有创建任何图像处理函数,因为我想问你这个设计是否正确,是否会出现性能问题.它还没有完成。我会等待你的建议来修正我的概念中可能出现的错误。
我的基本想法是在 lena.jpg 上使用一些过滤器功能,例如高斯模糊。首先我会传递一些线程。我有 8 个内核,所以最多只能通过 8 个线程。我打算将 lena 图像分成 8 个相同大小的条带,然后将像素复制到向量(8 个基本向量),然后它们应该被模糊。然后另一个阶段是我需要创建下一个 7-8 个图像,这些图像与 8 个部分的边缘重叠。我只想重复模糊动作。最后,对于可能是图像其余部分的区域(来自 source_image.rows()/8 的剩余部分),还需要再通过一次。
我需要解决的主要问题(我不知道该怎么做)是停止无限循环。我应该为 1)应对和 2)模糊 3)裁剪 4)粘贴创建不同的类和不同的方法吗?或者我可以一次调用所有内容(复制+模糊)吗?这是与 fabonnaci 数字示例的区别,因为该代码做了同样的事情,但我需要做更多不同的事情......那么逻辑应该是什么,如何对事物进行排序,如何命名函数?
更简单的解决方案是使用 8 个相同大小的条带......然后覆盖 7-8 个区域。
下面的代码没有打印错误,但它不应该返回正确的结果,因为它只是时间概念。
#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
{}
};
【问题讨论】:
-
您可以继承 OpenCV 的
ParallelLoopBody并使用cv:parallel_for_,如果可用,它将使用 tbb。可以看一个例子here进行灰度转换,大概可以适应你的需要 -
谢谢,我会尝试更改示例。现在我正在尝试用 TBB 来做这件事。
标签: c++ image opencv multiprocessing tbb