OpenCV算法加速(1)OpenMP/PPL/TTB基础知识

一、提高OpenCV的运算速度,有以下几种方法:

1、利用x86转为x64提速,可以提高1倍的速度

2、多线程的openmp或Intel TBB提速,将cpu的利用率从20%多提高到100%

3、利用GPU提速,至少可以提高5~10倍的运算速度

二、openmp

Home - OpenMPhttps://www.openmp.org/

Specifications - OpenMPhttps://www.openmp.org/specifications/很多主流的编译环境都内置了OpenMP。VS 版本不低于2015,都支持 OpenMP。在VS中启用OpenMPhttps://docs.microsoft.com/zh-cn/cpp/parallel/openmp/openmp-in-visual-cpp很简单,在项目上右键->属性->配置属性->C/C++->语言->OpenMP支持,选择是”即可。这实际上使用了编译选项/openmp。但是学习和使用openmp,应该考虑使用官方的高版本,最好不要使用Visual Studio自带的,因为VS2017只支持到OpenMP2.0版本。

openmp3.0中的task(任务,可动态配置)在多线程中是很重要的。
openmp4.0中的simd(向量化,单指令多数据),在密集性计算优化中很有用,比如挖矿类型的计算。target(异构计算相关系列指令),可以直接在openmp中使用gpgpu并行。
openmp4.5和5.0加入了很多对simd及gpu支持的深化内容。

举例:

#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
#pragma comment(lib,"opencv_core2410d.lib")
#pragma comment(lib,"opencv_highgui2410d.lib")
#pragma comment(lib,"opencv_imgproc2410d.lib")

void EdgeOpenMP(IplImage *src,IplImage *dst,int thresh)
{
    int height    = src->height;
    int width     = src->width;
    int step      = src->widthStep;
    uchar *data1  = (uchar *)src->imageData;
    uchar *data2  = (uchar *)dst->imageData;
    int i = step;
    #pragma omp parallel for
    for(i=step+1;i<height*width;i++)
    {
         if(abs(data1[i]-data1[i-1])>thresh || abs(data1[i]-data1[i-step])>thresh)
            data2[i]=255;/* 对于单通道,前后两帧差分大于门限
            或者对于多通道前后两帧的一个指标差分大于门限,则视为边缘*/
         else
            data2[i]=0;
    }
}

void Edge(IplImage *src,IplImage *dst,int thresh)
{
    int height    = src->height;
    int width     = src->width;
    int step      = src->widthStep;
    uchar *data1      = (uchar *)src->imageData;
    uchar *data2      = (uchar *)dst->imageData;
    int i = step;
    for(i=step+1;i<height*width;i++)
    {
         if(abs(data1[i]-data1[i-1])>thresh || abs(data1[i]-data1[i-step])>thresh)
            data2[i]=255;
         else
            data2[i]=0;
    }
}

int main()
{
  char filename[512];
  IplImage *src,*edge1,*edge2;
  puts("File name:");
  gets(filename);
  src = cvLoadImage(filename,CV_LOAD_IMAGE_GRAYSCALE );
  edge1=cvCloneImage(src);
  edge2=cvCloneImage(src);
 
  cvNamedWindow("src", CV_WINDOW_AUTOSIZE);
  cvMoveWindow("src", 100, 100);
  cvShowImage( "src", src);
  cvNamedWindow("Edge", CV_WINDOW_AUTOSIZE);
  cvMoveWindow("Edge", 200, 100);
  cvNamedWindow("EdgeOpenMP", CV_WINDOW_AUTOSIZE);
  cvMoveWindow("EdgeOpenMP", 300, 100);
  /* 以上都是准备一些窗口和图形基本数据 */
 
  int tekrar=100;//运行次数
  int thresh=30;
  double start, end,t1, t2;
   
  /* 计算没有使用OpenMP优化的时间 */
  start= (double)cvGetTickCount();//记下开始的时钟计数,以便计算函数或用户代码执行时间
  for(int i=0;i<tekrar;i++)
    Edge(src,edge1,thresh);
  end= (double)cvGetTickCount();//记下结束的时钟计数
  t1= (end-start)/((double)cvGetTickFrequency()*1000.);//计算运行时间,以毫秒为单位
  printf( "Run time without OpenMP = %g ms\n", t1 );
 
  /* 计算使用了OpenMP优化的时间 */
  start= (double)cvGetTickCount();
  for(int i=0;i<tekrar;i++)
    EdgeOpenMP(src,edge2,thresh);
  end= (double)cvGetTickCount();
  t2= (end-start)/((double)cvGetTickFrequency()*1000.);
  printf( "Run time with OpenMP = %g ms\n", t2 );
 
  printf( "Performance ratio (%%) = %% %.1f \n", 100*(t1/t2-1) );
 
  cvShowImage( "Edge", edge1);
  cvShowImage( "EdgeOpenMP", edge2);
  cvWaitKey();
  cvDestroyWindow("Edge");
  cvDestroyWindow("EdgeOpenMP");
  cvReleaseImage(&src);
  cvReleaseImage(&edge1);
  cvReleaseImage(&edge2);
}

