天天看点

OpenCV算法加速(1)基础知识

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

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

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

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

二、openmp

https://www.openmp.org/ https://www.openmp.org/specifications/

下载

很多主流的编译环境都内置了OpenMP。VS 版本不低于2015,都支持 OpenMP。在VS中启用OpenMP很简单,在项目上右键->属性->配置属性->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并行计算入门案例

关于使用opencv的提速(二)(多线程问题,openMP)

OpenMP简介

三、TBB --- Threading Building Blocks

https://software.intel.com/en-us/tbb https://github.com/intel/tbb

按照目前网上的讨论,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://www.theimpossiblecode.com/blog/faster-opencv-smiles-tbb/

四、GPU

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

N卡支持的型号一览表:

https://developer.nvidia.com/cuda-gpus

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

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

cuda源码下载:

https://developer.nvidia.com/cuda-toolkit https://developer.download.nvidia.cn/compute/cuda/opensource/

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

2、CUDA vs OpenCL

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

https://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,如果它们能够持续发展下去)。

继续阅读