【问题标题】:How to create TBB Task Scheduler for OpenCV multicore image processing? C++如何为 OpenCV 多核图像处理创建 TBB 任务调度程序? C++
【发布时间】: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 数字示例(请参阅 ParallelTask​​Fib)。唯一的问题是计算非常简单,对 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


【解决方案1】:

您指向的文章是 2007 年的!它已经非常过时了(尽管由于 TBB 保持所有源兼容性,所以仍然相关)。 tbb::task 接口被认为是低级的,对于应用程序的开发不太方便。请refer totbb::parallel_fortbb::parallel_invoke,尤其是直接支持取消的tbb::task_group

【讨论】:

  • 我更新了一个带有问题的代码。如果我在何时调用 operator() 重载?是否可以在 ApplyFoo 类中使用一些附加功能?是否可以将 Foo 函数移动到 ApplyFoo 类,否则会因为运算符 () 而导致冲突?我可以将 Foo 作为一种方法移动到某个不同的类下吗?在这种情况下,我如何传递对 Foo 所在对象的引用?在哪里放置指针?我认为唯一的解决方案是将它放入类 ApplyFoo 中,放入构造函数中,这样我就可以从 operator () 函数中访问它。
  • @JohnBoe 您可以使用operator() 定义任何类,也可以使用[](const tbb::blocked_range&lt;size_t&gt;&amp; r){ do_it_inline(); } 等lambda 表达式。它在parallel_for 完成之前或parallel_invoketask_group.wait() 完成之前的某处被调用。
  • 那么当我不想将函数保留在一个类之外时,是否有必要创建两个类?我创建了一个类 BlurAction_wrapper,它应该创建所有操作 BlurAction_wrapper * BlurAction = new BlurAction_wrapper(sourceImg, &amp;targetImg, iArgs);,例如 Copy、Blur、Crop、Merge。我应该将公共部分从 ApplyFoo 移动到 BlurAction 类(重命名)还是保持分离?
  • @JohnBoe,抱歉,如果您需要一般的 C++ 建议,请在单独的问题中提出
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-28
  • 2015-12-29
  • 1970-01-01
  • 1970-01-01
  • 2019-03-19
  • 2019-01-21
相关资源
最近更新 更多