参考文献:

《多核异构并行计算 OpenMP 4.5 C/C++篇》语法介绍比较详细,雷洪编著的。

OpenMP并行计算入门案例https://blog.csdn.net/tlqtangok/article/details/95875455
关于使用opencv的提速(二)(多线程问题,openMP)https://blog.csdn.net/wangzhebupt/article/details/22743515

OpenMP简介https://blog.csdn.net/longji/article/details/95076143

三、PPL

微软自家的并行库,ppl只能在windows上用不能跨平台,tbb能跨平台,但是受限于原始设计,tbb的task比较弱没有ppl的强大。

concurrency::parallel_for(0, num, [&](int i) { //索引[0, num)

});

其中,索引[0, num)的意思是

for (int i = 0; i < num; i++)

注意,0和num的类型必须是int型。因为int i。

并发运行时 | Microsoft Docs详细了解:并发运行时https://docs.microsoft.com/zh-cn/cpp/parallel/concrt/concurrency-runtime

#include <ppl.h> //concurrency::parallel_for

concurrency::parallel_for(0, szSize.height, [&](int i) { //y方向,索引[0,szSize.height)
        short *pSdx = (short *)(Sdx.data + Sdx.step * (i));
        short *pSdy = (short *)(Sdy.data + Sdy.step * (i));
        double dSx = 0, dSy = 0;
        double gradMag = 0;

        for (int j = 0; j < szSize.width; j++) //x方向
        {
            dSx = pSdx[j]; //X derivative of Source image
            dSy = pSdy[j]; //Y derivative of Source image

            gradMag = sqrt((dSx * dSx) + (dSy * dSy)); //Magnitude = Sqrt(dx^2 +dy^2)

            if (gradMag != 0) //hande divide by zero
            {
                matGradMag[i][j] = 1 / gradMag; //1/Sqrt(dx^2 +dy^2)
            }
            else
            {
                matGradMag[i][j] = 0;
            }
        }
    });

四、TBB --- Threading Building Blocks

Advanced HPC Threading: Intel® oneAPI Threading Building Blockshttps://www.intel.com/content/www/us/en/developer/tools/oneapi/onetbb.html

https://github.com/oneapi-src/oneTBBhttps://github.com/oneapi-src/oneTBB

按照目前网上的讨论,TBB风头要盖过openMP,比如openCV过去是使用openMP的,但从2.3版本开始抛弃openMP,转向TBB。Opencv源码需要自己编译,cmake选项勾上TBB。Add option WITH_TBB=ON when building opencv。

举例:

// A very raw example of using tbb's thread
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/video/video.hpp>
#include <opencv2/highgui.hpp>
#include <tbb/tbb.h>
#include <iostream>

using namespace std;
using namespace cv;
using namespace tbb;

int main(int argc, char* argv[])
{
    int TBB_THREADS = 3;
    task_scheduler_init init(TBB_THREADS);

    Mat im1 = imread(argv[1]);
    Mat imGray;
    if (im1.data == nullptr)
    {
        cout << "Error while reading file " << argv[1];
        return 1;
    }

    imshow("Input image", im1);
    cvtColor(im1, imGray, CV_RGB2GRAY);
    Mat im3, im4;
    tbb_thread th1([&imGray, &im3]() // in fact, you can do this with C++ thread
    {
        int windowSize = 5; // starting threshold value
        int constant = 5; // starting constant value

        adaptiveThreshold(imGray, im3, 255,
            CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY,
            windowSize, constant);
    });

    tbb_thread th2([&imGray, &im4]()
    {
        cv::GaussianBlur(imGray, im4, cv::Size(3, 3), 5.0f);
    });

    th1.join();
    th2.join();

    imshow("Grayscale image", imGray);
    imshow("Adaptive thresholding", im3);
    imshow("Gaussian blur", im4);
    cvWaitKey(0);
    return 0;
}

参考文献:

OpenCV加速与优化,让代码执行速度飞起来https://cloud.tencent.com/developer/article/1536447

https://www.theimpossiblecode.com/blog/faster-opencv-smiles-tbb/https://www.theimpossiblecode.com/blog/faster-opencv-smiles-tbb/

五、GPU

1、主流的显卡是NVIDIA和ATI两种,简称N卡和A卡;而opencv的gpu单指N卡显卡模块,而且只支持一部分。

N卡支持的型号一览表:

CUDA GPUs | NVIDIA Developerhttps://developer.nvidia.com/cuda-gpus

Windows开始菜单--运行--输入dxdiag--显示,可以看到自己电脑的显卡型号。

笔者的显卡需要查阅网页的字段:CUDA-Enabled GeForce and TITAN Products,型号是GeForce GT 630,支持!

cuda源码下载:

CUDA Toolkit | NVIDIA Developerhttps://developer.nvidia.com/cuda-toolkit

Index of /compute/cuda/opensourcehttps://developer.download.nvidia.cn/compute/cuda/opensource/

如果你是ATI显卡,或者用的不是电脑,又想GPU加速怎么办呢?那就让OpenCV和OpenCL结合起来,有跨平台的作用,可以应用到ATI显卡上。

2、CUDA vs OpenCL

OpenCL: Open Computing Language,开放计算语言。

OpenCL Overview - The Khronos Group Inchttps://www.khronos.org/opencl/
OpenCL和CUDA是两种异构计算(此异构平台可由CPU,GPU或其他类型的处理器组成。)的编程模型。

CUDA只支持NVIDIA自家的GPU。OpenCL最早是由Apple提出,后来交给了Khronos这个开放标准组织。

CUDA C语言与OpenCL的定位不同,或者说是使用人群不同。CUDA C是一种高级语言,那些对硬件了解不多的非专业人士也能轻松上手;而OpenCL则是针对硬件的应用程序开发接口,它能给程序员更多对硬件的控制权,相应的上手及开发会比较难一些。

从很多方面来看,CUDA和OpenCL的关系都和DirectX与OpenGL的关系很相像。如同DirectX和OpenGL一样,CUDA和OpenCL中,前者是配备完整工具包、针对单一供应商(NVIDIA)的成熟的开发平台,后者是一个开放的标准。
虽然两者抱着相同的目标:通用并行计算。但是CUDA仅仅能够在NVIDIA的GPU硬件上运行,而OpenCL的目标是面向任何一种Massively Parallel Processor,期望能够对不同种类的硬件给出一个相同的编程模型。由于这一根本区别,二者在很多方面都存在不同:
1)开发者友好程度。CUDA在这方面显然受更多开发者青睐。原因在于其统一的开发套件(CUDA Toolkit, NVIDIA GPU Computing SDK以及NSight等等)、非常丰富的库(cuFFT, cuBLAS, cuSPARSE, cuRAND, NPP, Thrust)以及NVCC(NVIDIA的CUDA编译器)所具备的PTX(一种SSA中间表示,为不同的NVIDIA GPU设备提供一套统一的静态ISA)代码生成、离线编译等更成熟的编译器特性。相比之下,使用OpenCL进行开发,只有AMD对OpenCL的驱动相对成熟。
2)跨平台性和通用性。这一点上OpenCL占有很大优势(这也是很多National Laboratory使用OpenCL进行科学计算的最主要原因)。OpenCL支持包括ATI,NVIDIA,Intel,ARM在内的多类处理器,并能支持运行在CPU的并行代码,同时还独有Task-Parallel Execution Mode,能够更好的支持Heterogeneous Computing。这一点是仅仅支持数据级并行并仅能在NVIDIA众核处理器上运行的CUDA无法做到的。
3)市场占有率。作为一个开放标准,缺少背后公司的推动,OpenCL显然没有占据通用并行计算的主流市场。NVIDIA则凭借CUDA在科学计算、生物、金融等领域的推广牢牢把握着主流市场。再次想到OpenGL和DirectX的对比,不难发现公司推广的高效和非盈利机构/标准委员会的低效(抑或谨慎,想想C++0x)。
我接触的很多开发者(包括我本人)都认为,由于目前独立显卡市场的萎缩、新一代处理器架构(AMD的Graphics Core Next (GCN)、Intel的Sandy Bridge以及Ivy Bridge)以及新的SIMD编程模型(Intel的ISPC等)的出现,未来的通用并行计算市场会有很多不确定因素,CUDA和OpenCL都不是终点,我期待未来会有更好的并行编程模型的出现(当然也包括CUDA和OpenCL,如果它们能够持续发展下去)。

参考文献:

opencv如何利用GPU加速

CUDA和OpenCL有什么区别?

CUDA 与 OpenCL 区别

六、引申阅读之TaskCpp

https://github.com/qicosmos/TaskCppV1.1

TaskCpp是c++11开发的一个跨平台的并行task库,它的设计思路来源于微软的并行计算库ppl和intel的并行计算库tbb

(原创)发布一个c++11开发的轻量级的并行Task库TaskCpp - qicosmos(江南) - 博客园TaskCpp简介 TaskCpp是c++11开发的一个跨平台的并行task库,它的设计思路来源于微软的并行计算库ppl和intel的并行计算库tbb,关于ppl和tbb我在前面有介绍。既然已经有了这https://www.cnblogs.com/qicosmos/p/3534967.html

---

欢迎访问姊妹篇《关于实现Halcon算法加速的基础知识

  • 14
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值